当前位置: 首页 > news >正文

IoT/HCIP实验-1/物联网开发平台实验Part2(HCIP-IoT实验手册版)

文章目录

  • 概述
  • 产品和设备
    • 实例的产品和设备
    • 产品和设备的关联
    • 单个产品有多个设备
      • 为产品创建多个设备
      • 产品模型和物模型
      • 设备影子(远程代理)
  • 新建产品
  • 模型定义
  • 编解码插件开发
    • 编解码插件工作原理
    • 消息类型与二进制码流
    • 添加消息(数据上报消息)
    • 添加消息(命令下发消息)
    • 关联消息和服务
    • 插件部署
    • 字段标记为地址域
    • 字段的偏移值
    • 标记为响应标识/mid
  • 在线调试
    • 新旧版本调试界面
    • QA 设备发送成功,应用接收失败
  • 小结

概述

本文基于《HCIP-IoT Developer V2.5 实验手册》实验1 —— 物联网开发平台实验展开,按部就班其中的实验过程,重点描述编解码插件开发过程、概念理解、开发注意事项等,分析遇到的问题并提出解决方案,记录并扩展基础知识要点。HCIP-IOT物联网开发平台实验:
本实验通过在华为云物联网平台上创建产品,进行案例的功能定义和编解码插件开发,掌握物联网平台的操作流程,以及如何验证编解码插件是否正确。本实验完成后,你将掌握物联网平台的功能定义,掌握物联网平台编解码插件的开发,掌握物联网平台的调试。

@History
刚开始我想硬着头皮,依据《HCIP-IoT Developer V2.5 实验手册》按部就班,可当前实际IoTDA平台页面,有小部分与指导书中的描述并不匹配,或者指导书中的描述步骤过于跳跃,新手理解不了。在阅读本文前,请先读《IoT/HCIP实验-1/物联网开发平台实验Part1(快速入门,MQTT.fx对接IoTDA)》相关内容。 之后再接着来啃实验手册中的的内容。

产品和设备

如下图,在华为IoTDA平台中,其明确定义了:产品是设备的集合,是指某一类具有相同能力或特征的设备的合集。
在这里插入图片描述
有的地方将产品比作是模板,将设备比作实例,我觉着这种说法有些片面,但此时还不能具体说上来。然后又过了两天…
@Note:本章节与《物联网开发平台实验Part1》关联比较大,是在其实验基础上的进一步的探究实验和知识分析总结。

实例的产品和设备

如下图,总览是实例的总览,其Web页面显示实例的基本信息,
在这里插入图片描述

如下图,产品是实例的产品列表,
在这里插入图片描述

如下图,设备是实例的设备列表,
在这里插入图片描述

也就是说,产品和实例在一定程度上是并列在IoTDA实例之下的。那么我们再来回顾下什么是实例?

在华为云物联网平台中,IoTDA实例 指的是云服务提供的虚拟化环境或资源单元,用于支持具体的应用与服务。根据不同的需求,华为云物联网平台提供了两种实例类型:标准版(标准实例)和企业版(专享实例)。实例的含义非常类似于Docker的容器,具体可从如下三个方面来理解:
虚拟化资源:
实例是基于云计算技术虚拟化的资源单元,它可以包含计算、存储和网络等资源。每个实例可以运行特定的应用程序或服务,支持不同的业务场景。
隔离性:
实例提供了资源的隔离,确保不同用户或不同应用之间的数据和资源不会互相干扰。这是云平台的重要特性,能够提升安全性和稳定性。
可扩展性:
实例可以根据用户的需求进行扩展和缩减,以适应不同的工作负载。用户可以灵活地选择所需的资源配置,来提高系统的性能或降低成本。

产品和设备的关联

关联环节1-在设备注册界面要指定其所属的产品
在这里插入图片描述
关联环节2-Topic主题消息的数据体中,含有产品模型的服务ID和属性字段名称,
在这里插入图片描述
通过上述分析我们可以推论出来的一个结论是,一个设备只能属于一个产品。设备管理,是有分组功能的,但也不可使得一个设备服务于多个产品。
在这里插入图片描述

