物模型編程

更新時間:
Copy as MD

物模型是阿里雲物聯網平台為產品定義的資料模型,通過屬性、事件、服務的方式對產品支援的能力進行描述,在裝置開發時也需要以物模型的方式進行編程。

擷取Link SDK

不同版本的Link SDK下載,請參見SDK擷取,本文以Link SDK v3.2.0版本為例,介紹裝置屬性、服務、事件的開發。

重要
  • Linux環境下,可通過修改wrappers/os/ubuntu/HAL_OS_linux.c檔案中的預設裝置認證資訊,從而使用您在物聯網控制台建立裝置的身份資訊。
  • src/dev_model/examples目錄下提供了名為model_for_example.json的物模型描述檔案,您可以用已建立產品的ProductKey替換掉該檔案中的ProductKey值後,將該物模型檔案匯入到物聯網平台上的產品定義中,這樣可以快速的參照範例程式碼來體驗基於物模型的編程方式。

裝置屬性

  • 屬性上報說明
    您可以調用IOT_Linkkit_Report()函數來上報屬性,屬性上報時需要按照物聯網平台定義的屬性格式,使用JSON編碼後進行上報。樣本中函數user_post_property展示了如何使用IOT_Linkkit_Report進行屬性上報(對於異常情況的上報,詳見./src/dev_model/examples/linkkit_example_solo.c)。
    說明
    • property_payload = “{\”Counter\”:1}” 即是將屬性編碼為JSON對象。
    • 當上報屬性或者事件需要物理網平台應答時,可以通過IOT_IoctlIOTX_IOCTL_RECV_EVENT_REPLY選項進行設定。
    void user_post_property(void)
    {
        static int cnt = 0;
        int res = 0;
    
        char property_payload[30] = {0};
        HAL_Snprintf(property_payload, sizeof(property_payload), "{\"Counter\": %d}", cnt++);
    
        res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY,
                                (unsigned char *)property_payload, strlen(property_payload));
    
        EXAMPLE_TRACE("Post Property Message ID: %d", res);
    }
  • 屬性設定說明
    範例程式碼在回呼函數user_property_set_event_handler中擷取物聯網平台設定的屬性值,並原樣將收到的資料發回給物聯網平台,這樣可以更新在物聯網平台的裝置屬性值,在此處對收到的屬性值進行處理。
    說明 該回呼函數是在example初始化時使用IOT_RegisterCallback註冊的ITE_PROPERTY_SET事件對應的回呼函數。
    static int user_property_set_event_handler(const int devid, const char *request, const int request_len)
    {
        int res = 0;
        user_example_ctx_t *user_example_ctx = user_example_get_ctx();
        EXAMPLE_TRACE("Property Set Received, Devid: %d, Request: %s", devid, request);
    
        res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                                (unsigned char *)request, request_len);
        EXAMPLE_TRACE("Post Property Message ID: %d", res);
    
        return 0;
    }

裝置服務

在樣本程式中,當收到服務調用請求時(包括同步服務和非同步服務),會進入下面的回呼函數:

說明
  • 您需要動態分配記憶體空間用於存放服務應答資料,並通過response參數返回給SDK,SDK在完成應答資料發送後會負責response指向記憶體的釋放。
  • 對服務輸出參數為空白的情況,*response應指向存放JSON對象{}的記憶體,不能讓*response指向null 指標。
