このトピックでは、Link SDK for CのAPI操作を呼び出して、デバイスにThing Specification Language (TSL) モデル機能を実装する方法について説明します。 この例では、という名前のサンプルコードファイルです。/demos/data_model_basic_demo.cが使用されます。
背景情報
ステップ1: SDKの初期化
ヘッダーファイルを追加します。
を含む…… …… # 「aiot_dm_api.h」基になる依存関係を追加し、ログ出力機能を設定します。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);aiot_dm_init操作を呼び出して、
data-modelという名前のクライアントインスタンスを作成し、デフォルトパラメーターを初期化します。dm_handle = aiot_dm_init(); if (dm_handle == NULL) { printf("aiot_dm_init failed"); return -1;}
ステップ2: 必要な機能の設定
aiot_dm_setopt操作を呼び出して、次の項目を設定します。
MQTT接続ハンドルとの関連付け
重要デバイスタグ固有のパラメーターを設定する前に、デバイス検証情報が指定されていることを確認してください。詳細は、「例」をご参照ください。
サンプルコード:
aiot_dm_setopt(dm_handle、AIOT_DMOPT_MQTT_HANDLE、mqtt_handle);パラメーター:
パラメーター
例
説明
AIOT_DMOPT_MQTT_HANDLE
mqtt_handle
MQTT接続を確立する必要があります。 このパラメータは、MQTT接続ハンドルに関連付けるために使用されます。
TSLメッセージを処理するようにコールバック関数を設定します。
コールバック関数を定義します。
static void demo_dm_recv_handler(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_handler, type = % d\r\n", recv->type); switch (recv->type) { /* デバイスがプロパティを送信、イベントを送信、希望のプロパティを要求、または希望のプロパティを削除した後、IoT Platformから応答を送信します。 */ ケースAIOT_DMRECV_GENERIC_REPLY: { demo_dm_recv_generic_reply(dm_handle, recv, userdata); } break; /* プロパティを設定します。 */ ケースAIOT_DMRECV_PROPERTY_SET: { demo_dm_recv_property_set(dm_handle, recv, userdata); } break; /* 非同期的にサービスを呼び出す * / case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: { demo_dm_recv_async_service_invoke(dm_handle, recv, userdata); } break; /* 同期呼び出しサービス * / case AIOT_DMRECV_SYNC_SERVICE_INVOKE: { demo_dm_recv_sync_service_invoke(dm_handle, recv, userdata); } break; /* 下流のバイナリデータを送信 * / ケースAIOT_DMRECV_RAW_DATA: { demo_dm_recv_raw_data(dm_handle, recv, userdata); } break; /* サービスを同期的に呼び出してバイナリデータを送信すると、rrpc_idパラメーターがバイナリデータに追加されます。 */ case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: { demo_dm_recv_raw_sync_service_invoke(dm_handle, recv, userdata); } break; /* デバイスがバイナリデータを送信した後、IoT Platformから応答を送信します。 */ case AIOT_DMRECV_RAW_DATA_REPLY: { demo_dm_recv_raw_data_reply(dm_handle, recv, userdata); } break; default: break; } }TSLメッセージを処理するようにコールバック関数を設定します。
サンプルコード:
aiot_dm_setopt(dm_handle、AIOT_DMOPT_RECV_HANDLER、(void *)demo_dm_recv_handler);パラメーター:
パラメーター
例
説明
AIOT_DMOPT_RECV_HANDLER
demo_dm_recv_handler
この関数は、TSLメッセージを受信したときに呼び出されます。
IoT Platformがレスポンスを返すかどうかを指定します。
次のコードを使用して、IoT Platformがデバイスメッセージを受信した後に応答を返すかどうかを指定できます。
サンプルコード:
uint8_t post_reply = 1; …… …… aiot_dm_setopt(dm_handle, AIOT_DMOPT_POST_REPLY, (void *)&post_reply);パラメーター:
パラメーター
例
説明
AIOT_DMOPT_POST_REPLY
1
有効な値:
1: 応答を返します
0: 応答を返さない
AIOT_DMOPT_POST_REPLYパラメーターの値が1の場合、応答メッセージを処理するロジックを指定する必要があります。
ビジネス要件に基づいて処理ロジックを指定できます。 この例では、応答メッセージが印刷される。
ICA標準データ形式 (Alink JSON)

応答メッセージは、
recv->data.generic_reply構造に含まれています。 メッセージはAlinkデータ形式を使用します。 詳細については、「デバイスのプロパティ、イベント、およびサービス」をご参照ください。static void demo_dm_recv_generic_reply(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata){ printf("demo_dm_recv_generic_reply msg_id = % d, code = % d, data = %.* s, message = %.* s\r\n" 、 recv->data.generic_reply.msg_id、 recv->data.generic_reply.code、 recv->data.generic_reply.data_len、 recv->data.generic_reply.data、 recv->data.generic_reply.message_len、 recv->data.generic_reply.message; }カスタムデータ形式

応答メッセージは、
recv->data.raw_data構造に含まれています。 メッセージを送信する前に、解析スクリプトをIoT Platformにアップロードする必要があります。 詳細については、「メッセージ解析」をご参照ください。static void demo_dm_recv_raw_data_reply(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_async_service_invoke reply for up_raw msg, data len = % d\r\n", recv->data.raw_data.data_len); }
ステップ3: デバイスのプロパティを送信する
MQTT経由でデバイスをIoT Platformに接続した後、aiot_dm_send操作を呼び出してデバイスのプロパティを送信します。 送信されるプロパティは、aiot_dm_msg_t構造体を使用して指定された場所に格納されます。 この構造は、aiot_dm_send操作の入力パラメーターです。
次のデバイスデータ形式に基づいて、デバイスプロパティを送信するコードを作成します。
ICA標準データ形式 (Alink JSON)
TSL機能を追加します。
IoT Platformコンソールにログインします。 [製品の詳細] ページの [機能の定義] タブで、プロパティをデフォルトのTSLモジュールまたはカスタムモジュールに追加します。 たとえば、
demo_extra_blockという名前のカスタムモジュールを作成できます。 詳細については、「TSL機能の追加」をご参照ください。次の表に、この例で追加されるTSLプロパティを示します。
TSLモジュール
機能
ID
データ型
データ定義
読み取り /書き込みタイプ
デフォルトモジュール
夜ライトスイッチ1
LightSwitch
bool
0: on
1: オフ
Read と write
Power
Power
text
データ長: 10,240文字
Read と write
操作の流れ
WF
int32
有効な値: 0〜10。
ステップサイズ: 1
ユニット: A
Read と write
demo_extra_block
夜ライトスイッチ2
NightLightSwitch
bool
0: on
1: オフ
Read と write
この例では、TSLファイルをインポートして機能を追加することもできます。 詳細については、「TSL機能の一括追加」をご参照ください。
プロパティメッセージの内容を指定します。
サンプルコード:
デフォルトモジュール:
/* プロパティを送信します。 */ demo_send_property_post(dm_handle, "{\" LightSwitch\": 0}"); /* 一度に複数のプロパティを送信します。 */ demo_send_property_batch_post(dm_handle, "{\" properties\":{\" Power\": [{\" value\":\" on\",\" time\":1612684518000}],\" WF\": [{\" value\": 3,\" time\":1612684518000}]}");カスタムモジュール:
demo_send_property_post(dm_handle, "{\" demo_extra_block:NightLightSwitch\": 1}");
サンプルメッセージ:
送信されたTSLメッセージはJSON形式です。 次の表に、サンプルメッセージとメッセージのAlink形式を示します。 詳細については、「デバイスのプロパティ、イベント、およびサービス」をご参照ください。
説明必要なのは、Alinkデータのparamsパラメーターで示されるメッセージコンテンツのみです。 Link SDKは、メッセージをカプセル化して処理します。
メッセージタイプ
例
Alink形式
1つのプロパティ
{\"LightSwitch\": 0}{ "id": "123", "version": "1.0"、 "sys":{ "ack":0 }, "params": { "LightSwitch": 0 }, "method": "thing.event.property.post" }複数のプロパティ
{\"properties\" :{\ "Power\": [{\"value\":\"on\",\"time\":1612684518000}],\"WF\": [{\"value\": 3,\"time\":1612684518000}]}}{ "id": 123, "version": "1.0"、 "sys":{ "ack":0 }, "method": "thing.event.property.batch.post" 、 "params": { "properties": { "パワー": [{ "value": "on", "time": 1612684518000 }, ], "WF": [{ "value": 3、 "time": 1612684518000 }, ] }, }複数のプロパティが送信されると、IoT Platformはメッセージが受信されたことを確認する応答を送信します。 応答を受け取るには、
/sys/a18wP ******/LightSwitch/thing/event/property/batch/post_replyトピックをサブスクライブする必要があります。サンプルコード:
aiot_mqtt_sub(mqtt_handle, "/sys/a18wP ******/LightSwitch/thing/event/property/batch/post_reply", NULL, 1, NULL);説明:
a1oGs ******はデバイスのProductKeyです。LightSwitchは、デバイスのDeviceNameです。
プロパティを送信する関数を定義します。
/* プロパティを送信します。 * / int32_t demo_send_property_post(void * dm_handle, char * params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_POST; msg.data.property_post.params = params; aiot_dm_send(dm_handle, &msg) を返します。} /* 一度に複数のプロパティを送信します。 * / int32_t demo_send_property_batch_post(void * dm_handle, char * params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_BATCH_POST; msg.data.property_post.params = params; aiot_dm_send(dm_handle, &msg) を返します。}
カスタムデータ形式
データ解析スクリプトをIoT Platformにアップロードします。
詳細については、「データ解析とは」をご参照ください。
バイナリデバイスデータを送信するコードを記述します。
サンプルコード:
{ aiot_dm_msg_t msg; uint8_t raw_data[] = {0x 01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_DATA; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle、&msg); }
通信にTSLモデルを使用する場合は、メッセージ数がしきい値を超えないようにしてください。
通信制限の詳細については、「制限」をご参照ください。
メッセージ数がしきい値を超えた場合は、IoT Platformコンソールにログインして、蓄積されたメッセージを表示します。 詳細については、「コンシューマーグループの表示と監視」をご参照ください。
ステップ4: デバイスのプロパティを設定する
SetDevicePropertyまたはSetDevicesProperty操作を呼び出して、プロパティ設定コマンドをデバイスに送信できます。デバイスはaiot_mqtt_recv操作を呼び出してコマンドを受け取り、demo_dm_recv_handlerコールバック関数を呼び出してコマンドを処理します。
コールバック関数の処理ロジックを指定する場合は、次の項目に注意してください。
使用法ノート:
受信したメッセージは、aiot_dm_recv_handler_tコールバック関数の入力パラメーターとして使用されます。 メッセージは、aiot_dm_recv_t構造体を使用して指定された場所に格納されます。
受信されたメッセージ内のコマンド内容は、デバイスのデータフォーマットに基づいて変化する。 コマンドフィールドを次の表に示します。
デバイスデータ形式
受信メッセージのフィールド
説明
ICA標準データ形式 (Alink JSON)
recv->data.property_set.paramsこのフィールドは、Alinkデータのparamsパラメーターと同じです。 詳細については、「デバイスのプロパティ、イベント、およびサービス」をご参照ください。
カスタムデータ形式
recv->data.raw_dataデバイス上のデータを解析する必要があります。 詳細については、「データ解析とは」をご参照ください。
推奨処理ロジック:
デバイスのプロパティを更新します。
更新されたプロパティを送信します。
サンプルコードの処理ロジック:
この例では、受信したメッセージが印刷される。 更新されたプロパティを送信するには、TODOの下にあるコメントシンボル (
/*および*/) を削除します。ICA標準データ形式 (Alink JSON)
static void demo_dm_recv_property_set(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_property_set msg_id = % ld, params = %.* s\r\n", (unsigned long)recv->data.property_set.msg_id、 recv->data.property_set.params_len、 recv->data.property_set.params); /* TODO: 次のサンプルコードは、IoT Platformがプロパティを設定するコマンドを送信した後に応答を返す方法を示しています。 サンプルコードを実行するには、サンプルコードのコメントを解除します。 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY; msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id; msg.data.property_set_reply.code = 200; msg.data.property_set_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ }カスタムデータ形式
static void demo_dm_recv_raw_data(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_raw_data raw data len = % d\r\n", recv->data.raw_data.data_len); /* TODO: 次のサンプルコードは、バイナリデータの送信方法を示しています。 バイナリデータを送信する場合は、IoT Platformコンソールでデータ解析スクリプトを設定する必要があります。 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x 01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_DATA; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle、&msg); } */ }
ステップ5: デバイスイベントの送信
MQTT経由でデバイスをIoT Platformに接続した後、aiot_dm_send操作を呼び出してデバイスのプロパティを送信します。 送信されるイベントは、aiot_dm_msg_t構造体を使用して指定された場所に保存されます。 この構造は、aiot_dm_sendの入力パラメーターです。
次のデバイスデータ形式に基づいてデバイスプロパティを送信するコードをします。
デバイスデータがカスタム形式の場合、同じAPI操作を使用してイベントとプロパティを送信できます。 詳細については、「手順3」をご参照ください。
デバイスデータがICA標準データ形式 (Alink JSON) の場合は、次の手順を実行します。
TSLモデルを追加します。
IoT Platformコンソールにログインします。 [製品の詳細] ページの [機能の定義] タブで、イベントを製品に追加します。 詳細については、「TSL機能の追加」をご参照ください。
次の表に、この例で追加されるTSLイベントを示します。
機能
ID
イベントタイプ
出力パラメータ
エラー
エラー
エラー
パラメータ名: エラーコード
パラメータ識別子: ErrorCode
データ型: enum
列挙アイテム:
0: デバイスのハードウェアでエラーが発生しました。
1: デバイスソフトウェアでエラーが発生しました。
2: 不明なエラーが発生しました。
この例では、TSLファイルをインポートして機能を追加することもできます。 詳細については、「TSL機能の一括追加」をご参照ください。
イベントメッセージの内容を指定します。
サンプルコード:
demo_send_event_post(dm_handle, "Error", "{\" ErrorCode\": 0}");サンプルメッセージ:
送信されたTSLメッセージはJSON形式です。 次の表に、サンプルメッセージとメッセージのAlink形式を示します。 詳細については、「デバイスのプロパティ、イベント、およびサービス」をご参照ください。
説明必要なのは、Alinkデータのparamsパラメーターで示されるメッセージコンテンツのみです。 Link SDKは、メッセージをカプセル化して処理します。
例
Alink形式
{\"ErrorCode\": 0}{ "id": "123", "version": "1.0"、 "params": { "ErrorCode": 0 }, "method": "thing.event.alarm.post" }
イベントを送信する関数を定義します。
int32_t demo_send_event_post(void * dm_handle, char * event_id, char * params) { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_EVENT_POST; msg.data.event_post.event_id = event_id; msg.data.event_post.params = params; aiot_dm_send(dm_handle, &msg) を返します。}
ステップ6: デバイスサービスを呼び出す
InvokeThingServiceまたはInvokeThingsService操作を呼び出して、サービス呼び出しコマンドをデバイスに送信できます。デバイスはaiot_mqtt_recv操作を呼び出してコマンドを受信し、demo_dm_recv_handlerコールバック関数を呼び出してコマンドを処理します。
コールバック関数の処理ロジックを指定する場合は、次の項目に注意してください。
使用法ノート:
受信したメッセージは、aiot_dm_recv_handler_tコールバック関数の入力パラメーターとして使用されます。 メッセージは、aiot_dm_recv_t構造体を使用して指定された場所に格納されます。
受信されたメッセージ内のコマンド内容は、デバイスのデータフォーマットに基づいて変化する。 コマンドフィールドを次の表に示します。
デバイスデータ形式
同期メッセージのフィールド
非同期メッセージのフィールド
説明
ICA標準データ形式 (Alink JSON)
recv->data.sync_service_invokerecv->data.async_service_invokeこのフィールドは、Alinkデータのparamsパラメーターと同じです。 詳細については、「デバイスのプロパティ、イベント、およびサービス」をご参照ください。
カスタムデータ形式
recv->data.raw_datarecv->data.raw_service_invokeコマンドの内容を解析するスクリプトをアップロードする必要があります。 詳細については、「データ解析とは」をご参照ください。
定義されたコールバック関数にサービス呼び出しに応答するロジックがない場合は、ロジックを指定するように関数を構成する必要があります。 レスポンスを送信するように関数を設定するときは、レスポンスの次のパラメーターの値がリクエストの値と同じであることを確認してください。
同期呼び出し:
rrpc_idおよびmsg_id非同期呼び出し:
msg_id
IoT Platformに応答を送信するときは、タイムアウト期間に注意してください。
同期呼び出し: デバイスがリクエストを受信した後、8秒以内に応答を送信します。 それ以外の場合、IoT Platformは、デバイスがリクエストを受信したかどうかに関係なく、呼び出しが失敗したと判断します。
非同期呼び出し: ビジネス要件に基づいてタイムアウト期間を指定できます。 制限は適用されません。
推奨処理ロジック:
コマンドを処理します。
応答をIoT Platformに返します。
サンプルコードの処理ロジック:
この例では、受信したメッセージが印刷される。 更新されたプロパティを送信するには、TODOの下にあるコメントシンボル (
/*および*/) を削除します。ICA標準データ形式 (Alink JSON)
同期コール:
static void demo_dm_recv_sync_service_invoke(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_sync_service_invoke msg_id = % ld, rrpc_id = % s, service_id = % s, params = %.* s\r\n" 、 (unsigned long)recv->data.sync_service_invoke.msg_id、 recv->data.sync_service_invoke.rrpc_id、 recv->data.sync_service_invoke.service_id、 recv->data.sync_service_invoke.params_len、 recv->data.sync_service_invoke.params); /* TODO: 次のサンプルコードは、IoT Platformがデバイスサービスを同期的に呼び出した後に応答を返す方法を示しています。 */ { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY; msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id; msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id; msg.data.sync_service_reply.code = 200; msg.data.sync_service_reply.service_id = "SetLightSwitchTimer"; msg.data.sync_service_reply.data = "{}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } }非同期呼び出し:
static void demo_dm_recv_async_service_invoke(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_async_service_invoke msg_id = % ld, service_id = % s, params = %.* s\r\n" 、 (unsigned long)recv->data.async_service_invoke.msg_id、 recv->data.async_service_invoke.service_id, recv->data.async_service_invoke.params_len, recv->data.async_service_invoke.params); /* TODO: 次のサンプルコードは、IoT Platformがデバイスサービスを非同期で呼び出した後に応答を返す方法を示しています。 サンプルコードを実行するには、サンプルコードのコメントを解除します。 */ /* { aiot_dm_msg_t msg; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY; msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id; msg.data.async_service_reply.code = 200; msg.data.async_service_reply.service_id = "ToggleLightSwitch"; msg.data.async_service_reply.data = "{\" dataA\": 20}"; int32_t res = aiot_dm_send(dm_handle, &msg); if (res < 0) { printf("aiot_dm_send failed\r\n"); } } */ }
カスタムデータ形式
バイナリデータ形式に基づいて非同期でサービスを呼び出す操作は、バイナリデータ形式に基づいてプロパティを構成する操作と同じように機能します。 詳細については、「カスタムトピックに送信されるメッセージの解析」をご参照ください。
次のサンプルコードは、バイナリデータ形式に基づいてサービスを同期的に呼び出す方法を示しています。
static void demo_dm_recv_raw_sync_service_invoke(void * dm_handle, const aiot_dm_recv_t * recv, void * userdata) { printf("demo_dm_recv_raw_sync_service_invoke raw同期サービスrrpc_id = % s, data_len = % d\r\n", recv->data.raw_service_invoke.rrpc_id、 recv->data.raw_service_invoke.data_len); /* TODO: 次の例は、IoT Platformがサービスを同期的に呼び出すコマンドを送信した後に応答を返す方法を示しています。 サンプルコードを実行するには、サンプルコードのコメントを解除します。* / /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x 01, 0x02}; memset(&msg, 0, sizeof(aiot_dm_msg_t)); msg.type = AIOT_DMMSG_RAW_SERVICE_REPLY; msg.data.raw_service_reply.rrpc_id = recv->data.raw_service_invoke.rrpc_id; msg.data.raw_data.data = raw_data; msg.data.raw_data.data_len = sizeof(raw_data); aiot_dm_send(dm_handle、&msg); } */ }
ステップ7: 接続を閉じる
MQTT接続は、永続的に接続されたままのデバイスに適用されます。 IoT Platformからデバイスを手動で切断できます。
この例では、メインスレッドを使用してパラメータを設定し、接続を確立します。 接続が確立されたら、メインスレッドを一時停止できます。
aiot_mqtt_disconnect操作を呼び出して、デバイスをIoT Platformから切断します。
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x % 04X\n", -res);
return -1;
}ステップ8: プログラムを終了する
aiot_dm_deinit操作を呼び出して、data-modelクライアントインスタンスを削除し、対応するリソースをリリースします。
res = aiot_dm_deinit(&dm_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dm_deinit failed: -0x % 04X\n", -res);
return -1;
}