本文档描述 jh2028-new-service 的服务通讯主流程,覆盖 TCP 接收、设备识别、协议解码、本地缓存、大屏展示、MQTT 上报和阿里云上报。
现场通讯里需要先区分两个角色:
因此,从 TCP 连接方向看:
设备数据盒子 TCP 客户端 ---> 本程序 TCP 服务端
从数据流方向看:
设备/透传盒 ---> 本程序 ---> MQTT / 阿里云 / 监测大屏
本程序启动后监听 config.tcp.host 和 config.tcp.port。设备列表中维护每台设备盒子的 ip、deviceId、name,程序根据 TCP 来源 IP 匹配设备编号。
flowchart TD
A[JH2028 设备] --> B[串口透传盒子]
B -->|主动连接 TCP 服务端<br/>持续发送数据| C[本程序 TCP 服务端<br/>tcp-service.js]
C --> D{来源 IP 是否在<br/>config.devices 中}
D -->|否| D1[记录未知设备日志<br/>丢弃本包数据]
D -->|是| E[绑定设备编号 deviceId]
E --> F[接收二进制数据流]
F --> G[按 55 AA 帧头和 LEN 拆包]
G --> H{是否完整帧}
H -->|否| H1[继续缓存等待后续数据]
H -->|是| I[CRC8 校验]
I --> J{CRC 是否正确}
J -->|否| J1[记录校验失败日志<br/>丢弃该帧]
J -->|是| K[协议解码<br/>decoder.js]
K --> L{帧类型}
L -->|实时数据<br/>CMDTYPE=01 CMDID=00| M[解析治疗/温度/压力/血液指标]
L -->|血压数据<br/>CMDTYPE=01 CMDID=01| N[解析 N/O/P<br/>写入 M=接收时间]
L -->|血压错误码| N1[只记录日志<br/>不更新缓存/不上报]
L -->|未知命令| L1[记录不支持命令日志]
M --> O[更新设备本地缓存<br/>state-cache.js]
N --> O
O --> P[生成完整上报数据<br/>包含最后一次实时数据和血压数据]
P --> Q[刷新大屏快照<br/>dashboard-service.js]
Q --> Q1[浏览器大屏<br/>SSE 实时更新/轮询兜底]
P --> R{上报通道配置<br/>send.channels}
R -->|mqtt| S[MQTT 上报<br/>mqtt-service.js]
R -->|aliyun| T[阿里云上报<br/>aliyun-service.js]
R -->|mqtt + aliyun| S
R -->|mqtt + aliyun| T
S --> U{上报结果}
T --> U
U -->|成功| U1[记录成功日志]
U -->|失败| U2[记录失败日志<br/>不补发]
flowchart TD
A[启动 app.js] --> B[读取 config.json]
B --> C[校验 tcp/devices/protocol/send 配置]
C --> D[初始化日志 logger]
D --> E[加载物模型 alModel.json]
E --> F[初始化 StateCache]
F --> G{send.channels}
G -->|包含 mqtt| H[初始化 MQTT 服务]
G -->|包含 aliyun| I[初始化阿里云服务]
G --> J[初始化 DashboardService]
H --> K[启动 TCP 服务]
I --> K
J --> K
K --> L[监听 TCP 端口<br/>默认 9000]
J --> M[监听大屏端口<br/>默认 9100]
L --> N[等待设备盒子接入]
M --> O[等待浏览器访问]
sequenceDiagram
participant Device as JH2028设备/透传盒子
participant TCP as TCP服务
participant Decoder as 协议解码器
participant Cache as 本地状态缓存
participant UI as 监测大屏
participant MQTT as MQTT服务
participant Aliyun as 阿里云服务
Device->>TCP: 主动建立 TCP 连接
Device->>TCP: 持续发送 55 AA 新协议数据帧
TCP->>TCP: 根据来源 IP 匹配 deviceId
TCP->>TCP: 数据流拆包,得到完整帧
TCP->>Decoder: CRC8 校验并解码
Decoder-->>TCP: 返回 realtime 或 bloodPressure 数据
TCP->>Cache: 合并到设备最后一次完整缓存
Cache-->>TCP: 返回完整 payload
TCP->>UI: 推送最新设备状态和指标
TCP->>MQTT: 按原 Topic 规则立即上报完整 payload
TCP->>Aliyun: 获取/复用三元组并立即上报完整 payload
MQTT-->>TCP: 返回上报结果
Aliyun-->>TCP: 返回上报结果
TCP->>TCP: 成功/失败均记录日志,失败不补发
stateDiagram-v2
[*] --> 未连接
未连接 --> 已连接: 设备盒子主动连接
已连接 --> 等待数据: 已匹配设备 IP
等待数据 --> 数据正常: 收到并成功解码一帧
数据正常 --> 数据正常: 持续收到有效数据
数据正常 --> 数据超时: 超过 staleDataMs 未更新
数据超时 --> 数据正常: 再次收到有效数据
已连接 --> 离线: socket close/error/timeout
等待数据 --> 离线: socket close/error/timeout
数据正常 --> 离线: socket close/error/timeout
数据超时 --> 离线: socket close/error/timeout
离线 --> 已连接: 设备盒子重新连接
flowchart TD
A[收到完整 payload] --> B{是否启用 aliyun 通道}
B -->|否| B1[跳过阿里云上报]
B -->|是| C{设备是否已有可用连接}
C -->|有| H[调用 postProps 上报属性]
C -->|没有| D[按 deviceId 请求三元组接口]
D --> E{三元组是否完整}
E -->|否| E1[记录失败日志<br/>本次不补发]
E -->|是| F[创建阿里云 IoT Device 实例]
F --> G[等待/复用连接]
G --> H
H --> I{postProps 是否成功}
I -->|成功| I1[记录上报成功]
I -->|失败| I2[记录失败原因<br/>不补发]
flowchart LR
A[config.devices<br/>设备清单] --> D[大屏快照]
B[TCP 连接会话<br/>在线/离线/连接时间] --> D
C[StateCache<br/>最后一次完整 payload] --> D
D --> E[/api/snapshot]
D --> F[/events SSE]
E --> G[浏览器大屏]
F --> G
config.devices 中配置的来源 IP 匹配,不从报文中解析设备编号。M,值为服务端接收时间。deviceName = deviceId。