从 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 请求;

  1. private async sendMessage() {
  2. const res = this.checkClientMessage();
  3. if (!res) return;
  4. let len = 0;
  5. if (this.clientMessageType === 'array') {
  6. len = (this.clientMessage as IValueType[]).length;
  7. } else {
  8. len = Object.keys(this.clientMessage).length;
  9. }
  10. if (!len) {
  11. return edger.notify.error('发送数据不可为空!');
  12. }
  13. try {
  14. const res = await this.$api.sendMessage(this.clientMessage);
  15. } catch (error) {
  16. edger.notify.error('发送数据失败!')
  17. }
  18. }

后端在 eap/main.js 中会处理前端 http 请求传递的数据并调用 device 模块中的 device.send 方法将消息通过 EdgerOS 向硬件设备发送消息;

  1. app.post('/send-message', (req, res) => {
  2. if (Object.keys(req.body).length > 0) {
  3. device.send(
  4. req.body,
  5. (error) => {
  6. if (error) {
  7. console.error('send message error!' + error);
  8. io.emit('error', 'device.send error,try again!');
  9. } else {
  10. console.info('setSensorParams success!' + JSON.stringify(req.body));
  11. }
  12. },
  13. 3
  14. );
  15. res.send('send success!');
  16. } else {
  17. res.sendStatus(400, 'not Data!');
  18. }
  19. });

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

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

sddc_sdk_lib 中对消息的解析处理

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

  1. // 应用向硬件设备主动查询 attr1 和 attr2 两个属性的状态
  2. {
  3. "method": "get"
  4. "obj" : ["attr1","attr2"]
  5. }
  6. // 应用向硬件设备主动设置或控制 attr1 和 attr2 两个属性的状态
  7. {
  8. "method": "set"
  9. "attr1" : "ON"
  10. "attr2" : 0.12
  11. }
  12. // 硬件设备回复应用的查询或主动上报 attr1 和 attr2 两个属性的状态
  13. {
  14. "method": "report"
  15. "data" : {
  16. "attr1" : "ON"
  17. "attr2" : 0.12
  18. }
  19. }

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

SDDC_SDK_lib.c 文件中 sddc_on_message_lib() 函数是注册到 SDDC 协议中的消息入口,函数中主要的功能就是识别 method 字段是 get 方式还是 set 方式,再根据不同的方式调用不同的实现函数,其中 set 方式会遍历匹配属性名(也就是“attr1”,"attr2")是否一致后再调用对应的函数实现;

  1. static sddc_bool_t sddc_on_message_lib(sddc_t *sddc, const uint8_t *uid, const char *message, size_t len)
  2. {
  3. cJSON *root = cJSON_Parse(message);
  4. cJSON *Json_method;
  5. uint8_t uimethod =DDC_METHOD_VALIDE;
  6. Json_method = cJSON_GetObjectItem(root, "method");
  7. if (NULL == Json_method) {
  8. return SDDC_FALSE;
  9. }
  10. if (cJSON_IsString(Json_method)) {
  11. if (strcmp(Json_method->valuestring,"set") == 0) {
  12. uimethod = DDC_METHOD_SET;
  13. } else if (strcmp(Json_method->valuestring,"get") == 0) {
  14. uimethod = DDC_METHOD_GET;
  15. } else {
  16. return SDDC_FALSE;
  17. }
  18. } else {
  19. return SDDC_FALSE;
  20. }
  21. if (uimethod == DDC_METHOD_VALIDE) {
  22. return SDDC_FALSE;
  23. }
  24. if (uimethod == DDC_METHOD_SET) {
  25. int i;
  26. /*
  27. * 数字量、显示量先查询设置,防止开关量是设备的使能
  28. */
  29. for (i=0; i<G_config->num_dev_reg_num; i++) {
  30. object_Number_Set(root, G_config->num_dev_reg->objname, G_config->num_dev_reg->Num_Fun);
  31. }
  32. for (i=0; i<G_config->dis_dev_num; i++) {
  33. object_Display_Set(root, G_config->dis_dev_reg->objname, G_config->dis_dev_reg->Dis_Fun);
  34. }
  35. for (i=0; i<G_config->io_dev_reg_num; i++) {
  36. object_IO_Set(root, G_config->io_dev_reg->objname, G_config->io_dev_reg->IO_Fun);
  37. }
  38. } else if (uimethod == DDC_METHOD_GET) {
  39. object_get(sddc, uid, root, G_config->state_get_reg, G_config->state_get_reg_num);
  40. } else {
  41. return SDDC_FALSE;
  42. }
  43. return SDDC_TRUE;
  44. }

