データのシリアル化と逆シリアル化の例



データのシリアル化と逆シリアル化の例

このトピックでは、Alexa Gadgets Toolkitインターフェースのディレクティブとイベントをシリアル化(エンコード)および逆シリアル化(デコード)するサンプルコードについて詳しく説明します。ここではC言語の実装を取り上げていますが、PythonやJavaなどのほかの言語にも、この概念を応用できます。

ステップ1: リソースの取得

最初の手順は、以下のリソースを取得することです。

  1. nanopb .protoコンパイラをダウンロードする – C言語でプロトコルバッファーを使用するには、nanopbの使用をお勧めします。nanopbは、ANSI C準拠で実装された小規模コードのプロトコルバッファーです。特にマイクロコントローラーでの使用に適していますが、あらゆるメモリが制限されたシステムに対応します。nanopbをインストールするには、以下の手順を実行します。
    1. Nanopb Downloadsでnnanopb 3.xをダウンロードしてください。このサンプルコードはnanopb 4.xでは動作しません。
    2. nanopbダウンロードのコンテンツを展開します。展開されたnanopbディレクトリには、.protoファイルをコンパイルする実行可能ファイルが含まれます(つまり、*.cファイルおよび*.hファイルを取得します)。また、protobufエンコードされたデータをエンコードおよびデコードする関数とデータ構造を含むヘルパーファイルも含まれます。
  2. Alexa Gadgetsサンプルコードをダウンロードする – GitHubのAlexa Gadgetsサンプルコードリポジトリを取得します。このリポジトリには、Alexa Gadget .protoファイルとサンプルコードが含まれます。

ステップ2: ユーティリティファイルのコピー

前の手順でダウンロードしたAlexa Gadgetsリポジトリの/examplesディレクトリに、nanopbディレクトリから以下のファイルをコピーします。これらのファイルは、protobufエンコードされたメッセージのエンコードとデコードに必要な手法とデータ構造を定義するユーティリティファイルです。

  • pb.h
  • pb_common.c
  • pb_common.h
  • pb_decode.c
  • pb_decode.h
  • pb_encode.c
  • pb_encode.h

ステップ3: .protoファイルのコンパイル

以下のように、Alexa Gadgets .protoファイルを、ガジェットコードから参照できるクラスにコンパイルする必要があります。

  1. ダウンロードしたAlexa Gadgetsサンプルコードリポジトリの/examplesディレクトリに移動します。
  2. compile_nanos.sh(MacまたはLinuxの場合)またはcompile_nanos.bat(Windowsの場合)を開き、指定したPROTO_COMPILE_PATHにnanopb .protoコンパイラがあることを確認します。
  3. /examplesディレクトリから、compile_nanos.sh(MacまたはLinuxの場合)またはcompile_nanos.bat(Windowsの場合)を実行します。この操作で、Alexa Gadgetsサンプルコードリポジトリの.protoファイルがすべてコンパイルされ、生成されたCファイルとヘッダーソースファイルが/examplesディレクトリに入ります。

ステップ4: サンプルのコンパイル

/examplesディレクトリから、以下のコマンドを使用してproto_sample.cをコンパイルします。

クリップボードにコピーされました。

gcc -I. -DPB_FIELD_16BIT notificationsSetIndicatorDirectivePayload.pb.c notificationsSetIndicatorDirective.pb.c alexaDiscoveryDiscoverResponseEventPayload.pb.c alexaDiscoveryDiscoverResponseEvent.pb.c alexaDiscoveryDiscoverDirective.pb.c alexaDiscoveryDiscoverDirectivePayload.pb.c alexaGadgetStateListenerStateUpdateDirective.pb.c alexaGadgetStateListenerStateUpdateDirectivePayload.pb.c alexaGadgetSpeechDataSpeechmarksDirective.pb.c alexaGadgetSpeechDataSpeechmarksDirectivePayload.pb.c pb_common.c pb_decode.c pb_encode.c directiveHeader.pb.c eventHeader.pb.c directiveParser.pb.c eventParser.pb.c proto_sample.c -o proto_sample

ステップ5: サンプルの実行

Alexa Gadgetsディレクティブとイベントをバイナリーからエンコードおよびデコードし、結果を印刷する./proto_sampleを実行します。ディレクティブをエンコードし、イベントをデコードする関数は、例を示すためだけに表示しています。現実のアプリケーションでは、ガジェットはディレクティブをデコードし、イベントをエンコードします(その逆ではありません)。

以下の表に、サンプルコードに含まれる関数をまとめます。