单个产品有多个设备

本小节我关注的终极问题是:一个产品下的多个设备,总不能强硬要求他们是相同的设备类型吧?

为产品创建多个设备

一个产品映射多个设备的情形在 <物联网开发平台实验> 中并没有被提及,这里我们实操验证下。很给力的是,MQTT.fx 可以多进程实例启动,这就方便多了。如下图,我们在设备 <IoT路灯1> 的基础上,新增设备路灯2 和 智慧电线杆100,哈哈,假设智慧电线杆上装设有风速和湿度传感器,并能上传华为云IoTDA服务。
在这里插入图片描述
这里多提及一个事情,上述三个设备都是创建在同一个产品“智慧路灯-冶源”之下的,而该产品的协议类型是MQTT,数据格式是JSON,故所有产品继承了协议类型是MQTT这一属性,因此三个设备都可以使用MQTT.fx来进行模拟。通过使用Beyond Compare工具对比上述三个设备的MQTT参数可知,它们的MQTT Broker Profile Setting 是相同的,
在这里插入图片描述
三个MQTT.fx设备配置各自的MQTT参数后,均可以成功连接到IoTDA设备接入服务。我们在三个设备中都执行亮度数据上报,
在这里插入图片描述
在Web应用模拟器中查看,均可以正确接收到正确的数据上报结果。

产品模型和物模型

这里将从一个新的角度来审视产品模型的定义。定义产品模型的过程,实质上就是添加服务的过程,而每个服务都已包含一个属性列表和一个命令列表。我们在物联网开发平台实验Part1的基础上,添加新服务 EnvData,代表电线杆上的环境传感器,
在这里插入图片描述
给产品添加上述服务及其属性字段后,我们重新查看设备详细信息,可以看到该产品下的所有设备都具有了新添加的内容。也就是说在当前的IoTDA平台下,即使某产品是由千差万别的物理设备作用而成,在华为IoT管理平台中,它们都要具有相同的产品模型。虽然每个设备具有全量的产品模型,但并不意味着每个设备都要上报全部种类的采集数据,这用脚指头想也不可能。
怎么说呢?
前文是站在产品模型的角度来说的,我们再站到设备角度来看看,也许就明白了,还是以IoT路灯2为例,
在这里插入图片描述
如上图所示,我们的IoT路灯2设备,完全可以只是/或者只能上报WindSpeed数据,而永远不会上报Temperature数据。即一个具体的设备可以只是关联部分产品模型服务或字段。

综上所述,产品模型包含了所有设备的、综合起来的、全部的数据上报功能和被控制功能,是一个彻头彻尾的功能全集。而每个设备可以只是实现其中的一个或几个功能,不同设备支持的功能类型可以是相同的,也可以是完全不同的。

设备影子(远程代理)

影子设备的概念,看到过好几次了,它是什么意思呢?我们以实验Part1中设备<IoT路灯2>为例。
1、在设备详情->设备信息选项卡->物模型数据->点击查看全部属性
2、在设备详情->直接点击设备影子
在这里插入图片描述
物联网平台支持创建设备的“影子”。设备影子是一个JSON文件,用于存储设备的在线状态、设备最近一次上报的设备属性值、应用服务器期望下发的配置。每个设备有且只有一个设备影子,设备可以获取和设置设备影子以此来同步设备属性值,这个同步可以是影子同步给设备,也可以是设备同步给影子。

应用场景
适合资源受限低功耗设备,长期处于休眠状态的场景。设备影子的具体作用如下,
1、查询设备最新上报数据和设备最新在线状态:
当在控制台上查询设备上报数据时,由于设备可能长时间处于离线状态或因网络不稳定掉线,而无法获取到最新数据。通过设备影子机制,设备影子中始终保持设备最新上报的数据和设备当前状态,控制台上只需要查询设备影子中存储的数据,即可获取设备最新上报的数据和设备状态。
很多应用服务器频繁地查询设备在线状态,由于设备处理能力有限,频繁查询会损耗设备性能。使用设备影子机制,设备只需要主动同步状态给设备影子一次,多个应用程序请求设备影子获取设备状态,即可获取设备最新状态,从而将应用程序和设备解耦。
2、修改设备属性值:
用户通过“设备 > 设备详情 > 设备影子”修改设备的属性值。由于设备可能长时间处于离线状态,修改设备属性值的操作不能及时下发给设备。在这种情况下,物联网平台可以将修改设备的属性信息存储在设备影子中,待设备上线后,将修改的设备属性值同步给设备,从而完成设备属性值的修改。