关于 set 方式,三种不同类型的属性有不同的实现,以下方 Num 类型的处理为例,如果数据类型不是 Num 就不会处理,相反就会调用注册的具体实现,注册相关可以参考这篇文章 基于sddc 协议的SDK框架 sddc_sdk_lib 解析

  1. static sddc_bool_t object_Number_Set(cJSON *input, const char *object_name, Num_Set do_fun)
  2. {
  3. cJSON *Json_object;
  4. Json_object = cJSON_GetObjectItem(input, object_name);
  5. if (NULL == Json_object) {
  6. return SDDC_FALSE;
  7. }
  8. if (cJSON_IsNumber(Json_object)) {
  9. return do_fun((uint64_t)Json_object->valuedouble);
  10. } else {
  11. return SDDC_FALSE;
  12. }
  13. return SDDC_TRUE;
  14. }

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

  1. static sddc_bool_t object_get(sddc_t *sddc, const uint8_t *uid, cJSON *input,
  2. DEV_STATE_GET *state_get_list, int list_len)
  3. {
  4. cJSON *Json_object;
  5. cJSON *root;
  6. cJSON *json_data;
  7. char *str;
  8. int i,j;
  9. Json_object = cJSON_GetObjectItem(input, "obj");
  10. if (NULL == Json_object) {
  11. return SDDC_FALSE;
  12. }
  13. if (!cJSON_IsArray(Json_object)) {
  14. return SDDC_FALSE;
  15. }
  16. root = cJSON_CreateObject();
  17. cJSON_AddStringToObject(root, "method", "report");
  18. json_data = cJSON_CreateObject();
  19. for (i=0; i<cJSON_GetArraySize(Json_object); i++) {
  20. for (j=0; j<list_len; j++) {
  21. if (strcmp(cJSON_GetArrayItem(Json_object, i)->valuestring, state_get_list[j].objname) == 0) {
  22. if (state_get_list[j].type == DEV_IO_TYPE) {
  23. if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {
  24. cJSON_AddStringToObject(json_data, state_get_list[j].objname, cstate_buf);
  25. }
  26. } else if (state_get_list[j].type == DEV_NUM_TYPE ){
  27. if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {
  28. cJSON_AddNumberToObject(json_data, state_get_list[j].objname, atof(cstate_buf));
  29. printf("atof(cstate_buf) = %lf\n", atof(cstate_buf));
  30. }
  31. } else if (state_get_list[j].type == DEV_DISPLAY_TYPE) {
  32. if (state_get_list[j].state_fun(cstate_buf, sizeof(cstate_buf)) == SDDC_TRUE) {
  33. cJSON *display_obj;
  34. display_obj = cJSON_AddObjectToObject(json_data, state_get_list[j].objname);
  35. cJSON_AddStringToObject(display_obj, state_get_list[j].objname, cstate_buf);
  36. }
  37. }
  38. }
  39. }
  40. }
  41. cJSON_AddItemToObject(root, "data", json_data);
  42. str = cJSON_Print(root);
  43. printf("object_get str = %s\n", str);
  44. if (!uid) {
  45. sddc_broadcast_message(sddc, str, strlen(str), 3, SDDC_FALSE, NULL);
  46. } else {
  47. sddc_send_message(sddc, uid, str, strlen(str), 3, SDDC_FALSE, NULL);
  48. }
  49. cJSON_free(str);
  50. cJSON_Delete(root);
  51. return SDDC_TRUE;
  52. }

总结

这就是基于 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. 第24篇-虚拟机对象操作指令之getfield

    getfield指令表示获取指定类的实例域,并将其值压入栈顶.其格式如下: getstatic indexbyte1 indexbyte2 无符号数indexbyte1和indexbyte2构建为(i ...

  2. python安装easyinstall/pip出错

    在Windows中装了python3.6,自然还要装pip.按度娘的提供的方法先下载easyinstall,然后在CMD下输入: python ez_setup.py 结果报错 ........... ...

  3. PHP的zip压缩工具扩展包学习

    总算到了 PHP 的拿手好戏上场了,前面我们学习过 Bzip2 . LZF . Phar 和 rar 这些压缩相关扩展在 PHP 中的使用,不过它们要么是太冷门,要么就是很多功能不支持.而 Zip 则 ...

  4. seo优化刷百度指数方法

    站长朋友们都听过"刷指数"这个概念,并且一直以来都有站长刷指数的现象.大家或为了提升网站数据,或为了满足排名的虚荣心,或为了与竞争对手抗衡,都或多或少研究过刷指数的原理和工具. 那 ...

  5. Docker系列(24)- 实战:DockerFile制作tomcat镜像

    实战:DockerFile制作tomcat镜像 step-1 准备镜像文件 tomcat压缩包,jdk压缩包! step-2 编写dockerfile文件,官方命名Dockerfile,build会自 ...

  6. Dapr + .NET Core实战(四)发布和订阅

    什么是发布-订阅 发布订阅是一种众所周知并被广泛使用的消息传送模式,常用在微服务架构的服务间通信,高并发削峰等情况.但是不同的消息中间件之间存在细微的差异,项目使用不同的产品需要实现不同的实现类,虽然 ...

  7. django把变量变成字段进行搜索

    from ceshi.models import Student     #引入model中的模型 获取前端请求的参数 searchKey=request.GET.get("key" ...

  8. display:flex;下的子元素width无效问题

    因为flex属性默认值为flex:0 1 auto;其中 1 为 flex中的 flex-shrink 属性. 该属性介绍: 一个数字,规定项目将相对于其他灵活的项目进行收缩的量. 根据上述介绍可以理 ...

  9. 『PyTorch』矩阵乘法总结

    1. 二维矩阵乘法 torch.mm() torch.mm(mat1, mat2, out=None),其中mat1(\(n\times m\)),mat2(\(m\times d\)),输出out的 ...

  10. 搞定 NodeJS 开发调试

    代码调试有时候是一种充满挑战的工作,如果有一个趁手的调试工具的话,往往可以做到事半功倍的效果.得益于这些年的快速发展,在 NodeJS 生态中已经有了多种调试工具可以使用.我们今年就来分享几个常用的调 ...