static int user_service_request_event_handler(const int devid, const char *serviceid, const int serviceid_len,
                                            const char *request, const int request_len,
                                            char **response, int *response_len){    int add_result = 0;
    cJSON *root = NULL, *item_number_a = NULL, *item_number_b = NULL;
    const char *response_fmt = "{\"Result\": %d}";

    EXAMPLE_TRACE("Service Request Received, Service ID: %.*s, Payload: %s", serviceid_len, serviceid, request);

    /* Parse Root */
    root = cJSON_Parse(request);
    if (root == NULL || !cJSON_IsObject(root)) {
        EXAMPLE_TRACE("JSON Parse Error");
        return -1;
    }

    if (strlen("Operation_Service") == serviceid_len && memcmp("Operation_Service", serviceid, serviceid_len) == 0) {
        /* Parse NumberA */
        item_number_a = cJSON_GetObjectItem(root, "NumberA");
        if (item_number_a == NULL || !cJSON_IsNumber(item_number_a)) {
            cJSON_Delete(root);
            return -1;
        }
        EXAMPLE_TRACE("NumberA = %d", item_number_a->valueint);

        /* Parse NumberB */
        item_number_b = cJSON_GetObjectItem(root, "NumberB");
        if (item_number_b == NULL || !cJSON_IsNumber(item_number_b)) {
            cJSON_Delete(root);
            return -1;
        }
        EXAMPLE_TRACE("NumberB = %d", item_number_b->valueint);

        add_result = item_number_a->valueint + item_number_b->valueint;

        /* 服務應答資料,資料長度通過response,response_len參數傳給SDK */
        *response_len = strlen(response_fmt) + 10 + 1;
        *response = (char *)HAL_Malloc(*response_len);
        if (*response == NULL) {
            EXAMPLE_TRACE("Memory Not Enough");
            return -1;
        }
        memset(*response, 0, *response_len);
        HAL_Snprintf(*response, *response_len, response_fmt, add_result);
        *response_len = strlen(*response);
    }

    cJSON_Delete(root);
    return 0;
}

裝置事件

樣本中使用IOT_Linkkit_TriggerEvent上報事件。其中展示了如何使用IOT_Linkkit_Report進行事件上報(對於異常情況的上報,詳細資料,請參見./src/dev_model/examples/linkkit_example_solo.c)。

void user_post_event(void){
    int res = 0;
    char *event_id = "HardwareError";
    char *event_payload = "{\"ErrorCode\": 0}";

    res = IOT_Linkkit_TriggerEvent(EXAMPLE_MASTER_DEVID, event_id, strlen(event_id),
                                event_payload, strlen(event_payload));
    EXAMPLE_TRACE("Post Event Message ID: %d", res);
}

關於上報訊息的格式說明及樣本

上報屬性時,屬性ID和值以JSON格式的形式放在IOT_Linkkit_Report()payload中,不同資料類型以及多個屬性的格式樣本如下:

/* 整型資料 */
char *payload = "{\"Brightness\":50}";

/* 浮點型資料上報 */
char *payload = "{\"Temperature\":11.11}";

/* 枚舉型資料上報 */
char *payload = "{\"WorkMode\":2}";

/* 布爾型資料上報, 在物模型定義中, 布爾型為整型, 取值為0或1, 與JSON格式的整型不同 */
char  *payload = "{\"LightSwitch\":1}";

/* 字串資料上報 */
char *payload = "{\"Description\":\"Amazing Example\"}";

/* 時間型資料上報, 在物模型定義中, 時間型為字串 */
char *payload = "{\"Timestamp\":\"1252512000\"}";

/* 複合類型屬性上報, 在物模型定義中, 符合類型屬性為JSON對象 */
char *payload = "{\"RGBColor\":{\"Red\":11,\"Green\":22,\"Blue\":33}}";

/* 多屬性上報, 如果需要上報以上各種資料類型的所有屬性, 將它們放在一個JSON對象中即可 */
char *payload = "{\"Brightness\":50,\"Temperature\":11.11,\"WorkMode\":2,\"LightSwitch\":1,\"Description\":\"Amazing Example\",\"Timestamp\":\"1252512000\",\"RGBColor:{\"Red\":11,\"Green\":22,\"Blue\":33}\"}";

/* 屬性payload準備好以後, 就可以使用如下介面進行上報了 */
IOT_Linkkit_Report(devid, ITM_MSG_POST_PROPERTY, payload, strlen(payload));           

