从 DDC 嗅探器到 sddc_sdk_lib 的数据解析

之前的 DDC 协定介绍 次要讲了设施退出、退出以及维持设施状态,而 SDK框架 sddc_sdk_lib 解析 次要讲了 SDK 库的构造和如何应用这个库,DDC 协定嗅探器 则介绍了一个前端构筑报文,对于具体的消息报文没有具体的阐明介绍。

这篇文章就着重讲一下消息报文的传输和解决流程。从之前写的 DDC 协定嗅探器 构建报文开始到嵌入式设施中 sddc_sdk_lib 对音讯解决解析,全面的解说下 message 音讯在整个 Spirit 1 网络中的传递门路以及数据在 sddc_sdk_lib 中是如何被一步一步解析的。


感觉本人真是太厉害了!(叉腰)

DDC 协定嗅探器中的音讯构建以及传输

嗅探器工具和 SDDC 的代码,老样子都放在了灵感桌面的机密宝库 中(不会应用 Git 的敌人请看简略无脑,上手即用 - 手把手教你应用 智能红外温度传感器代码以及依赖的 gitee 库!)。

前端的主页面代码在 ui/src/views/DeviceMessage.tsx 中,应用的 vue 以及 vant 库来开发,前端构建好 json 报文调用 sendMessage() 办法即可,其中调用 this.$api.sendMessage(this.clientMessage) 来向后端发送 http 申请;

  private async sendMessage() {    const res = this.checkClientMessage();    if (!res) return;    let len = 0;    if (this.clientMessageType === 'array') {      len = (this.clientMessage as IValueType[]).length;    } else {      len = Object.keys(this.clientMessage).length;    }    if (!len) {      return edger.notify.error('发送数据不可为空!');    }    try {      const res = await this.$api.sendMessage(this.clientMessage);    } catch (error) {      edger.notify.error('发送数据失败!')    }  }

后端在 eap/main.js 中会解决前端 http 申请传递的数据并调用 device 模块中的 device.send 办法将音讯通过 EdgerOS 向硬件设施发送音讯;

app.post('/send-message', (req, res) => {  if (Object.keys(req.body).length > 0) {    device.send(      req.body,      (error) => {        if (error) {          console.error('send message error!' + error);          io.emit('error', 'device.send error,try again!');        } else {          console.info('setSensorParams success!' + JSON.stringify(req.body));        }      },      3    );    res.send('send success!');  } else {    res.sendStatus(400, 'not Data!');  }});

eap/main.js 中 通过下方代码来监听硬件设施返回或者上报的数据;

      device.on('message', function (msg) {        io.emit('message', msg);      });

sddc_sdk_lib 中对音讯的解析解决

sddc_sdk_lib 库基于不同平台我也适配了不同版本,具体代码还是到我的 灵感桌面的机密宝库中拿,具体规定的报文格式如下:

// 利用向硬件设施被动查问 attr1 和 attr2 两个属性的状态{    "method": "get"    "obj" : ["attr1","attr2"]}// 利用向硬件设施被动设置或管制 attr1 和 attr2 两个属性的状态{    "method": "set"    "attr1" : "ON"    "attr2" : 0.12}// 硬件设施回复利用的查问或被动上报 attr1 和 attr2 两个属性的状态{    "method": "report"    "data" : {        "attr1" : "ON"        "attr2" : 0.12    }}

"attr1","attr2"就是要和写在对应的注册函数的字符串对应!(重点!敲黑板!)

SDDC_SDK_lib.c 文件中 sddc_on_message_lib() 函数是注册到 SDDC 协定中的音讯入口,函数中次要的性能就是辨认 method 字段是 get 形式还是 set 形式,再依据不同的形式调用不同的实现函数,其中 set 形式会遍历匹配属性名(也就是“attr1”,"attr2")是否统一后再调用对应的函数实现;

