编辑 | blame | 历史 | 原始文档

阿里云上报流程功能需求文档

1. 文档目的

本文档用于说明当前透析机 TCP 通讯服务中,**从设备报文中提取设备序列号**、**获取阿里云设备三元组**、**建立阿里云物联网连接**、**上报设备属性数据** 的完整流程。

目标是让其他开发人员在新的透析机联机项目中,按同样的业务规则实现一致的阿里云上报能力。


2. 适用范围

适用于以下类型的系统:

  • 透析机 TCP 联机服务
  • 网关型设备接入服务
  • 需要将设备实时数据同步到阿里云物联网平台的通讯服务

3. 相关模块

当前项目中与阿里云上报链路直接相关的模块如下:

  • index.js
  • 设备连接管理
  • 报文接收与解析触发
  • 三元组获取
  • 阿里云连接创建
  • 属性上报
  • Strholp.js
  • 原始报文解析
  • 提取设备序列号 n
  • api.js
  • 调用后端接口获取阿里云三元组
  • aliyun.json
  • 阿里云功能启用配置
  • aliyun-iot-device-sdk
  • 阿里云物联网设备 SDK

4. 总体业务目标

系统需要支持以下业务能力:

  1. 接收透析机通过 TCP 长连接发送的原始报文。
  2. 从报文中解析出设备序列号和各项业务数据。
  3. 以设备序列号作为设备唯一标识,向后端接口请求阿里云设备三元组。
  4. 使用三元组在阿里云物联网平台建立设备连接。
  5. 在连接建立后,将透析机最新数据作为属性数据上报到阿里云。
  6. 当设备重复上报时,不重复注册,只复用已有连接持续上报。
  7. 在失败时具备日志记录、重试冷却和可追踪能力。

5. 术语定义

  • 设备序列号:透析机报文中提取出的唯一设备编号,对应当前实现中的字段 n
  • 三元组:阿里云设备身份信息,包括:
  • productKey
  • deviceName
  • deviceSecret
  • 连接标识:当前 TCP 连接标识,格式为 IP:Port,对应代码中的 connectionId
  • 属性上报:调用阿里云设备 SDK 的 postProps 方法,将设备实时属性上传到阿里云。

6. 配置需求

6.1 阿里云启用配置

文件:aliyun.json

当前配置示例:

{
  "enabled": true,
  "autoRegister": true
}

6.2 配置项要求

  • enabled
  • 类型:boolean
  • 含义:是否启用阿里云链路
  • 行为:
    • true:执行三元组获取、阿里云连接和属性上报
    • false:跳过阿里云相关逻辑
  • autoRegister

  • 类型:boolean
  • 含义:后端接口是否支持自动注册设备
  • 说明:当前代码中未直接消费此字段,但后端接口请求参数中固定传了 isAutoRegister=1

6.3 其他依赖配置

阿里云链路运行还依赖:

  • TCP 服务正常接收设备数据
  • 后端三元组接口可用
  • aliyun-iot-device-sdk 已安装

7. 数据来源与设备序列号获取规则

7.1 原始报文来源

透析机通过 TCP 发送 ASCII 数据流。

7.2 报文边界规则

在当前实现中,完整报文需满足:

  • 起始标记:K1
  • 结束标记:\r\n

系统通过缓冲区持续接收数据,并从中提取完整报文。

7.3 设备序列号提取规则

完整报文解析由 Strholp.js 中的 strToObj(str, ipAddress) 完成。

其中设备序列号字段规则如下:

n: str.substring(192, 200)

即:

  • 字段名:n
  • 来源:报文第 192200
  • 含义:设备序列号
  • 用途:
  • 作为设备唯一识别码
  • 作为三元组接口的 deviceName
  • 作为阿里云设备连接身份的一部分

7.4 解析结果要求

报文解析后至少要得到以下数据:

  • n:设备序列号
  • 其他业务字段:A、B、C、...、C53、C54、C55 等
  • suedtime:接收时间
  • deviceType:设备类型
  • IPAddress:连接来源标识

8. 阿里云上报完整流程

8.1 流程总览