按照我个人的理解,设备影子类似于是一种远程代理架构或机制。设备影子充当了设备的状态代理,实时反映设备的在线状态和最近一次上报的属性值。设备影子支持异步通信。设备影子机制实现了应用程序与设备之间的解耦。设备影子提供了一个集中管理设备状态和属性的方式。种设计理念不仅提升了物联网系统的性能,还增强了用户对设备管理的控制力。

新建产品

目前IoTDA服务所提供的创建产品的界面,与指导书中的截图已经不太一样,但也没有什么不好理解的,顺着填写就行。
其中,
设备类型选择,标准类型(包含所属行业:智慧农业、所属子行业:农业农机、设备类型:农业农机)
高级设置中,定义了产品ID为:dahequ_aa77bb,
在这里插入图片描述
产品创建后,
在这里插入图片描述
我们可以直接点击上图中的查看详情,或者点击确定后,在在产品列表中点击产品名称或操作中的详情进入如下页面,
在这里插入图片描述

模型定义

在实验指导书(2019发布)中,模型定义卡是单独占据一个Tab子界面的,现在模型定义配置界面在基本信息这个Tab之下,插件开发和在线调试还是单独占据一个TabPage页。按照指导书的描述,实验是使用自定义模型。

如上图,点击"模型定义"配置卡中的"自定义模型"按钮,立即就弹出来服务创建界面,按指导书完成后,模型定义卡显示如下,
在这里插入图片描述
左侧是服务类表(Agriculture是我们刚才添加的服务),每个服务包含一个属性集合和一个命令集合。页面格式也与指导书中描述有差异。接下来的,为Agriculture服务添加属性和命令的操作很简答,这里不再赘述,参考指导书即可。
在这里插入图片描述
最终定义好的模型如上图。在指导书中,还有一种可行的访问方式,在当前版本的平台下实际没有看到,可能是合并到可写啦。

编解码插件开发

在这里插入图片描述

编解码插件工作原理

为了更好的理解编解码插件的工作原理,我们从不同的文档中获取了其原理图。

来自培训手册,
在这里插入图片描述

来自产品文档,
在这里插入图片描述

来自图形化开发界面的新手指引,
在这里插入图片描述
在一开始的学习过程中我并没有注意到。在图形化开发界面的右上角,有个新手指引连接,有详细的图形化开发过程。其中的工作原理说明部分是gif动态图,比指导书详细生动多了。
请添加图片描述
通过如上各个编解码插件工作原理图,我们对诸如,消息、服务、Profile、产品模型等概念的理解,将会逐渐清晰起来。

消息类型与二进制码流

在这里插入图片描述
在编辑吗插件的定义中,消息类型分为两种,数据上报类型和命令下发类型。无论是那种消息类型都可指定响应。

我们以命令下发型消息 Agriculture_Control_Light 为例,分析该消息及其响应消息的字段和取值表,
在这里插入图片描述
如上,可能对应于如下C/C++结构数据定义,

typedef unsigned   char    u8_t;    /* Unsigned 8 bit quantity         */
typedef signed     char    s8_t;    /* Signed    8 bit quantity        */
typedef unsigned   short   u16_t;   /* Unsigned 16 bit quantity        */
typedef signed     short   s16_t;   /* Signed   16 bit quantity        */
typedef unsigned   long    u32_t;   /* Unsigned 32 bit quantity        */
typedef signed     long    s32_t;   /* Signed   32 bit quantity        */struct Agriculture_Control_Light /*report*/ {u8_t  messageid;    //==0x01u16_t mid;          //0x0001s8_t  Light[3];     //string# 4F4E==ON; 4F4646==OFF
}; //5bytesstruct Agriculture_Control_Light_ACK /*report*/ {u8_t  messageid;    //==0x02u16_t mid;          //0x0001u8_t  errcode;      //0==OKu8_t  Light_State;  //0==ON; 1==OFF
}; //5bytes

