📝

SORACOMバイナリーパーサーのCBOR向けフォーマットを試してみた

2023/12/04に公開

これは

SORACOM Advent Calendar2023の5日目の記事です。

バイナリーパーサーがCBORに対応

SORACOMのサービス更新情報をつらつらと眺めていて、2023/5/12のアップデートで「バイナリパーサーが Concise Binary Object Representation (CBOR) に対応しました」というものがありました。

https://changelog.soracom.io/ja/bainaripasaga-concise-binary-object-representation-cbor-nidui-ying-simasita-2AbOLK

私は普段バイナリーパーサーといえばボタン用の@buttonやGPSマルチユニット用の@gpsmultiunitくらいしか使ったことがなかったのですが、公式ドキュメントを見てみるとそれ以外にも定義済みのフォーマットが結構沢山あることに今更ながら気がついたので、この機会に試してみました。

バイナリーパーサーとは

バイナリパーサーは、デバイスが送信したデータを変換し、JSON形式のデータとしてBeam/Funnel/Funk/Harvest Dataに出力する機能です。
Unified Endpointとソラコムの各サービスとの間に挟まり、デバイスから送信されたバイナリデータを、設定されたフォーマットに従ってJSON形式に変換してくれるサービスです。
これを使うと、デバイスから送るときにはできるだけ小さなデータになるようにバイナリにパックして送り、クラウドに送るときにはクラウド側で扱いやすいようにJSONにするということができます。これによってデバイスからの通信量が削減でき、ひいては通信料や消費電力量を削減することが可能になります。

詳しくは公式ドキュメントを参照ください。

https://users.soracom.io/ja-jp/docs/binary-parser/

Concise Binary Object Representation(CBOR)とは

Concise Binary Object Representation(CBOR)とは、RFC8949で定義されたバイナリーデータのフォーマットです。JSONデータモデルに基づいており、手軽かつデータを節約しつつ、JSONのように柔軟な形式のデータをバイナリの列で表現できます。
詳しくは公式ページをご確認ください。

https://cbor.io/

データ形式やデータ長が固定の物であれば単純なバイナリパックにする方がデータ量は小さくなりますが、JSON由来の柔軟さも備えているところが特徴だと思います。

CBORで送る準備

では、早速CBORでデータを送ってみましょう。バイナリーパーサの定義済みフォーマットの説明にある例も参考にしつつ、適当なJSONをudpで送るプログラムを作成します。
今回はSORACOM ArcでPCをSORACOMプラットフォームに繋ぎ、プログラムはpythonで記載しました。pythonでcborを取り扱うには、cbor2というライブラリを利用します。

from cbor2 import dumps, loads, load
import json
import socket

serv_address = ("uni.soracom.io",23080)
json_text = '{"integer":1,"string":"str","array":["array1","array2"]}'
json = json.loads(json_text)

data = dumps(json)

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
send_len = sock.sendto(data,serv_address)
print(send_len)

SORACOM BeamのUDPToHTTP機能を使い、AWS Lambdaにデータを送信します。Lambdaは単純に受け取ったデータをそのままログにダンプするだけの以下のようなpythonプログラムとしました。

import json

def lambda_handler(event, context):
    body = {
        "event": event
    }
    print(json.dumps(body))
    return {
        'statusCode': 200,
        'headers': {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "Content-Type"
        },
        'body': json.dumps('Hello from Lambda!')
    }    

SORACOM側の準備は以下の通りです。

  • 新しいSIMグループの作成
  • 作成したSIMグループに、ArcのvSIMを所属させる
  • 作成したSIMグループの設定で、BeamのUDPToHTTPを設定する

そのまま送ってみる

では、まずはバイナリーパーサを設定せずに送ってみます。Lambda側のログを見ると、以下のようになっています(ヘッダ情報等は省略しています)。

"body": "{\"payload\":\"o2dpbnRlZ2VyAWZzdHJpbmdjc3RyZWFycmF5gmZhcnJheTFmYXJyYXky\"}"

バイナリーデータはそのままでは送れないので、Base64で送られています。Base64デコードツールでこの文字列を復元してみましょう。

要素の前にデータタイプを表す文字が入っていて、CBORのバイナリっぽいですね。

バイナリーパーサーに@cborを設定

では、次にバイナリーパーサーに@cborを設定して送ってみます。Lambdaのログは以下の通りです。

"body": "{\"integer\":1,\"string\":\"str\",\"array\":[\"array1\",\"array2\"],\"binaryParserEnabled\":true}"

ちゃんと元のJSONになっていることが分かります。

JSONで送ってみる

定義済みデータを見ると、@jsonがあることに気がつきました。せっかくですのでちょっとこれも試してみましょう。
まずはプログラムを以下のように修正します。

import json
import socket

serv_address = ("uni.soracom.io",23080)
json_text = '{"integer":1,"string":"str","array":["array1","array2"]}'

sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
send_len = sock.sendto(json_text.encode('utf-8'),serv_address)
print(send_len)

そしてバイナリーパーサーを一旦無効にしてからデータを送ってみます。Lambdaのログは以下の通りです。

"body": "{\"payload\":\"eyJpbnRlZ2VyIjoxLCJzdHJpbmciOiJzdHIiLCJhcnJheSI6WyJhcnJheTEiLCJhcnJheTIiXX0=\"}"

バイナリーで送られているので、Base64で送られています。デコードしてみましょう。

元のJSONになりました。

では、バイナリーパーサーに@jsonを設定して送ってみます。Lambdaのログは以下の通りです。

"body": "{\"integer\":1,\"string\":\"str\",\"array\":[\"array1\",\"array2\"],\"binaryParserEnabled\":true}"

バッチリですね!

まとめ

SORACOMバイナリーパーサーを使うと、デバイスから送信するデータ量を削減することができます。CBOR形式はそれほどデータ量自体は減らないものの、単純なバイナリパックに比べると柔軟性があるのでなかなか使いやすそうです。
また、JSONを送る際にもHTTPで送らずにUDPで送ってバイナリーパーサーを使うことでデバイス側の処理量や通信量を削減できます。

独自にバイナリーパックして送信するとデータ量は小さくできますが、それに対応するバイナリーパーサーを書くのはそこそこ面倒です。定義済みフォーマットとCBOR等を使うことでその辺りの苦労を軽減できそうです。

GitHubで編集を提案

Discussion