物联网通讯协议定义原则
Background #
物联网颇有一副崛起之势,但是底层硬件由于功能边界清晰,无涉及复杂业务等原因,很少人在协议抽象和统一标准上下功夫,特别是非标产品,常常陷入能用即可的窘境。
本文是我这一年与硬件打交道的总结。
一个标准成熟硬件的协议抽象 - 充电机 #
充电机标准上,把协议抽象成了四种
遥信
遥测
遥控
遥调
“遥” #
物联网之前,人们都是手动离线操作设备,不存在远程控制一说,当充电机的专家聚集在一起商讨远程控制时,强调了此三种抽象后的协议类型皆为”遥”字开头,说明两点:
- 中文的博大精深
- 协议就是给远程设备使用的,软件行业叫做API,application programming interface,接口。
遥信 #
充电设备的状态信号,充电机能够直接获取的信息,如继电器开关量,系统运行时间等。
遥测 #
充电机需要借助测量设备如电压,电流等。
遥控 #
下控功能
遥调 #
上行调试参数。
充电机协议overview #
这几种协议类型覆盖了充电机常见的使用场景,但是他存在几个问题:
- 遥信遥测的定义晦涩暧昧,边界不够清晰。某些数据,在不同的理解下,可以是遥信,也可以是遥测。
- 遥调的存在很尴尬,ta的边界更加模糊不清。
- 没有专门的故障信息区域用于拓展。
竟然公然表达国标定义不好?其实也不是,毕竟充电机的遥信,遥测,遥控,遥调满打满算,不会超过一千个,而且过去,硬件迭代和拓展速度相对较慢,限制了软件层面的迭代和拓展速度。即使定义的不好,不清晰,也无伤大雅。结果就是,只要协议能用,能满足需求,能跑通即可。
那么紧接着,我们要先澄清既然硬件迭代慢,硬件协议有必要做到高可读,高可扩展,高度抽象吗?
我的回答和立场很明确,ta们都是肯定的。待我展开讨论这两个必要性之后,再切入主题,”物联网底层通讯协议定义原则”
硬件协议有必要做到高可读,高可扩展,高度抽象吗? #
过去的场景
一个硬件设备,卖一次,测试验收一次,由于功能边界清晰,测试场景用例并不复杂多变,而且能够高度重复利用已有测试用例进行横展。
- 标准化产品,如串口服务器
- 非标产品,如产线PLC
- 集成产品,如油车
当下,局势有很大的变化
万物互联的时候,想用一套标准囊括所有需求是不切实际的,那么可扩展性的重要性就开始显现出来。一个硬件设备,卖一次,可能在上到市场之后,还持续的面临多次的迭代。
- 标准化产品的定制开发部分
- 非标产品,如定制网关等
- 集成产品,如电车,换电站
特斯拉,宝马,都持续推出订阅的硬件服务,远程升级提升硬件指标并不是什么新概念。
那么技术上,一套可读,可拓展,的硬件通讯协议,就能够给将来的新功能,新迭代铺路,保证迭代质量。
物联网通讯协议定义原则 #
既然是协议,我们完全可以套用几十年以来,前人总结出来的API设计原则,再加上一些由物联网产生出的特点,来规范我们定义协议的原则。
1. 抽象的原则 #
类似充电机的遥信,遥测,遥控,遥调,我们对硬件设备也需要有特别的抽象,能够将不同类别的信号统一沟通方式和操作流程。
这样做的好处是能够在代码实现上统一规则,有了标准的通讯规则和流程之后,每个人对协议的解读都能统一思想。
以下归类几种常见的抽象方式:
按读写抽象 #
适合对读写严格区分的设备,消防,安全类设备。
抽象方式可类似:
- 读系统状态
- 读系统信息
- 读故障信息
- 写控制信息
- 写系统参数
按上报周期抽象 #
适合纯监控系统,电表,温湿度,电池监控,车辆监控系统等。
抽象方式可类似:
- 高实时数据:发送频率10ms:电池瞬时电压,车辆实时状态
- 低实时数据:发送频率500ms:电池SOC
- 非实时数据:创建连接时发送一次后停止,或者可触发式查询:电池型号等静态变量
按功能抽象 #
具体情况具体分析,适用于功能复杂的设备。
拿机器狗举例:
- 表情控制
- 行走姿态控制
- 参数设置
- 基本状态上报
- 故障状态上报
2. 单一职责 #
每一帧报文只能做一件事。
优点:降低耦合度,报文复用能力强,变更影响小。(高可拓展性)
例:机器狗控制中,假设机器狗完成一个完整的翻跟斗动作,需要准备翻跟斗和翻跟斗两个动作。
❌错误示范
名称 | 描述 | 备注 |
---|---|---|
翻跟斗 | 1. 圈数; 2. 准备翻跟斗时间(ms) 3. 翻跟斗次数 | 执行全套翻跟斗 = 准备 + 翻跟斗,重复n次 |
✅正确示范
名称 | 描述 | 备注 |
---|---|---|
准备翻跟斗 | 准备时间(ms) | 准备翻跟斗 |
翻跟斗 | 无 | 执行翻跟斗 |
应该分别定义准备翻跟斗动作和翻跟斗两个动作接口。
因为,在将来,或许有一个假动作定义,需要假装准备翻跟斗,但是之后并不会真的执行翻跟斗,我们就能复用准备翻跟斗这个协议了。
拓展使用 - 执行翻跟斗假动作后卖乖
名称 | 描述 | 备注 |
---|---|---|
准备翻跟斗 | 准备时间(ms) | 准备翻跟斗 |
翻跟斗 | 无 | 执行翻跟斗 |
卖乖 | 卖乖时间(ms) | 0 |
使用方调用三次方法
for (n) {
* pre execute fangendou(100ms)
* execute fangendou()
}
* act cute(5000ms)
3. 迪米特法则(最少知识原则) #
每帧报文,不应出现超出职责范围的其它信息。
优点:业务边界明确。(高可读性,高抽象,降低耦合度)
例:机器狗的充电信息监控,不应包机器狗的姿态信号。
❌错误示范
名称 | 描述 | 备注 |
---|---|---|
上报充电信息 | 1. 充电时候的姿态;2. 电流;3. 电压 |
✅正确示范
名称 | 描述 | 备注 |
---|---|---|
上报充电信息 | 1. 电流;2. 电压 |
名称 | 描述 | 备注 |
---|---|---|
上报姿态信息 | 1. 身体姿态;2. 耳朵姿态;3. 四肢姿态 |
4. 接口(报文)隔离法则 #
在一个闭环的交互报文流程过程中,绝不允许耦合任何流程报文之外的其它报文。
优点:业务边界明确。(高可读性,高抽象)
例:控制机器狗的行走结果反馈应该是一个独立的反馈报文,而不是通过其他状态报文上报反馈。
❌错误示范
名称 | 描述 | 备注 |
---|---|---|
开始行走 | 行走距离 |
名称 | 描述 | 备注 |
---|---|---|
上报姿态信息 | 1. 身体姿态;2. 耳朵姿态;3. 四肢姿态 |
✅正确示范
名称 | 描述 | 结果 | 备注 |
---|---|---|---|
开始行走 | 行走距离 | 1. 执行结果(成功|失败);2.失败原因 |
5. 合并同类项原则 #
不允许一个报文内包含多种语义的类型。
优点:代码复用程度高,开发量低,减少出现bug的可能。(高可读性,高抽象)
例:控制机器狗的行走的控制报文,不应该有修改某个参数的报文。
6. 流程闭环原则 #
每一个下控报文,都要有闭环反馈。
优点:对控制报文统一标准,减少出现bug的可能。(高抽象)
例:控制机器狗行走的报文,无论控制结果如何,都应该有反馈。
7. 统一原则 #
- 系统层级,通讯方式,通讯主从关系宜统一。
- 系统层级,某一种特定格式的报文类型宜统一。比如所有时间格式统一,字符串大端小端,宜统一。
- 对于不同要求实时状态信息的报文周期,应有统一定义。
优点:对状态报文统一标准,减少出现bug的可能。(高抽象)
例:监控机器狗表情状态反馈报文每10ms反馈一次,监控机器狗体温反馈报文,每1s反馈一次。
8. 场景自治原则 #
不应生搬硬套其它场景的协议到不适用的另一个场景。每个场景有每个场景的特点。
优点:对同类报文高度抽象,对不同类报文进行解藕。(高可拓展性,高可读,高抽象)
例:根据实际场景对功能进行抽象,而不是全部强求统一。
9. 下位设备无超时原则 #
超时必然耦合业务,所有超时都应由上位机自行判断。
优点:解藕业务,减少出现bug的可能。