设备影子与物模型

设备影子与物模型

物模型

物模型(Thing Specification Language)是为产品定义的数据模型,用于描述产品的功能。物模型将设备在云端从属性、服务和事件三个维度,分别描述了该实体是什么、能做什么、可以对外提供哪些信息。定义了物模型的这三个维度,即完成了产品功能的定义。

设备影子

设备影子(Device Shadow)是指在物联网设备与云端之间建立的一种虚拟设备模型,它保存了物联网设备的最新状态和控制信息,并提供了远程访问这些信息的能力。
简单来说,设备影子保存了设备即时状态。应用程序可对影子状态进行获取和设置。设备在线时,可直接获取到平台下发的设置指令;设备离线后,再次上线时可以主动查询影子进行配置。

主要目的:

  • 缓存设备状态:应用程序可以方便地查看设备状态,即使设备下线。
  • 离线操作:设备影子允许应用程序和其他设备对设备状态进行操作,即使设备处于离线状态,等设备上线时主动获取期望状态进行更新。
  • 解耦:设备影子充当了设备和应用程序之间的缓冲层,使得应用程序无需直接与设备进行通信,从而降低了设备和应用程序之间的耦合度。

控制模型

控制模型

数据结构

数据以 JSON 文档形式存储,主要包含以下属性:

  • state
    • desired 设备预期状态
      应用程序设置的设备预期状态,可用于远程控制设备。
    • reported 设备报告状态
      记录了设备当前的属性和状态信息。应用程序读取文档的该部分以确定设备的上次报告状态。
  • metadata 元数据
    存储 state 部分中每个属性更新的时间戳。
  • version 文档版本
    文档每次更新时,此版本号都会递增。用于确保正在更新的文档为最新版本。更新时版本号需要设置成大于影子系统的版本号,否则会更新失败。如果不进行校验版本号,则不要带上该字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"state": {
"desired": {
"human_check_switch": 1,
"move_track_switch": 1
},
"reported": {
"human_check_switch": 0,
"move_track_switch": 0
}
},
"metadata": {
"desired": {
"human_check_switch": {
"timestamp": 1689219987
},
"move_track_switch": {
"timestamp": 1689219987
}
},
"reported": {
"human_check_switch": {
"timestamp": 1689219980
},
"move_track_switch": {
"timestamp": 1689219980
}
}
},
"TIMESTAMP": 1689219987,
"VERSION": 1
}

存储引擎

占用空间:以一个设备有 30 个属性为例,大约需要 4.2K 空间,30万个设备则需要 1.2G, 1百万个设备则需要 4G

对比了 Redis、MongoDB、COS

  • MongoDB:需要引入新技术栈,有学习、维护成本
  • COS:费用不低,心跳机制每天读写请求量好几亿

考虑到目前现有的业务以及对存储引擎的读写操作并发要求不低、使用成本,决定使用 Redis 进行存储。

数据流

下面以摄像机设备为例,说明设备、设备影子以及应用程序之间的通信,主要介绍设备主动上报状态、应用程序(SDK)改变设备状态、设备主动获取影子内容,和设备、应用程序主动删除影子属性。

设备主动上报

设备登录在线时,主动上报设备状态到影子。应用程序主动获取设备影子状态。

控制模型
  1. 设备通过 Topic 上报最新状态到设备影子。如果携带 version ,影子系统则判断根据版本号决定是否更新,否则直接覆盖更新。
  2. 设备影子文件更新后,设备影子会返回结果给设备,即发送消息到设备订阅的 Topic 。
  3. 如果更新失败,设备则需要重新上报。

应用程序改变设备状态

设备在线时,应用程序更新设备期望状态到影子,设备影子将指令下发至设备,如果设备下线,则当设备上线时主动获取影子信息,根据期望状态进行更新。

控制模型
  1. 应用程序通过 HTTP 或者 MQTT 更改期望状态,开启人脸检测
  2. 平台设备影子文件更新
  3. 影子文件更新成功后,下发指令给设备(需要重试机制),设备订阅 Topic 获取期望状态进行更新。
    1. 设备在线:根据上面的 desired 部分的值,将人脸检测进行开启。(设备可根据时间戳决定是否更新)
    2. 设备不在线:上线时主动获取设备影子期望状态进行更新
  4. 设备更新成功后,使用 Topic 上报最新状态到影子。影子更新成功后则返回结果给设备。(流程参考 3.5.1 设备上报状态)
  5. 设备上报完最新状态后,影子系统可自动清空 desired
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"state": {
"reported": {
"face_check_switch": 0,
"voice_check_switch": 0,
"move_track_switch": 1
},
"desired": {
"face_check_switch": 1
}
},
"metadata": {
"reported": {
"face_check_switch": {
"timestamp": 1689303208
}
},
"desired": {
"face_check_switch": {
"timestamp": 1689303210
}
}
},
"timestamp": 1689303210,
"version": 2
}

设备主动获取影子内容

若应用程序发送指令时,设备离线。设备再次上线后,将主动获取设备影子内容。

控制模型
  1. 主动发送以下消息到 Topic 中,请求获取设备影子中保存的最新状态。这里设备影子将只返回设备需要更新的期望状态。
  2. 当设备影子收到消息后,发送最新状态到 Topic 。
  3. 设备根据返回的数据决定是否更新设备当前状态,一旦更新设备自身状态,需要进行上报状态至设备影子,(流程参考 3.5.1 设备上报状态)
  4. 设备影子接收上报的最新状态后更新 JSON 文档,并返回设备更新结果。

设备主动删除影子属性

设备或应用需要删除影子则需要发送请求至设备影子,例如在重置设备时。

  1. 设备发送删除命令到 Topic,将需要删除的属性值设置为 null 。
  2. 设备影子接收到命令后,更新影子 JSON 文件,然后会返回结果给设备

版本控制

以下客户端指的是 SDK 或设备

设备影子服务支持在每个更新消息中进行版本控制。这意味着,每次更新影子时,JSON 文档的版本将会增加。这可以确保两件事情:

  • 如果客户端尝试使用旧版本号覆盖影子,则会更新失败。需要先重新获取影子信息,然后才能更新数据。
  • 如果消息的版本比客户端存储的版本低,客户端则可决定不对该消息执行操作。
    客户端可以在影子文档中不包含版本以绕过版本匹配。

为了减少因版本冲突更新失败的问题,在版本校验失败时进一步校验属性的更新时间戳,如果时间戳符合更新条件(即时间戳大于修改该属性
时间戳时),则忽略版本冲突问题。