LoginSignup
14
9

More than 5 years have passed since last update.

ツナでもわかるmruby [4回目:バイナリデータフォーマットの実装]

Posted at

バイナリデータフォマットを実装する

組込み分野はセンサーと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

固定ヘッダのフォーマットは以下のようになっています。

スクリーンショット 2014-05-04 17.24.08.png

あまりプロトコルの詳細に深入りすると延々と続いてしまいますのでここでは固定長ヘッダに話を限定し、以下のようなデータを送信したいとします。

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で作ってみます。

mirb
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フラグ等を引数で受け取ってその都度生成するはずなので以下のようになると思われます。

一応各値にはデフォルト値を設定します。

mirb
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でラベル付きの引数つかえるんだっけ

mirb
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+を使ってバイナリフォーマットの実装できました。

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9