添加消息(数据上报消息)

当前IoTDA平台与指导书中的描述一致,据指导书按部就班即可。
在这里插入图片描述
如上图,我们添加了一个名字为Agriculture的消息,这个消息有4个字段。这里我们把Agriculture理解为一个结构体就形象化多了。实际上前文将编解码插件原理的时候也说过,所谓消息本质就是用于与设备端交互的二进制码流。另外在实验1中,我们并没有定义编解码插件,也能使得设备与平台通信,因为彼时我们发送的是主题数据/MQTT格式的JSON数据,而不是二进制码流,我们的设备使用的是MQTT协议不是CoAP等协议。

接下来,产品模型属性与消息字段的绑定,后面的小节会继续讲解。

添加消息(命令下发消息)

按照指导书的步骤,完成“命令下发”类消息的构建,其中,Light命令消息,结果如下,
在这里插入图片描述
同理,我们添加Motor命令消息。在其添加过程中,我们遇到如下问题,
指导书中,Motor命令消息,其命令字段messageid0x1,响应字段messageid0x2。但在目前的平台中进行操作时,其默认值分别是0x3和0x4,似乎是与Light命令消息进行排序的。为了验证这个想法,我么再尝试新加一个命令消息,
在这里插入图片描述
实验结果符合预期,1-2-3-4-5-6,即消息类型同为“命令下发”的3对消息,其messageid是默认递增编号的。

关联消息和服务

如上两节我们按照实验手册 <智慧农业案例插件设计思路> 中的消息列表及消息字段表,定义好全部的数据上报消息和命令下发消息和响应消息后,便可以使用IoTDA的图形化开发模式,以拖拽的方式建立(设备)消息与平台产品模型(服务)之间的关系。编解码插件负责二进制码流和JSON之间的数据格式转换。
在这里插入图片描述

插件部署

这很简单,但很重要,不要漏下啦哈。
另外与部署按钮仅挨着的保存按钮,也要时不时的去点击下,以防止web页意外关闭,导致丢失。

字段标记为地址域

messageID字段没有与之绑定的属性,因为它被标记为了地址域,那么什么是地址域呢?
在这里插入图片描述
如果你参与定义过软件中的一些消息协议,这也很好理解,这里的地址域类似于我们常用的消息CTF(消息类别、消息类型、消息功能)标识(字段),该标识表明了接收方该如何解析后续数据字段或数据流块。“标记为地址域” 是指将特定字段定义为消息类型的标识符,其字段名称强制固定为messageId,其本质是消息协议中的元数据标识。

在华为 IoTDA 平台的插件开发中,messageId 属于平台预定义的协议关键字,其命名权仅在标记地址域时开放,其他场景禁止使用。比如,若你把温度字段错误命名为messageId,编解码插件会误将温度值识别为消息类型标识,引发数据解析错误。其主要有两层含义:

1、协议层定位
地址域是编解码协议中用于区分消息类型的专用字段,通常作为二进制消息流的首个字段。例如:
0x00表示数据上报消息;0x01表示命令响应消息;0x02表示…
该字段不携带业务数据,仅用于标识后续数据的解析规则
2、字段属性强制约束
当某字段被标记为地址域时:
字段名称必须为 messageId:系统强制约定,不可修改。
位置必须固定:在消息结构中的第一个字段位置。
数据类型限制:通常为整型或枚举值,对应二进制码流的解析规则。

结合前文的实验过程,可看出来:
1、同为数据下发类型消息的全部消息定义及其响应消息的定义,其messageid是统一顺序编号的,是不能重复的,在创建卡中也有输入筛选过滤器存在,不允许你那么搞。
2、数据上报消息的messageid和命令下发类消息的messageid编号,没有任何关系。这里,结合Topic定义和使用也很好理解,这两种消息都是定义在系统Topic中的,它们的topic名中分别含有report和command字样。