sequenceDiagram
    participant Device as 透析机
    participant TCP as TCP服务
    participant Parser as 协议解析器
    participant API as 三元组接口
    participant Aliyun as 阿里云物联网平台

    Device->>TCP: 发送原始报文
    TCP->>TCP: 按 K1 ~ \r\n 提取完整消息
    TCP->>Parser: 解析报文
    Parser-->>TCP: 返回 masData(含 n)
    TCP->>TCP: 保存 info.iotDeviceNo = masData.n
    TCP->>API: 请求三元组(deviceName=n)
    API-->>TCP: 返回 productKey/deviceName/deviceSecret
    TCP->>Aliyun: 使用三元组建立设备连接
    TCP->>Aliyun: postProps(masData)
    Aliyun-->>TCP: 返回上报结果

8.2 详细步骤说明

步骤 1:建立设备 TCP 连接

输入

  • 透析机发起 TCP 连接

系统动作

index.jshandleDevice(socket) 中:

  1. 读取设备远端地址 remoteAddress
  2. 兼容 IPv6 映射 IPv4 场景,去掉 ::ffff: 前缀
  3. 生成连接标识:
const connectionId = `${remoteAddress}:${socket.remotePort}`;
  1. 初始化设备状态对象 deviceInfo

状态初始化内容

  • ipAddress
  • connectedAtMs
  • lastAck
  • status = 'pending'
  • lastSignal = 'K'
  • iotDevice = null
  • iotDeviceNo = ''
  • lastAliyunRegisterAttempt = 0
  • lastAliyunRegisterError = ''

目的

为后续“握手、解析、三元组获取、阿里云注册、属性上报”准备上下文。


步骤 2:接收原始报文并识别完整消息

输入

  • TCP 数据块 chunk

系统动作

socket.on('data') 中:

  1. chunk 转换为 ASCII 字符串
  2. 追加到缓冲区 buffer
  3. 查找起始标记 K1
  4. 查找结束标记 \r\n
  5. 截取完整消息:
const message = buffer.substring(startIdx, endIdx).trim();

约束要求

  • 仅当同时找到 K1\r\n 时,才认为是一条完整数据
  • 缓冲区过大且仍找不到起始标记时,应清空并记录告警
  • 本步骤不做阿里云处理,只负责拿到完整原始报文

步骤 3:解析报文并提取设备序列号

输入

  • message:完整报文字符串
  • connectionId:连接标识

系统动作

handleData(connectionId, message) 中:

const masData = toModel(message, connectionId);
info.iotDeviceNo = masData.n;
info.masData = masData;

输出

  • masData:完整解析对象
  • masData.n:设备序列号
  • info.iotDeviceNo:写入当前设备上下文
  • info.masData:保存最近一帧数据

功能要求

  • 设备序列号必须从报文中提取,不能使用 TCP 连接 ID 替代
  • 若解析后没有得到合法设备序列号,则不得继续执行三元组获取和阿里云注册
  • 解析成功后,应更新 lastAck

步骤 4:设备状态切换为有效

触发条件

  • 成功解析到一条有效报文

系统动作

若当前设备状态不是 valid,则执行:

  1. info.status = 'valid'
  2. 停止重试机制 stopRetryMechanism(connectionId)
  3. 开启保活机制 startKeepAlive(connectionId, info.lastSignal)
  4. 记录日志:设备已完成握手并进入上报阶段

功能意义

说明设备已经从“连接成功但未确认有效数据”状态,切换为“已具备正式上报条件”状态。


步骤 5:根据设备序列号获取阿里云三元组

触发条件

  • aliyunConfig.enabled === true
  • 当前设备已成功解析出 masData.n
  • 当前设备尚未建立阿里云连接,或允许重试

调用入口

const tupleResult = await this.registerAliyunDevice(connectionId);

三元组获取逻辑

registerAliyunDevice(connectionId) 中:

  1. 读取 info.iotDeviceNo
  2. 调用:
const { data } = await getDeviceSYZ(info.iotDeviceNo);
  1. getDeviceSYZ(deviceNo) 内部调用:
getAliyunDeviceSecret('device/info/getAliyunDeviceSecret', deviceNo)
  1. api.js 中发起 HTTP POST 请求:
  • 地址:https://things.icoldchain.cn/device/info/getAliyunDeviceSecret
  • 请求方式:POST
  • 请求头:application/x-www-form-urlencoded
  • 请求参数:
isAutoRegister=1
deviceName={设备序列号}

三元组接口返回要求

接口应返回如下信息:

  • productKey
  • deviceName
  • deviceSecret