static sddc_bool_t sddc_on_message_lib(sddc_t *sddc, const uint8_t *uid, const char *message, size_t len){    cJSON   *root    = cJSON_Parse(message);    cJSON   *Json_method;    uint8_t uimethod =DDC_METHOD_VALIDE;    Json_method = cJSON_GetObjectItem(root, "method");    if (NULL == Json_method) {        return SDDC_FALSE;    }    if (cJSON_IsString(Json_method)) {        if (strcmp(Json_method->valuestring,"set") == 0) {            uimethod = DDC_METHOD_SET;        } else if (strcmp(Json_method->valuestring,"get") == 0) {            uimethod = DDC_METHOD_GET;        } else {            return SDDC_FALSE;        }    } else {        return SDDC_FALSE;    }    if (uimethod == DDC_METHOD_VALIDE) {        return SDDC_FALSE;    }    if (uimethod == DDC_METHOD_SET) {        int i;        /*         *  数字量、显示量先查问设置,避免开关量是设施的使能         */        for (i=0; i<G_config->num_dev_reg_num; i++) {            object_Number_Set(root, G_config->num_dev_reg->objname, G_config->num_dev_reg->Num_Fun);        }        for (i=0; i<G_config->dis_dev_num; i++) {            object_Display_Set(root, G_config->dis_dev_reg->objname, G_config->dis_dev_reg->Dis_Fun);        }        for (i=0; i<G_config->io_dev_reg_num; i++) {            object_IO_Set(root, G_config->io_dev_reg->objname, G_config->io_dev_reg->IO_Fun);        }    } else if (uimethod == DDC_METHOD_GET) {        object_get(sddc, uid, root, G_config->state_get_reg, G_config->state_get_reg_num);    } else {        return SDDC_FALSE;    }    return SDDC_TRUE;}

对于 set 形式,三种不同类型的属性有不同的实现,以下方 Num 类型的解决为例,如果数据类型不是 Num 就不会解决,相同就会调用注册的具体实现,注册相干能够参考这篇文章 基于sddc 协定的SDK框架 sddc_sdk_lib 解析 ;

static sddc_bool_t object_Number_Set(cJSON   *input,  const char *object_name, Num_Set do_fun){    cJSON   *Json_object;    Json_object = cJSON_GetObjectItem(input, object_name);    if (NULL == Json_object) {        return SDDC_FALSE;    }    if (cJSON_IsNumber(Json_object)) {        return do_fun((uint64_t)Json_object->valuedouble);    } else {        return SDDC_FALSE;    }    return SDDC_TRUE;}

对于 get 形式,调用对立的 object_get() 函数即可,会顺次遍历数组中的每个属性,调用注册的 get 函数实现,并构建 report 报文发送给利用;

static sddc_bool_t object_get(sddc_t *sddc, const uint8_t *uid, cJSON   *input,                            DEV_STATE_GET *state_get_list, int list_len){    cJSON   *Json_object;    cJSON   *root;    cJSON   *json_data;    char    *str;    int     i,j;    Json_object = cJSON_GetObjectItem(input, "obj");    if (NULL == Json_object) {        return SDDC_FALSE;    }    if (!cJSON_IsArray(Json_object)) {        return SDDC_FALSE;    }    root = cJSON_CreateObject();    cJSON_AddStringToObject(root, "method", "report");    json_data = cJSON_CreateObject();        for (i=0; i<cJSON_GetArraySize(Json_object); i++) {        for (j=0; j<list_len; j++) {            if (strcmp(cJSON_GetArrayItem(Json_object, i)->valuestring, state_get_list[j].objname) == 0) {                if (state_get_list[j].type == DEV_IO_TYPE) {                    if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {                        cJSON_AddStringToObject(json_data, state_get_list[j].objname, cstate_buf);                    }                } else if (state_get_list[j].type == DEV_NUM_TYPE ){                    if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {                        cJSON_AddNumberToObject(json_data, state_get_list[j].objname, atof(cstate_buf));                        printf("atof(cstate_buf) = %lf\n", atof(cstate_buf));                    }                } else if (state_get_list[j].type == DEV_DISPLAY_TYPE) {                    if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {                        cJSON   *display_obj;                        display_obj = cJSON_AddObjectToObject(json_data, state_get_list[j].objname);                        cJSON_AddStringToObject(display_obj, state_get_list[j].objname, cstate_buf);                    }                }            }        }    }    cJSON_AddItemToObject(root, "data", json_data);    str = cJSON_Print(root);    printf("object_get str = %s\n", str);    if (!uid) {        sddc_broadcast_message(sddc, str, strlen(str), 3, SDDC_FALSE, NULL);    } else {        sddc_send_message(sddc, uid, str, strlen(str), 3, SDDC_FALSE, NULL);    }    cJSON_free(str);    cJSON_Delete(root);    return SDDC_TRUE;}

总结

这就是基于 spirit 1 构建智能设施从利用到硬件的残缺数据链路,其实只须要依据规定好的 json 格局构建解决对应的 json 数据包就能够了,对于前端利用和嵌入式开发都很敌对,只须要关注各自的畛域,两头的就交个这个神奇的 spirit 1吧ヾ(◍°∇°◍)ノ゙。