物联网系统中的网关内部程序应该如何设计?

道哥分享
关注

三、网关内部进程之间的通信

在设计一个应用程序的架构时,可以通过多线程来实现,也可以通过多进程来实现,每个人的习惯都不一样,各有各的好处。我们这里不去讨论孰优孰劣,因为我对多进程这样的设计思想比较偏爱,所以就直接按照多进程的程序架构来讨论。

3.1 网关中需要哪些进程

网关中需要执行的所有进程,是根据网关的功能来决定的,假设包括如下的功能:

(1)连接外网的进程 Proc_Bridge

网关需要连接到云端的服务器,需要一个进程与服务器之间保持长连接,这样就可以及时接收到服务器发来的控制指令,以及把系统内部数据及时上报给服务器。

这个进程需要把从服务器接收到的指令转发到网关系统内部,把从系统内部接收到的信息转发给服务器,类似于桥接的功能,因此命名为 Proc_Bridge。

(2)设备管理进程 Proc_DevMgr

这个进程用来执行设备管理功能,设备的添加(入网)、删除(退网),都由此进程来管理。

(3)协议转换进程 Proc_Protocol

下行:把应用层的统一通信协议,转换成不同类型无线通信协议,发送给相应的无线模块。

上行:把设备上报的、不同类型的无线通信协议,转换成应用层的统一通信协议。

(4)边沿计算进程(自动化控制) Proc_Auto

很明显,这需要一个独立的进程来处理各种计算,这个进程就相当于系统的大脑。

(5)无线通信协议相关的进程 Proc_ZigBee, Proc_RF, Proc_ZWave

在硬件上,每一种无线通信模块通过串口或其他硬件连接方式与到网关的 CPU 进行通信,因此,每一种无线通信模块都需要一个相应的进程来处理。

(6)其他“软设备”进程 Proc_Xxx

在之前的项目中,还遇到一些硬件设备,它们与门磁、插座等设备在逻辑上处于同一个层次,但是与网关之间是通过 TCP 来连接。对于这样的设备,也可以使用一个独立的进程来进行管理。

上面的这些进程,在网关中的运行模型如下:

3.2 MQTT消息总线

以上这些进程之间需要相互通信,不是简单的点对点通信,而是一个网状的通信模型。比如:

设备管理进程 Proc_DevMgr:当任何一种设备被添加到系统中时,都需要处进行处理,因此它需要与 Proc_ZigBee, Proc_RF, Proc_ZWave 这些进程进行通信;当某个设备上报数据时(例如:Proc_ZigBee),Proc_Protocol 进程需要把数据进行协议转换,然后 Proc_Bridge 进程把转换后的数据上报给服务器,同时 Proc_Auto 进程需要检查这个设备上报的数据是否触发了其他相关联的设备;

也就是说,这些进程中间的通信是相互交叉的,如果通过传统的 IPC 方式(共享内存、命名管道、消息队列、Socket)等,处理起来比较复杂。

引入了 MQTT 消息总线之后,每个进程只需要挂载到总线上。每个进程只需要监听自己感兴趣的 topic,就可以接收到相应的数据。

既然这些进程之间的通信关系比较复杂,那么一个良好的 topic 设计规范就显得很重要了!

3.3 Topic 的设计

MQTT 的通信模型是基于订阅/发布的模式,一个客户端(进程)接入到消息总线之后,需要注册自己感兴趣的 主题 topic,其他客户端(进程)往这个 topic 发送消息,即可被订阅者接收到。

主题 topic 是一个以反斜线(/)分割的字符串,用来表示多层的分级结构,例如下面的这 2 个 topic,是亚马逊 AWS 平台中在线升级(OTA)相关的 topic:

$aws/things/MyThing/jobs/get/accepted$aws/things/MyThing/jobs/get/rejected

在我们的示例场景中,可以按照下面这样来设计主题 topic:

(1) Proc_DevMgr

