本文以C Link SDK中的Demo文件./demos/data_model_basic_demo.c為例,介紹如何調用Link SDK的API,使設備可使用物模型的功能。
背景信息
步驟一:初始化
添加頭文件。
…… …… #include "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;}
步驟二:配置功能
調用aiot_dm_setopt,配置以下功能。
關聯MQTT連接的句柄。
重要配置物模型功能參數前,請確保已配置設備認證信息等相關參數。具體操作,請參見MQTT配置連接參數。
示例代碼:
aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
相關參數:
配置項
示例值
說明
AIOT_DMOPT_MQTT_HANDLE
mqtt_handle
物模型功能的請求基于MQTT連接,通過該配置項,關聯MQTT連接句柄。
編寫物模型消息的回調函數。
定義物模型消息的回調函數。
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) { /* 對屬性上報、事件上報、獲取期望屬性值或者刪除期望屬性值指令的應答 */ case AIOT_DMRECV_GENERIC_REPLY: { demo_dm_recv_generic_reply(dm_handle, recv, userdata); } break; /* 屬性設置 */ case 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; /* 下行二進制數據 */ case 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; /* 上行二進制數據后, 物聯網平臺的回復報文 */ case AIOT_DMRECV_RAW_DATA_REPLY: { demo_dm_recv_raw_data_reply(dm_handle, recv, userdata); } break; default: break; } }
配置物模型消息的回調函數。
示例代碼:
aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);
相關參數:
配置項
示例值
說明
AIOT_DMOPT_RECV_HANDLER
demo_dm_recv_handler
接收到物模型消息時,調用該函數。
配置物聯網平臺是否應答報文。
您可以通過以下設置,要求物聯網平臺在接收到設備消息后,是否應答。
示例代碼:
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
中,消息收發前,您需在物聯網平臺上傳解析腳本,更多信息請參見物模型消息解析。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 receive reply for up_raw msg, data len = %d\r\n", recv->data.raw_data.data_len); }
步驟三:上報屬性
設備建立MQTT連接后,調用aiot_dm_send,向物聯網平臺發送屬性消息。要發送的屬性消息,通過aiot_dm_msg_t結構體,存放于指定位置,作為aiot_dm_send的入參。
根據以下不同的設備數據格式類型,為上報屬性消息,編寫代碼。
ICA標準數據格式(Alink JSON)
添加物模型。
設置屬性消息的內容。
示例代碼:
默認模塊:
/* 單個上報屬性的內容 */ demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}"); /* 批量上報屬性的內容 */ demo_send_property_batch_post(dm_handle, "{\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518}],\"WF\": [{\"value\": 3,\"time\":1612684518}]}}");
自定義模塊:
demo_send_property_post(dm_handle, "{\"demo_extra_block:NightLightSwitch\": 1}");
示例消息說明:
物模型上報消息為JSON格式。以下為上報消息的示例及其對應的Alink數據格式,更多信息,請參見設備上報屬性。
說明為了方便使用,您只需關注設備上報的數據(即Alink數據格式中params中的內容),Link SDK會對上報的消息封裝處理。
消息類型
上報消息的示例
對應的Alink格式
單個屬性
{\"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": { "Power": [{ "value": "on", "time": 1612684518000 }, ], "WF": [{ "value": 3, "time": 1612684518000 }, ] }, }
批量上報屬性消息后,物聯網平臺基于批量上報的消息,發送應答報文。如果您需確認物聯網平臺已接收到該上報的消息,需訂閱Topic
/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);
其中:
a18wP******
為設備的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; return 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; return aiot_dm_send(dm_handle, &msg); }
透傳/自定義格式
在物聯網平臺上傳物模型數據解析腳本。
需更多信息,請參見透傳/自定義格式的消息。
編寫設備上報二進制消息的代碼。
示例代碼:
{ aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 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); }
步驟四:設置屬性
您可以調用物聯網平臺的云端API 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: 以下代碼演示如何對物聯網平臺的屬性設置指令進行應答, 您可取消注釋查看演示效果 */ /* { 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: 以下代碼演示如何發送二進制格式數據, 如需使用,需在物聯網平臺部署對應的數據透傳腳本。 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 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); } */ }
步驟五:上報事件
設備建立MQTT連接后,調用aiot_dm_send,向物聯網平臺發送事件消息。要發送的事件消息,通過aiot_dm_msg_t結構體,存放于指定位置,作為aiot_dm_send的入參。
根據以下不同的設備數據格式類型,為上報事件,編寫代碼。
如果您的設備數據格式為透傳/自定義格式,設備上報事件與上報屬性的接口一致,具體流程,請參見步驟三中的上報透傳/自定義格式的消息。
如果您的設備數據格式為ICA標準數據格式(Alink JSON),設備端代碼編寫的流程,請參見下文。
添加物模型。
設置事件消息內容。
示例代碼:
demo_send_event_post(dm_handle, "Error", "{\"ErrorCode\": 0}");
示例消息說明:
物模型上報消息為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; return aiot_dm_send(dm_handle, &msg); }
步驟六:調用服務
您可以調用物聯網平臺的云端API 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_invoke
recv->data.async_service_invoke
字段中數據的格式與Alink格式數據中params的值一致,更多信息,請參見服務消息。
透傳/自定義格式
recv->data.raw_data
recv->data.raw_service_invoke
需自行上傳解析腳本,解析指令內容。更多信息,請參見透傳/自定義格式的消息。
如果您定義的回調函數不對調用服務指令做應答,自行編寫應答處理邏輯函數時,請確保應答消息中的以下信息與物聯網平臺請求中的該參數值一致。
同步:
rrpc_id
和msg_id
異步:
msg_id
向物聯網平臺返回服務的應答報文時,需注意超時時間:
同步:接收同步服務消息后,請在8秒內做出應答。否則,即使設備端已收到消息,也視其失敗。
異步:您可根據業務需要,自定義異步調用超時的代碼邏輯。物聯網平臺未對此做限制。
建議的處理邏輯:
執行服務指令。
向物聯網平臺返回服務的應答報文。
示例代碼的處理邏輯:
示例代碼僅打印接收的消息,如需演示上報設置的屬性,請取消示例代碼中
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: 以下代碼演示如何對來自物聯網平臺的同步調用服務進行應答。 */ { 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: 以下代碼演示如何對來自物聯網平臺的異步調用服務進行應答, 您可取消注釋查看演示效果。 */ /* { 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"); } } */ }
透傳/自定義格式
二進制異步服務調用的處理邏輯,與二進制設置屬性的接口一致。具體操作,請參見自定義Topic消息解析。
二進制同步服務調用的處理邏輯,示例代碼如下:
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 sync service rrpc_id = %s, data_len = %d\r\n", recv->data.raw_service_invoke.rrpc_id, recv->data.raw_service_invoke.data_len); /* TODO: 以下代碼演示如何對來自物聯網平臺的同步調用服務進行應答, 您可取消注釋查看演示效果 */ /* { aiot_dm_msg_t msg; uint8_t raw_data[] = {0x01, 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); } */ }
步驟七:斷開連接
MQTT接入常應用于長連接的設備,程序通常不會運行至此。
例程的主線程任務為配置參數并成功建立連接。連接建立后,主線程可進入休眠。
調用aiot_mqtt_disconnect,向物聯網平臺發送斷開連接的報文,然后斷開網絡連接。
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;
}
步驟八:退出程序
調用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;
}