功能要求

  • 设备序列号 n 必须作为接口中的 deviceName
  • 三元组接口必须支持设备不存在时的自动注册能力,或返回明确失败信息
  • 若三元组字段不完整,不得继续进行阿里云连接创建

步骤 6:三元组重试冷却控制

当前实现规则

为避免频繁请求三元组接口,系统增加了重试冷却逻辑:

const ALIYUN_REGISTER_RETRY_MS = 60000;

当上一次三元组请求失败后,在 60 秒内重复请求会直接返回:

  • code = ALIYUN_TUPLE_RETRY_COOLDOWN
  • reason = 上次失败原因 或 三元组重试冷却中

功能要求

其他项目接入时应保留该能力:

  • 失败后不要无限高频打接口
  • 建议最小重试冷却时间不少于 60 秒
  • 冷却期间应记录最近一次失败原因,方便排查

步骤 7:使用三元组创建设备连接

触发条件

  • 已成功获取三元组
  • 当前设备尚未创建 iotDevice

系统动作

使用阿里云 SDK 创建连接:

info.iotDevice = aliyunIot.device({
  ProductKey: model.productKey,
  DeviceName: model.deviceName,
  DeviceSecret: model.deviceSecret
});

并监听两个事件:

  • connect
  • 记录阿里云物联网连接成功
  • error
  • 记录阿里云物联网错误信息

功能要求

  • 每个设备应维护自己的 iotDevice 实例
  • 同一设备若已存在可用 iotDevice,后续上报不得重复创建
  • 成功创建设备连接后,应清空上一次三元组失败信息

步骤 8:发送属性数据到阿里云

触发条件

  • info.iotDevice 已创建
  • info.masData 不为空

调用入口

const propsResult = await this.postPropsToAliyun(connectionId);

上报动作

info.iotDevice.postProps(info.masData, callback)

上报数据内容

当前实现中,直接将完整解析对象 masData 作为属性对象上报。

masData 包含:

  • 设备序列号 n
  • 各协议字段 A~Z、a~z、C53、C54、C55
  • suedtime
  • deviceType
  • IPAddress

返回结果要求

  • 成功:
  • code = ALIYUN_PROPS_OK
  • reason = 成功
  • 失败:
  • code = ALIYUN_PROPS_FAIL
  • reason = 阿里云返回的失败信息或未知错误

功能要求

  • 上报前必须校验 iotDevicemasData 均存在
  • 上报失败时不能导致 TCP 连接立即断开
  • 上报结果必须被记录,便于监控和追踪

9. 状态流转要求

9.1 TCP 连接状态

建议至少包含以下状态:

  • pending
  • 已连接,但还未收到有效设备报文
  • valid
  • 已收到有效设备报文,可进入上报阶段
  • offline
  • 连接断开或设备离线
  • error
  • TCP 或上游接口发生异常

9.2 阿里云状态逻辑

建议按以下逻辑控制:

  1. 没有有效报文时,不允许请求三元组
  2. 有设备序列号但无三元组时,先请求三元组
  3. 有三元组但无阿里云连接时,先创建连接
  4. 有连接且有数据时,执行属性上报
  5. 已有连接时,后续数据直接上报,不重复注册

10. 异常处理要求

10.1 设备序列号缺失

现象

  • 报文解析后 masData.n 为空或非法

要求

  • 不允许发起三元组接口调用
  • 记录失败日志
  • 保留 TCP 连接,由后续数据继续尝试恢复

10.2 三元组接口失败

现象

  • HTTP 请求异常
  • 返回数据中缺少 productKeydeviceNamedeviceSecret

要求

  • 返回失败结果
  • 记录失败原因到 lastAliyunRegisterError
  • 启用重试冷却,不要高频请求接口
  • 不影响本次 TCP 数据接收和本地缓存

10.3 阿里云连接失败

现象

  • SDK 连接异常
  • 触发 iotDevice.on('error')

要求

  • 记录日志
  • 保留后续重试能力
  • 不影响本地缓存和 HTTP/MQTT 分发

10.4 属性上报失败

现象

  • postProps 返回非 success

要求

  • 记录阿里云返回原因
  • 返回失败状态供监控模块记录
  • 不直接销毁 TCP 连接
  • 后续新数据到达时继续尝试上报