订阅主题:

$iot/v1/ZigBee/Register  $iot/v1/ZigBee/UnRegister  $iot/v1/RF/Register  $iot/v1/RF/UnRegister  $iot/v1/ZWave/Register  $iot/v1/ZWave/UnRegister

(2) Proc_Bridge

订阅主题:

$iot/v1/Device/Report

发出数据的主题:

$iot/v1/Device/Control  $iot/v1/Device/Remove  $iot/v1/Auto/AddRule  $iot/v1/Auto/RemoveRule

(3) Proc_Protocol

订阅主题:

$iot/v1/Device/Control  $iot/v1/Device/Remove  $iot/v1/ZigBee/Report  $iot/v1/RF/Report  $iot/v1/ZWave/Report  

发送数据主题:

$iot/v1/Device/Report  $iot/v1/ZigBee/Control  $iot/v1/ZigBee/Remove  $iot/v1/RF/Control  $iot/v1/RF/Remove  $iot/v1/ZWave/Control  $iot/v1/ZWave/Remove  

(4) Proc_Auto

订阅主题:

$iot/v1/Auto/AddRule  $iot/v1/Auto/RemoveRule  $iot/v1/Device/Report  

发送数据主题:

$iot/v1/Device/Control

(5) Proc_ZigBee

订阅主题:

$iot/v1/ZigBee/Control  $iot/v1/ZigBee/Remove  

发送数据主题:

$iot/v1/ZigBee/Register  $iot/v1/ZigBee/UnRegister  $iot/v1/ZigBee/Report  

(6) Proc_RF

订阅主题:

$iot/v1/RF/Control  $iot/v1/RF/Remove

发送数据主题:

$iot/v1/RF/Register  $iot/v1/RF/UnRegister  $iot/v1/RF/Report  

(7) Proc_ZWave

订阅主题:

$iot/v1/ZWave/Control  $iot/v1/ZWave/Remove  

发送数据主题:

$iot/v1/ZWave/Register  $iot/v1/ZWave/UnRegister  $iot/v1/ZWave/Report  