字段的偏移值

在这里插入图片描述
如果每个非地址域字段都是固定的数据类型值,则将上述插件定义中的消息及其字段与编程语言中的结构体概念映射起来,看起来没有什么任何的违和感。但同时要注意上图紫框中Tip提示,如果字段是可变长度的,则以实际偏移值为准。前文实验中有使用string类型,但是都指定了字符字节长度。

标记为响应标识/mid

类型为"数据上报"的消息,没有mid字段的定义。虽然数据上报类消息也支持设置响应字段,
在这里插入图片描述
如上图,数据上报类型消息的响应字段是一个32bit的状态码,支持自定义。

通过实验可知,在IoTDA下命令和命令的响应是两种消息,它们的消息标识messageid通常分别为n和n+1,这不难理解。但是关于Mid,我刚开始没有理解的地方是:命令下发消息及其响应消息中均要设置响应标识字段,mid字段到底是要干啥?
在华为云 IoTDA 的命令下发机制中,mid(Message Identifier)字段是消息的唯一追踪标识符,其核心作用是实现命令请求与响应的精准匹配,并解决异步通信场景下的消息关联难题。mid字段的核心作用,可从如下3点来理解,

  1. 命令与响应的逐一对应
    场景:当平台下发命令后,设备可能因网络延迟或处理耗时无法立即响应,甚至可能因重试机制收到重复命令。
    机制:平台为每条命令生成全局唯一的mid,设备响应时必须携带相同的mid。mid可以很复杂,只要唯一就行,示例如下,
// 平台下发命令(带mid)
{"messageId": "SET_TEMPERATURE","mid": "20231108001","params": {"temp": 25}
}// 设备响应(返回相同mid)
{"messageId": "SET_TEMPERATURE_RESPONSE","mid": "20231108001","result": 0  // 0表示成功
}
  1. 防止重复处理与消息去重
    问题:网络抖动可能导致设备收到重复命令(如 MQTT QoS 1/2 机制的重发)。
    解决方案:设备端通过mid判断是否已处理过该命令,若发现重复mid则直接返回缓存响应,避免重复执行。
  2. 状态追踪与调试
    运维价值:通过mid可快速在平台日志中定位某条命令的全生命周期(下发、传输、响应、超时)。

在线调试

本章节将重点关注在测试过程中遇到的问题,欢迎留言问题一起讨论和学习。

新旧版本调试界面

在这里插入图片描述
如上是平台当下的新版的调试界面,通过右上角的“回到旧版”按钮,可以切换到如指导书中描述的截图模样的调试界面。

QA 设备发送成功,应用接收失败

现象:
在设备模拟中输入 属性内容 00193C0064,并执行发送,结果设备测显示发送成功,但是应用测却没有收到信息。

分析
平台消息追踪信息显示如下异常,
在这里插入图片描述
按照日志时间戳,从下到上的消息先后顺序。
1、我们的编解码器/codec并没有找到。制造商识别码xxx,应用标识xxx,设备标识xxx. 此错误表明 IoTDA 平台在尝试解析设备上报数据时,未找到与设备产品模型匹配的编解码插件。
2、上行数据被IoTDA平台正确接收
3、上报数据中的服务ID,是为被定义的…

我们重新审视下 00193C0064这个二进制数据。在实验指导书中,它代表的是数据上报消息,

typedef unsigned   char    u8_t;    /* Unsigned 8 bit quantity         */
typedef signed     char    s8_t;    /* Signed    8 bit quantity        */
typedef unsigned   short   u16_t;   /* Unsigned 16 bit quantity        */
typedef signed     short   s16_t;   /* Signed   16 bit quantity        */
typedef unsigned   long    u32_t;   /* Unsigned 32 bit quantity        */
typedef signed     long    s32_t;   /* Signed   32 bit quantity        */struct Agriculture /*report*/ {u8_t  messageid;u8_t  Temperature;u8_t  Humidity;u16_t Luminance;
}; //5bytes

