##バイナリデータフォマットを実装する
組込み分野はセンサーとPCがやりとりするプロトコルはもちろん、PCとサーバーとの間でやりとりするデータもバイナリフォーマットである事が多いと思うのでドキュメント化されたフォーマットに従って数値からバイナリフォーマットされたデータを作ってみます。今回は内容的に普通のRubyとなんら変わらないですが、多分こういうこと(バイナリフォーマットとかの実装)をやるのは 組込み=mruby では多いだろうという勝手な想像で...
###MQTTプロトコル
ざっくりとしたMQTTの概念については以下を参照。簡単に言うと、機器同士が通信する為のPubSubプロトコルです。3G回線など場合によっては繋がりにくいような回線を想定したプロトコル設計になっています。
HTTPからMQTTへ - IBMが提唱するモノとモノがつながる時代に最適化したプロトコル&アプライアンス
###MQTTプロトコルフォーマット
MQTT v3.1プロトコル仕様にMQTTの固定ヘッダのフォーマットが記載されています。MQTT全部やるのはさすがにmrubyの勉強の範囲を超えるので、この固定ヘッダを実装してみます。
上記プロトコル仕様の日本語版PDFが以下リンク先に有ります。
http://public.dhe.ibm.com/software/dw/jp/websphere/wmq/mqtt31_spec/mqtt-v3r1_ja.pdf
固定ヘッダのフォーマットは以下のようになっています。
あまりプロトコルの詳細に深入りすると延々と続いてしまいますのでここでは固定長ヘッダに話を限定し、以下のようなデータを送信したいとします。
####1バイトめ
1バイトめは各種フラグ等で、1ビット、2ビット、4ビットの設定値からなる8ビットのデータです。
データ名称 | 格納する値 | 長さ(ビット数) |
---|---|---|
メッセージタイプ | 1 | 4 |
DUPフラグ | 0 | 1 |
QoSレベル | 1 | 2 |
RETAIN | 1 | 1 |
4+1+2+1 で 8ビット=1バイト となります。
####2バイトめ
これから送信するデータの固定長ヘッダを除く部分のサイズを格納します。
データ名称 | 格納する値 | 長さ(ビット数) |
---|---|---|
残りのデータの長さ(バイト数) | 20 | 8 |
ここでは残りのデータの長さはわからないので適当に20バイトとしました。
####固定長ヘッダをmrubyで作る
じゃあmrubyで作ってみます。
data_1 = 0b00010011 # 0b 0001(メッセージタイプ) 00(DUP) 1(QoS) 1(RETAIN)
data_2 = 20 # 残り部分の長さは20バイトとする
binary = data_1.chr + data_2.chr #=> "\023\024"
chr
メソッドを使うと数値からバイナリを生成できるので、chr
で生成したバイナリ(Stringクラスのインスタンス)を+
で連結しました。
最終的に出来上がった"\023\024"
が送信するデータになりますので、これをソケットにwrite
すればサーバー(MQTT的に言うとブローカー)に送信されます。
####メソッド化してみる
実際にはこんな固定値のコードではなくてメッセージタイプやDUPフラグ等を引数で受け取ってその都度生成するはずなので以下のようになると思われます。
一応各値にはデフォルト値を設定します。
def fixed_header(message_type = 1, dup = 0, qos = 0,
retain = 0, remaining_size = 0)
data_1 = (message_type << 4) | (dup << 3) | (qos << 1) | retain
data_2 = remaining_size
data_1.chr + data_2.chr
end
fixed_header(1, 0, 1, 1, 20) #=> "\023\024"
あ、というかmrubyでラベル付きの引数つかえるんだっけ
def fixed_header(params)
message_type = params[:message_type] || 1
dup = params[:dup] || 0
qos = params[:qos] || 0
retain = params[:retain] || 0
remaining_size = params[:remaining_size] || 0
data_1 = (message_type << 4) | (dup << 3) | (qos << 1) | retain
data_2 = remaining_size
data_1.chr + data_2.chr
end
fixed_header(message_type:1, dup:0, qos:1, retain:1, remaining_size:20) #=> "\023\024"
おー。こっちのほうが解り易いですね。引数で受け取った値のチェックとかは必要ですけど大体こんな感じになるかなと。
とりあえず固定ヘッダだけですが<< >> (ビットシフト)
と| (or)
、そしてchr
と+
を使ってバイナリフォーマットの実装できました。