この前はシリアルポートから受信したデータをChromeAppsの画面に表示しました。
それだけではあまり使い道がないので、その受信したデータをSinatraへ飛ばしてみようとChromeAPIを探してみたところ、chrome.sockets.tcp
やchrome.sockets.udp
APIがありました。
ただ、それぞれのsend()
メソッドを調べてみたところ、送信するデータ型がArrayBuffer
とSinatraでは扱いが面倒そうだったので、今回はChromeAPIを使うのは見送ることにしました。
他の手段を試してみたところ、ChromeAppsでWebSocketが使えることが分かりました。そこで、気になっていたWebSocketの勉強を兼ねて、ChromeAppsからSinatraへWebSocketを使ってデータを送信してみました。
環境
- Windows7 x64
- Google Chrome バージョン 35.0.1916.114 m
- Ruby 2.0.0p481 (RubyInstaller)
- gem
全体の構成
今回はWebSocket送信の成否は判定していないため、それぞれ片方向通信となっています。
ChromeApp ---(WebSocket)---> Sinatra (port 4567) ---(WebSocket)---> SinatraのView (index.erb)
ChromeApps側
ChromeAppsでWebSocketが使えるかどうかが問題でしたが、特に問題なく動作しました。
そのため、前回のコードを少し変更するだけで済みました。
シリアルポートからの送信は一回行うだけなので、「ボタンを押したら接続」「データ送信」「送信後に接続を閉じる」という簡単な実装になりました。
const WEBSOCKET_HOST = "ws://192.168.0.1:4567"; var clickedWebSocketListener = function() { var ws = new WebSocket(WEBSOCKET_HOST); ws.onopen = function() { console.log('opened'); ws.send(arrayReceived.join('')); // 送ったら閉じとく ws.close(); // 変数の初期化とlistのクリア stringReceived = ''; arrayReceived = []; var upload = document.getElementById('upload'); while (upload.firstChild) { upload.removeChild(upload.firstChild); } }; ws.onclose = function() { console.log('closed'); }; }; document.getElementById('connectWebSocket').addEventListener("click", clickedWebSocketListener, false);
Sinatra側
SinatraでWebSocketを扱うようなものがないかを探してみたところ、stackoverflowに以下のような回答がありました。
ruby - Any success with Sinatra working together with EventMachine WebSockets? - Stack Overflow
今回は、sinatra-websocket
gemを使うことにしました。
simulacre/sinatra-websocket - GitHub
Sinatra部分は、ほぼ sinatra-websocket のサンプルと同じです。
bundle exec ruby app.rb -e production
にて起動します。
get '/' do logger.info('accessed') unless request.websocket? erb :index else logger.info('received') request.websocket do |ws| ws.onopen do logger.info('opened') settings.sockets << ws end ws.onmessage do |msg| logger.info(msg) EM.next_tick { settings.sockets.each{|s| s.send(msg) } } end ws.onclose do logger.info('closed') end end end end
なお、SinatraのViewに記載したJavaScriptはChromeApps側とほぼ同じになっているため、記述は省略します。
画面イメージとコンソール出力
ChromeApps - シリアルポートからのデータ受信前
ChromeApps - シリアルポートからの受信後
ChromeApps - シリアルポート受信~WebSocket送信までのコンソールログ
Sinatra - WebSocket受信前
Sinatra - WebSocket受信後
ソースコード
GitHubに上げておきました。
ChromeApps-sample/serial_websocket at master · thinkAmi/ChromeApps-sample
参考
WebSocket
上記のstackoverflowの回答以外のgemなどを探してみたところ、faye-websocket
gemがありました。こちらの方がStar数が多かったので、メジャーなのかもしれません。
faye-websocket-ruby
試している日本語のBlogなどは以下がありました。
Sinatra + Faye::WebSocketでlambda do |env|したくない - 技術ネタ書ける人になりたい
WebSocketに関するその他の参考記事
- ruby - padrino && websockets - Stack Overflow
- Getting Started with Ruby and WebSockets
- WebSocket - Web API インターフェイス | MDN