11. 当前实现中的关键函数映射

11.1 设备序列号解析

  • 文件:Strholp.js
  • 函数:strToObj(str, ipAddress)
  • 输出字段:n

11.2 接收并处理设备数据

  • 文件:index.js
  • 函数:handleData(connectionId, message)

11.3 获取三元组

  • 文件:index.js
  • 函数:getDeviceSYZ(deviceNo)
  • 文件:api.js

  • 函数:getAliyunDeviceSecret(url, deviceName)

11.4 注册阿里云设备连接

  • 文件:index.js
  • 函数:registerAliyunDevice(connectionId)

11.5 上报属性

  • 文件:index.js
  • 函数:postPropsToAliyun(connectionId)

12. 其他透析机项目接入时的开发要求

12.1 必须实现的能力

其他透析机联机项目要复用此能力,至少必须实现:

  1. TCP 报文完整提取机制
  • 能从数据流中识别完整报文
  1. 设备序列号解析机制
  • 必须能从该设备协议中得到稳定唯一设备号
  • 最终可映射为阿里云 deviceName
  1. 设备连接上下文管理
  • 每个连接需保存:
    • 设备号
    • 最近数据
    • 阿里云连接实例
    • 最近注册失败时间和原因
  1. 三元组接口适配
  • 接口调用方式可复用当前项目,或对接新的后端接口
  • 但输出必须兼容:productKey/deviceName/deviceSecret
  1. 阿里云 SDK 连接与属性上报
  • 必须支持连接建立、错误监听、属性上报
  1. 失败重试和冷却控制
  • 必须避免三元组接口和阿里云连接高频重试
  1. 日志与监控
  • 必须记录:
    • 设备号获取结果
    • 三元组获取结果
    • 阿里云连接结果
    • 属性上报结果

12.2 允许按新项目调整的部分

以下内容可以根据新透析机协议进行调整:

  • 报文起止标志
  • 设备序列号在报文中的位置
  • 属性字段集合
  • 是否直接用原始解析对象上报,还是先做字段映射
  • 三元组接口地址
  • 重试时间间隔

13. 推荐实现顺序

建议其他开发按以下顺序落地:

  1. 完成 TCP 收包与完整报文识别
  2. 完成协议解析,并确认能正确解析设备序列号
  3. 完成三元组接口调用并验证返回结果
  4. 完成阿里云 SDK 建连
  5. 完成属性上报
  6. 增加失败重试、冷却控制和日志
  7. 最后与 HTTP/MQTT 等其他通道整合

14. 验收标准

若其他透析机联机项目完成以下能力,则视为满足本需求:

  • 能从真实设备报文中稳定解析出设备序列号
  • 能以设备序列号为参数成功获取三元组
  • 能使用三元组成功连接阿里云物联网平台
  • 能在收到新数据后持续调用 postProps 上报属性
  • 三元组缺失、接口异常、上报失败时有明确日志和重试策略
  • 已注册设备不会重复注册,而是直接复用已有连接上报

15. 当前项目的参考接口信息

15.1 三元组获取接口

  • 请求地址:https://things.icoldchain.cn/device/info/getAliyunDeviceSecret
  • 请求方式:POST
  • 请求头:Content-Type: application/x-www-form-urlencoded
  • 请求参数:
isAutoRegister=1
deviceName={设备序列号}

15.2 代码参考片段

const { data } = await getAliyunDeviceSecret(
  'device/info/getAliyunDeviceSecret',
  deviceNo
);
info.iotDevice = aliyunIot.device({
  ProductKey: model.productKey,
  DeviceName: model.deviceName,
  DeviceSecret: model.deviceSecret
});
info.iotDevice.postProps(info.masData, (res) => {
  if (res?.message === 'success') {
    // 上报成功
  } else {
    // 上报失败
  }
});

16. 总结

当前项目中的阿里云链路,本质上是一个标准的“设备数据驱动注册与上报流程”:

  1. TCP 收到透析机数据
  2. 解析出设备序列号 n
  3. ndeviceName 获取三元组
  4. 使用三元组创建阿里云设备连接
  5. 使用最新解析数据执行 postProps 上报
  6. 后续数据持续复用同一个阿里云连接进行上报

其他透析机联机项目只要满足“**能解析出设备唯一编号**”这一前提,就可以照此模式复用整条阿里云上报链路。