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



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

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

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

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

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

出力

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

Notifications - SetIndicator Example

Parsing Directive: name = SetIndicator, namespace=Notifications visualIndicator:1, audioIndicator=1, assetId=assetID1, url=url1

Creating setindicator directive: bytes written:57 0x0a 0x37 0x0a 0x1d 0x0a 0x0d 0x4e 0x6f 0x74 0x69 0x66 0x69 0x63 0x61 0x74 0x69 0x6f 0x6e 0x73 0x12 0x0c 0x53 0x65 0x74 0x49 0x6e 0x64 0x69 0x63 0x61 0x74 0x6f 0x72 0x12 0x16 0x08 0x01 0x10 0x01 0x1a 0x10 0x0a 0x08 0x61 0x73 0x73 0x65 0x74 0x49 0x44 0x31 0x12 0x04 0x75 0x72 0x6c 0x31

Alexa.Discovery - Discover.Response Example

Creating discover response event: bytes written:245 0x0a 0xf2 0x01 0x0a 0x24 0x0a 0x0f 0x41 0x6c 0x65 0x78 0x61 0x2e 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x79 0x12 0x11 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x2e 0x52 0x65 0x73 0x70 0x6f 0x6e 0x73 0x65 0x12 0xc9 0x01 0x0a 0xc6 0x01 0x0a 0x07 0x74 0x65 0x73 0x74 0x20 0x69 0x64 0x12 0x0d 0x66 0x72 0x69 0x65 0x6e 0x64 0x6c 0x79 0x20 0x6e 0x61 0x6d 0x65 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x31 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x31 0x1a 0x03 0x31 0x2e 0x30 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x32 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x32 0x1a 0x03 0x31 0x2e 0x30 0x5a 0x24 0x0a 0x0b 0x74 0x65 0x73 0x74 0x20 0x74 0x79 0x70 0x65 0x20 0x33 0x12 0x10 0x54 0x65 0x73 0x74 0x20 0x69 0x6e 0x74 0x65 0x72 0x66 0x61 0x63 0x65 0x20 0x33 0x1a 0x03 0x31 0x2e 0x31 0x62 0x3a 0x0a 0x02 0x31 0x39 0x12 0x09 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x78 0x1a 0x03 0x79 0x79 0x79 0x22 0x07 0x61 0x61 0x62 0x62 0x63 0x63 0x64 0x2a 0x0f 0x6d 0x6f 0x63 0x6b 0x20 0x6d 0x6f 0x64 0x65 0x6c 0x20 0x6e 0x61 0x6d 0x65 0x32 0x0a 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30

Parsing Event: name = Discover.Response, namespace=Alexa.Discovery endpoint count : 1 endpoint id: test id friendly name: friendly name description: manufacturer name: capability type: test type 1 capability interface: Test interface 1 capability version: 1.0 capability type: test type 2 capability interface: Test interface 2 capability version: 1.0 capability type: test type 3 capability interface: Test interface 3 capability version: 1.1 additional id - firmware version: 19 additional id - device token: xxxxxxxxx additional id - device token encryption type: yyy additional id - amazon device type: aabbccd additional id - model name: mock model name additional id - radioAddress: 1234567890

Alexa.Discovery - Discover Example

Creating discover directive: bytes written:31 0x0a 0x1d 0x0a 0x1b 0x0a 0x0f 0x41 0x6c 0x65 0x78 0x61 0x2e 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72 0x79 0x12 0x08 0x44 0x69 0x73 0x63 0x6f 0x76 0x65 0x72

Parsing Directive: name = Discover, namespace=Alexa.Discovery scope type: scope token:

Alexa.Gadget.StateListener - StateUpdate Example

Creating state update directive: bytes written:65 0x0a 0x3f 0x0a 0x29 0x0a 0x1a 0x41 0x6c 0x65 0x78 0x61 0x2e 0x47 0x61 0x64 0x67 0x65 0x74 0x2e 0x53 0x74 0x61 0x74 0x65 0x4c 0x69 0x73 0x74 0x65 0x6e 0x65 0x72 0x12 0x0b 0x53 0x74 0x61 0x74 0x65 0x55 0x70 0x64 0x61 0x74 0x65 0x12 0x12 0x0a 0x10 0x0a 0x06 0x74 0x69 0x6d 0x65 0x72 0x73 0x12 0x06 0x61 0x63 0x74 0x69 0x76 0x65

Parsing Directive: name = StateUpdate, namespace=Alexa.Gadget.StateListener state name: timers state value: active

Alexa.Gadget.SpeechData - Speechmarks Example

Creating speechmarks directive: bytes written:60 0x0a 0x3a 0x0a 0x26 0x0a 0x17 0x41 0x6c 0x65 0x78 0x61 0x2e 0x47 0x61 0x64 0x67 0x65 0x74 0x2e 0x53 0x70 0x65 0x65 0x63 0x68 0x44 0x61 0x74 0x61 0x12 0x0b 0x53 0x70 0x65 0x65 0x63 0x68 0x6d 0x61 0x72 0x6b 0x73 0x12 0x10 0x12 0x0e 0x0a 0x01 0x73 0x12 0x06 0x76 0x69 0x73 0x65 0x6d 0x65 0x18 0x82 0x01

Parsing Directive: name = Speechmarks, namespace=Alexa.Gadget.SpeechData player offset: 0 speechmark type: viseme speechmark value: s speechmark start offset: 130

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

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

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

alexaDiscovery.DiscoverResponseEventPayloadProto.Endpoints.capabilities    max_count:32

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

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