以上这些主题 topic 的设计,还是有些粗略的。如果借助通配符(#, +, $),可以设计出更灵活的层次结构。

多层通配符: “#”是用于匹配主题中任意层级的通配符,多层通配符表示它的父级和任意数量的子层级。单层通配符:“+”加号是只能用于单个主题层级匹配的通配符,在主题过滤器的任意层级都可以使用单层通配符,包括第一个和最后一个层级。通配符:“$”表示匹配一个字符,只要不是放在主题的最开头,其它情况下都表示匹配一个字符。

我们以一个控制指令为例,来梳理一下数据是如何通过 topic 进行流动:

Proc_Bridge 进程从服务器接收到控制指令后,发送到消息总线上的 topic: $iot/v1/Device/Control。由于 Proc_Protocol 进程订阅了这个 topic,所以立刻接收到指令。Proc_Protocol 分析指令内容,发现是一个 ZigBee 设备,于是进行协议转换,发送一个 ZigBee 控制指令到消息总线上的 topic: $iot/v1/ZigBee/Control。由于 Proc_ZigBee 进程订阅了这个 topic,因此它接收到这个控制指令。Proc_ZigBee 把控制指令转换成 ZigBee 无线通信模块要求的格式,通过硬件发送给设备灯泡。

我们再分析一下设备数据上报的场景:

先关注图中红色箭头,忽略蓝色箭头:

门磁打开后,通过无线通信把信息上报给进程 Proc_CF。Proc_RF 进程接收到 RF433 通信模块上报的数据,把“门磁打开”这个信息发送到消息总线上的 topic:$iot/v1/RF/Report。由于 Proc_Protocol 进程订阅了这个 topic,因此接收到上报的门磁数据。Proc_Protocol 分析数据,把 RF433 协议的数据转成统一的应用层协议的数据,发送到消息总线上的 topic:$iot/v1/Device/Report。由于 Proc_Bridge 进程订阅了这个 topic,因此就接收到了这次上报的数据。Proc_Bridge 进程把数据上报给服务器。

再来看一下蓝色箭头流程:

在上面的第 4 步:Proc_Protocol 进程把 RF433 协议数据转成应用层统一协议之后,把数据发送到消息总线上的 topic:$iot/v1/Device/Report 之后,Proc_Auto 进程同时进行如下操作:

由于 Proc_Auto 也订阅了这个 topic,因此它也接收到了门磁上报的这个应用层协议的数据。Proc_Auto 查找自己的配置信息(假设用户已经提前配置好了一条规则:当门磁打开的时候,就触发声光报警器),发现匹配到了“门磁->报警器”这条规则,于是发出一条控制报警器的指令,发送到消息总线上的 topic: $iot/v1/Device/Control。

后面的 7,8,9,10 这四个步骤就与上面的控制指令流程完全一样了。

3.4 与 DBUS 总线的对比

从上面描述的 3 个数据流向的场景中,是不是感觉到使用 topic 为“数据管道”的这种通信方式,与 Linux 系统中的 DBUS 总线特别的相似?

DBUS 总线也是用于进程之间的通信,按照我个人的理解,DBUS中其实是把进程之间的两种通信组织在一起了:

基于信号的数据传输;基于方法的 RPC 远程调用;

DBUS 总线包含的概念更复杂一些,包括:路径、对象、接口、方法等等,这些概念组织在一起共同定位到一个具体的服务提供者了。

相比较而言,我感觉 MQTT 这样的方式更简洁一些。

所谓的 RPC 远程调用,就是调用位于远程机器上的一个函数,主要解决两个问题:

网络连接;数据的序列化和反序列化;

后面我会专门写一篇文章,利用 protobuf 框架来实现 RPC 调用。

四、网关与云平台之间的通信

上面讲解的设计过程,是网关内部的各功能模块之间通信方式,这也是我们作为嵌入式开发者能充分发挥的部分。

网关与云平台之间的通信方式一般都是客户指定的,就那么几种(阿里云、华为云、腾讯云、亚马逊AWS平台)。一般都要求网关与云平台之间处于长连接的状态,这样云端的各种指令就可以随时发送到网关。

当然了,这些云平台都会提供相应的 SDK 开发包,一般使用的 MQTT 协议来连接云平台更多一些。在一些文档中,会把位于云端的 MQTT 服务器称作 Broker,其实就是一个服务器。

进程 Proc_Bridge 的功能主要有 2 点:

与云平台的数据传输通道;协议转换:把云平台相关的协议转换成网关内部的协议,以及相反的转换。

也就是说:Proc_Bridge 进程需要同时连接到云平台的 MQTT Broker 和网关内部的 MQTT 消息总线。在下一篇文章中,我们来专门讲解这部分的内容,并提供一个实现桥接功能的代码模板。

五、总结

作为一名嵌入式软件开发人员,仅仅根据别人设计好的框架来填充代码,时间久了就会有些倦怠,不知道技术提升的方向在哪里。仔细想想,其实方向挺多的:Linux 内核、文件系统、算法、应用程序设计等等。

这篇文章讨论的内容还谈不上架构设计,仅仅是一个简单的物联网网关内部各功能模块的通信模型。如果你有机会设计类似的产品,不妨尝试一下这样的通信模型,当然你一定会设计的更好!


声明: 本文由入驻OFweek维科号的作者撰写,观点仅代表作者本人,不代表OFweek立场。如有侵权或其他问题,请联系举报。
侵权投诉

下载OFweek,一手掌握高科技全行业资讯

还不是OFweek会员,马上注册
打开app,查看更多精彩资讯 >
  • 长按识别二维码
  • 进入OFweek阅读全文
长按图片进行保存