从 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吧ヾ(◍°∇°◍)ノ゙。

同人逼死官方系列!从 DDC 嗅探器到 sddc_sdk_lib 的数据解析的更多相关文章

  1. 同人逼死官方系列!基于sddc 协议的SDK框架 sddc_sdk_lib 解析

    基于sddc 协议的SDK框架 sddc_sdk_lib 解析 之前在移植 libsddc 库的时候感觉官方 demo 太低效了( ̄. ̄),复制粘贴代码好累,而且写出一个BUG,其他复制的代码整个就裂 ...

  2. C# 串口操作系列(4) -- 协议篇,文本协议数据解析

    C# 串口操作系列(4) -- 协议篇,文本协议数据解析 标签: c#uiobjectstringbyte 2010-06-09 01:50 19739人阅读 评论(26) 收藏 举报  分类: 通讯 ...

  3. C# 串口操作系列(3) -- 协议篇,二进制协议数据解析

    原文地址:http://blog.csdn.net/wuyazhe/article/details/5627253 我们的串口程序,除了通用的,进行串口监听收发的简单工具,大多都和下位机有关,这就需要 ...

  4. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  5. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  6. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  7. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  8. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  9. ADs系列之通用数据解析服务GAS(即将开源)

    面对成百上千的生产系统用户操作数据接入落地,你是否厌倦了每次机械编写打包解包的代码?对一次性接入多个数据的时候,还要对不同人联调,费时费力,你是否还会手忙脚乱,忙中不断出错?是否当数据出问题了,用的时 ...

随机推荐

  1. Docker宿主机管理

    不需要每次输入sudo 在当前用户的用户目录.bashrc中增加以下内容,此后使用docker命令时不需要每次都增加sudo. echo 'sudo usermod -aG docker $USER' ...

  2. 企业级镜像仓库 harbor

    企业级镜像仓库 harbor 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:跨 Docker 宿主机网络 overlay 类型 h ...

  3. FastAPI(6)- get 请求 - 详解 Query

    可选参数 上一篇文章讲过查询参数可以不是必传的,可以是可选参数 from fastapi import FastAPI from typing import Optional import uvico ...

  4. PHP的内置WEB服务器

    在很多时候,我们需要简单的运行一个小 demo 来验证一些代码或者轮子是否可用,是否可以运行起来,但是去配 nginx 或者 apache 都很麻烦,其实,PHP CLI 已经提供了一个简单的测试服务 ...

  5. 转mybatis返回自增主键,亲测

    重点是获取对象的主键值而不是insert的返回值 Mybatis获取插入数据的主键时,返回值总是1xml里的写法 第一种方式 <insert id="insertLogin" ...

  6. 【Azure 应用服务】App Service For Linux 部署PHP Laravel 项目,如何修改首页路径为 wwwroot\public\index.php

    问题描述 参考官方文档部署 PHP Laravel 项目到App Service for Linux环境中,但是访问应用时候遇见了500 Server Error 错误. 从部署的日志中,可以明确看出 ...

  7. Nginx系列(7)- Nginx安装 | Linux

    step-1 安装gcc 安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,则需要安装: [root@localhost ~]# yum install ...

  8. Typescript, ES6

    * typescript 中文文档 https://www.tslang.cn/docs/home.html * ECMAScript 6 入门 http://es6.ruanyifeng.com/# ...

  9. ubuntu18.04 kuebadm 安装 k8s-1.15.9

    ubuntu kubeadm 搭建kubernetes1.15.9 准备 update && 安装docker apt-get update apt install docker 修改 ...

  10. php页面 数组根据下标来排序

    $a = [ ['id'=>1,'title'=>'星期二的早晨','author'=>'张三','date'=>'2021-6-1'], ['id'=>2,'title ...