上報事件與上報屬性的區別是,事件ID需要單獨拿出來,放在IOT_Linkkit_TriggerEvent()eventid中,而事件的上報內容,也就是物模型定義中事件的輸出參數,則使用與上報屬性相同的格式進行上報,樣本如下:

/* 事件ID為Error, 其輸出參數ID為ErrorCode, 資料類型為枚舉型 */
char *eventid = "Error";
char *payload = "{\"ErrorCode\":0}";

/* 事件ID為HeartbeatNotification, 其輸出參數有2個. 第一個是布爾型參數ParkingState, 第二個是浮點型參數VoltageValue */
char *eventid = "HeartbeatNotification";
char *payload = "{\"ParkingState\":1,\"VoltageValue\":3.0}";

/* 事件payload準備好以後, 就可以使用如下介面進行上報了 */
IOT_Linkkit_TriggerEvent(devid, event_id, strlen(event_id), payload, strlen(payload));

/* 從上面的樣本可以看出, 當事件的輸出參數有多個時, payload的格式與多屬性上報是相同的 */

基於MQTT Topic進行資料收發

雖然物模型編程的API並未返回MQTT編程介面IOT_MQTT_XXX()所需要的pClient參數,但基於MQTT Topic進行資料收發仍可和物模型編程混用。下文介紹了MQTT資料收發的介面和部分樣本。
  • MQTT資料收發介面:
    說明 以下介面的第1個參數都可使用參數0作為輸入,表示使用當前唯一的MQTT通道進行資料收發等操作。
    • IOT_MQTT_Construct
    • IOT_MQTT_Destroy
    • IOT_MQTT_Yield
    • IOT_MQTT_CheckStateNormal
    • IOT_MQTT_Subscribe
    • IOT_MQTT_Unsubscribe
    • IOT_MQTT_Publish
    • IOT_MQTT_Subscribe_Sync
    • IOT_MQTT_Publish_Simple
  • 使用樣本:
    • Topic訂閱

      如果要在使用物模型編程API的程式碼段落中表示對某個Topic進行訂閱,可以通過如下介面:

      IOT_MQTT_Subscribe(0, topic_request, IOTX_MQTT_QOS0, topic_callback, topic_context);
    • 資料上報

      如果要在使用物模型編程API的程式碼段落中表示在某個Topic進行發布(即資料上報),可以通過如下介面:

      IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, payload_len);

與物模型功能相關的API列表

函數名說明
IOT_Linkkit_Open建立本地資源,在進行網路報文互動之前,必須先調用此介面,得到一個會話的控制代碼。
IOT_Linkkit_Connect對主裝置或網關來說,將會建立裝置與物聯網平台的通訊。對於子裝置來說,將向物聯網平台註冊該子裝置,並添加主子裝置拓撲關係。
說明 如果是已經註冊的子裝置,則直接添加主子裝置拓撲關係。
IOT_Linkkit_YieldSDK佔有獨立線程,該函數只將接收到的網路報文分發到您的回呼函數中,否則表示將CPU交給SDK讓其接收網路報文並將訊息分發到您的回呼函數中。
IOT_Linkkit_Close若入參中的交談控制代碼為主裝置或者網關,則關閉網路連接並釋放SDK為該會話所佔用的所有資源。
IOT_Linkkit_TriggerEvent向物聯網平台發送事件報文、錯誤碼、異常警示等。
IOT_Linkkit_Report向物聯網平台發送沒有業務資料下發的上行報文,包括屬性值、裝置標籤、二進位透傳資料和子裝置管理等各種報文。
IOT_Linkkit_Query向物聯網平台發送存在業務資料下發的查詢報文,包括OTA狀態查詢、OTA韌體下載、子裝置拓撲查詢和NTP時間查詢等各種報文。
IOT_RegisterCallbackSDK註冊事件回呼函數,如物聯網平台串連成功或失敗,有屬性設定或服務要求到達,子裝置管理報文回覆等。
IOT_IoctlSDK進行各種參數運行時設定和擷取,以及運行狀態的資訊擷取等,實參可以是任何資料類型。