期望的结果是 { Temperature=25,Humidity=60,Luminance=100 },多字节以大端字节序计。关于字节序的问题,可以参考《存储与传输/大小端字节序的概念、决定因素、给编程带来的困扰》等文章。
按照大端字节序,即人类对字节的阅读顺序,上述结构体的对象展开为,
messageid =0x00,Temperature=0x19,Humidity=0x3C,Luminance=0x0064

有了上述分析,再结合平台消息追踪提示+编解码插件动态工作原理展示,我去排查了我的Agriculture服务定义中的属性列表,
在这里插入图片描述
如上,服务属性中,温度和湿度字段的数据类型int(整型)与对应消息字段的数据类型int8u并不一致。我本来很肯定这就是问题所在了,结果很快打脸。因为在编解码插件服务属性配置中,压根就没有int8u这个数据类型,关于整其只有int和long类型。我没有理解这个事情,但是我确定,IoTDA处理了这种差异,是允许上述数据类型不严格匹配的。

原因确定
哈哈,我又要抽自己了。我再定义完成全部消息定义时,就着急忙活的来进行调试了。却落下了至关重要的一个步骤,部署插件。

解决方案
在插件编辑卡的右上角,点击部署后,重新测试,

(旧版调试界面)效果如下,
在这里插入图片描述
(新版调试界面)效果如下,
在这里插入图片描述
这里有个事情要注意下,可能是网络延迟的关系。一小段时间(1min左右)内,我在服务配置、消息配置、部署操作完全正确的情况下,模拟进行设备数据的上报操作时,在调试打印串口,显示的设备上报成功,应用模拟器窗口却没有显示收到的json数据,但是过会,再次进行尝试时,问题就不存在了。

小结

@Note 关于指导书的使用
现在我们完成了实验指导书中的实验1,我们发现,指导书中的小部分描述和截图,确实已经不再完全切合当前IoTDA的功能或功能展示界面。我们要接受和容忍这种差异性,不要因此影响学习心情。以本实验为例,其实我们重点关注智慧农业案例插件设计思路即可,包含编解码插件工作原理、消息定义列表,具体消息字段定义列表等。

http://www.xdnf.cn/news/675613.html

相关文章:

  • 数字人教师:开启教育智慧革新之旅
  • Unity数字人开发笔记
  • YOLOv4:目标检测的新标杆
  • 流量红利的破局之道—深度解析OPPO应用商店 CPD广告运营
  • 自动驾驶规划控制算法教程:从理论到实践
  • 《计算机组成原理》第 10 章 - 控制单元的设计
  • CST基础八-TOOLS工具栏(一)
  • 如何将 PDF 文件中的文本提取为 YAML(教程)
  • 自动化测试入门:解锁高效软件测试的密码
  • 59、【OS】【Nuttx】编码规范解读(七)
  • 【Python中的self】Python中的`self`:从基础到进阶的实战指南
  • roo code调用手搓mcp server
  • Python filter()函数详解:数据筛选的精密过滤器
  • 在promise中,多个then如何传值
  • sqli_labs第二十九/三十/三十一关——hpp注入
  • 《计算机组成原理》第 6 章 - 计算机的运算方法
  • 大模型的参数高效微调;大模型的对齐
  • Linux显示进程状态——ps命令详解与实战
  • 用C#最小二乘法拟合圆形,计算圆心和半径
  • chrome打不开axure设计的软件产品原型问题解决办法
  • 尚硅谷redis7 41-46 redis持久化之AOF异常恢复演示
  • 从零开始理解机器学习:知识体系 + 核心术语详解
  • 从中控屏看HMI设计的安全与美学博弈
  • FileZillaServer(1) -- 记录
  • Git 克隆别人的远程仓库以后,推到自己的远程仓库
  • BSRN地表基准辐射网数据批量下载
  • SQL基础教程:第一章与第二章内容总结(新手入门指南)
  • 文档注释:删还是不删
  • 关于 smali:3. Smali 与 APK 结构理解
  • LWIP 中,lwip_shutdown 和 lwip_close 区别