# 阿里云上报流程功能需求文档 ## 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` 当前配置示例: ```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)` 完成。 其中设备序列号字段规则如下: ```js n: str.substring(192, 200) ``` 即: - 字段名:`n` - 来源:报文第 `192` 到 `200` 位 - 含义:设备序列号 - 用途: - 作为设备唯一识别码 - 作为三元组接口的 `deviceName` - 作为阿里云设备连接身份的一部分 ### 7.4 解析结果要求 报文解析后至少要得到以下数据: - `n`:设备序列号 - 其他业务字段:A、B、C、...、C53、C54、C55 等 - `suedtime`:接收时间 - `deviceType`:设备类型 - `IPAddress`:连接来源标识 --- ## 8. 阿里云上报完整流程 ## 8.1 流程总览 ```mermaid 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.js` 的 `handleDevice(socket)` 中: 1. 读取设备远端地址 `remoteAddress` 2. 兼容 IPv6 映射 IPv4 场景,去掉 `::ffff:` 前缀 3. 生成连接标识: ```js const connectionId = `${remoteAddress}:${socket.remotePort}`; ``` 4. 初始化设备状态对象 `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. 截取完整消息: ```js const message = buffer.substring(startIdx, endIdx).trim(); ``` #### 约束要求 - 仅当同时找到 `K1` 和 `\r\n` 时,才认为是一条完整数据 - 缓冲区过大且仍找不到起始标记时,应清空并记录告警 - 本步骤不做阿里云处理,只负责拿到完整原始报文 --- ### 步骤 3:解析报文并提取设备序列号 #### 输入 - `message`:完整报文字符串 - `connectionId`:连接标识 #### 系统动作 在 `handleData(connectionId, message)` 中: ```js 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` - 当前设备尚未建立阿里云连接,或允许重试 #### 调用入口 ```js const tupleResult = await this.registerAliyunDevice(connectionId); ``` #### 三元组获取逻辑 在 `registerAliyunDevice(connectionId)` 中: 1. 读取 `info.iotDeviceNo` 2. 调用: ```js const { data } = await getDeviceSYZ(info.iotDeviceNo); ``` 3. `getDeviceSYZ(deviceNo)` 内部调用: ```js getAliyunDeviceSecret('device/info/getAliyunDeviceSecret', deviceNo) ``` 4. `api.js` 中发起 HTTP POST 请求: - 地址:`https://things.icoldchain.cn/device/info/getAliyunDeviceSecret` - 请求方式:`POST` - 请求头:`application/x-www-form-urlencoded` - 请求参数: ```txt isAutoRegister=1 deviceName={设备序列号} ``` #### 三元组接口返回要求 接口应返回如下信息: - `productKey` - `deviceName` - `deviceSecret` #### 功能要求 - 设备序列号 `n` 必须作为接口中的 `deviceName` - 三元组接口必须支持设备不存在时的自动注册能力,或返回明确失败信息 - 若三元组字段不完整,不得继续进行阿里云连接创建 --- ### 步骤 6:三元组重试冷却控制 #### 当前实现规则 为避免频繁请求三元组接口,系统增加了重试冷却逻辑: ```js const ALIYUN_REGISTER_RETRY_MS = 60000; ``` 当上一次三元组请求失败后,在 60 秒内重复请求会直接返回: - `code = ALIYUN_TUPLE_RETRY_COOLDOWN` - `reason = 上次失败原因 或 三元组重试冷却中` #### 功能要求 其他项目接入时应保留该能力: - 失败后不要无限高频打接口 - 建议最小重试冷却时间不少于 60 秒 - 冷却期间应记录最近一次失败原因,方便排查 --- ### 步骤 7:使用三元组创建设备连接 #### 触发条件 - 已成功获取三元组 - 当前设备尚未创建 `iotDevice` #### 系统动作 使用阿里云 SDK 创建连接: ```js info.iotDevice = aliyunIot.device({ ProductKey: model.productKey, DeviceName: model.deviceName, DeviceSecret: model.deviceSecret }); ``` 并监听两个事件: - `connect` - 记录阿里云物联网连接成功 - `error` - 记录阿里云物联网错误信息 #### 功能要求 - 每个设备应维护自己的 `iotDevice` 实例 - 同一设备若已存在可用 `iotDevice`,后续上报不得重复创建 - 成功创建设备连接后,应清空上一次三元组失败信息 --- ### 步骤 8:发送属性数据到阿里云 #### 触发条件 - `info.iotDevice` 已创建 - `info.masData` 不为空 #### 调用入口 ```js const propsResult = await this.postPropsToAliyun(connectionId); ``` #### 上报动作 ```js 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 = 阿里云返回的失败信息或未知错误` #### 功能要求 - 上报前必须校验 `iotDevice` 和 `masData` 均存在 - 上报失败时不能导致 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 请求异常 - 返回数据中缺少 `productKey`、`deviceName`、`deviceSecret` ### 要求 - 返回失败结果 - 记录失败原因到 `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 报文完整提取机制** - 能从数据流中识别完整报文 2. **设备序列号解析机制** - 必须能从该设备协议中得到稳定唯一设备号 - 最终可映射为阿里云 `deviceName` 3. **设备连接上下文管理** - 每个连接需保存: - 设备号 - 最近数据 - 阿里云连接实例 - 最近注册失败时间和原因 4. **三元组接口适配** - 接口调用方式可复用当前项目,或对接新的后端接口 - 但输出必须兼容:`productKey/deviceName/deviceSecret` 5. **阿里云 SDK 连接与属性上报** - 必须支持连接建立、错误监听、属性上报 6. **失败重试和冷却控制** - 必须避免三元组接口和阿里云连接高频重试 7. **日志与监控** - 必须记录: - 设备号获取结果 - 三元组获取结果 - 阿里云连接结果 - 属性上报结果 --- ## 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` - 请求参数: ```txt isAutoRegister=1 deviceName={设备序列号} ``` ### 15.2 代码参考片段 ```js const { data } = await getAliyunDeviceSecret( 'device/info/getAliyunDeviceSecret', deviceNo ); ``` ```js info.iotDevice = aliyunIot.device({ ProductKey: model.productKey, DeviceName: model.deviceName, DeviceSecret: model.deviceSecret }); ``` ```js info.iotDevice.postProps(info.masData, (res) => { if (res?.message === 'success') { // 上报成功 } else { // 上报失败 } }); ``` --- ## 16. 总结 当前项目中的阿里云链路,本质上是一个标准的“设备数据驱动注册与上报流程”: 1. TCP 收到透析机数据 2. 解析出设备序列号 `n` 3. 以 `n` 为 `deviceName` 获取三元组 4. 使用三元组创建阿里云设备连接 5. 使用最新解析数据执行 `postProps` 上报 6. 后续数据持续复用同一个阿里云连接进行上报 其他透析机联机项目只要满足“**能解析出设备唯一编号**”这一前提,就可以照此模式复用整条阿里云上报链路。