関数 説明
decode_directive() Alexa Gadgetsディレクティブをすべてデコードします。関数がインターフェースの名前空間とディレクティブ名を認識できない場合、エラーがプリントされます。この関数には自作のディレクティブの解析コードを追加できます。
decode_event() Alexa Gadgetsイベントをすべてデコードします。関数がインターフェースの名前空間とイベント名を認識できない場合、エラーがプリントされます。この関数には自作のイベントの解析コードを追加できます。
encode_set_indicator_directive()

以下のNotifications.SetIndicatorディレクティブをバイナリー形式にします。

 {
   "directive": {
      "header": {
         "namespace": "Notifications",
         "name": "SetIndicator",
         "messageId": "messageID1"
      },
      "payload": {
         "persistVisualIndicator": true,
         "playAudioIndicator": true,
         "asset": {
            "assetId": "assetID1",
            "url": "url1"
         }
      }
   }
}
encode_sample_discover_response_event()

以下のAlexa.Discovery.Discover.Responseイベントをバイナリー形式にします。

{
   "event": {
      "header": {
         "namespace": "Alexa.Discovery",
         "name": "Discover.Response",
         "messageId": ""
      },
      "payload": {
         "endpoints": [{
            "endpointId" : " test id",
            "friendlyName": " friendly name",
            "capabilities": [
               {
                  "type": "test type 1",
                  "interface": " Test interface 1",
                  "version":  "1.0"
               },
               {
                  "type": "test type 2",
                  "interface": " Test interface 2",
                  "version":  "1.0"
               },
               {
                  "type": "test type 3",
                  "interface": " Test interface 3",
                  "version":  "1.1"
               }],   
               "additionalIdentification": {
                  "firmwareVersion": "19",
                  "deviceToken": "xxxxxxxxx",
                  "deviceTokenEncryptionType": "yyy",
                  "amazonDeviceType": "aabbccd",
                  "modelName": "mock model name",
                  "radioAddress": "1234567890"
               }
         }]
      }
   }
}
encode_sample_discover_directive()

以下のAlexa.Discovery.Discoverディレクティブをバイナリー形式にします。

{
   "directive": {
      "header": {
         "namespace": "Alexa.Discovery",
         "name": "Discover",
         "messageId": ""
      },
      "payload": {
         "scope": {
            "type": "",
            "token": ""
         }
      }
   }
}
encode_sample_state_update_directive()

以下のStateListener.StateUpdateディレクティブをバイナリー形式にします。このディレクティブは、タイマーが設定されたことをガジェットに通知します。

{
   "directive": {
      "header": {
         "namespace": "Alexa.Gadget.StateListener",
         "name": "StateUpdate",
         "messageId": ""
      },
      "payload": {
         "states": [{
            "name": "timers",
            "value": "active"
         }]
      }
   }
}
encode_sample_speechmarks_directive()

以下のSpeechData.Speechmarksディレクティブをバイナリー形式にします。

{
   "directive": {
      "header": {
         "namespace": "Alexa.Gadget.SpeechData",
         "name": "Speechmarks",
         "messageId": ""
      },
      "payload": {
         "playerOffsetInMilliSeconds": 0,
         "speechmarksData": [{
            "type": "viseme",
            "value": "s",
            "startOffsetInMilliSeconds": 130
         }]
      }
   }
}
main() 上記の関数を呼び出します。

出力

サンプルを実行すると、以下のように出力されます。

(オプション)ステップ6: 静的に割り当てられたデータサイズの変更

Alexa Gadgetsサンプルコードリポジトリには、ペイロードに1つ以上のフィールドを含む各ディレクティブおよびイベントの.protoファイルに対応するオプションファイルがあります。

このオプションファイルは、ペイロードの各フィールドの最大サイズを指定します。これにより、フィールドのメモリを静的に割り当てるよう.protoコンパイラに指示します。たとえば、Discover.Responseイベントのオプションファイルは、以下のようにcapabilities配列のサイズを定義します。

alexaDiscovery.DiscoverResponseEventPayloadProto.Endpoints.capabilities    max_count:32

コンパイラがコードを生成すると、32の機能にメモリを割り当てます。max_countを変更してコードを再コンパイルすることはできますが、最大値を増やすとデコードが失敗する可能性がありますので、注意してください。

メモリの使用状況を最適化したい場合は、max_count(またはその代わりに指定されたmax_size)を減らしてください。これにより、作成する.protoメッセージにコンパイラが静的に割り当てるメモリが減少します。