Skip to content

14.2 IoT DC3 端到端项目实战

14.2.1 项目背景与需求定义

多数失败的物联网项目,问题并非出在编码实现,而是出在开始写代码之前——需求定义阶段。团队花大量时间讨论“我们要做一个强大的物联网平台”,却没人定义“强大”的具体工程边界。功能清单列了几十项,优先级全是 P0,最后交付时核心链路跑不通,边缘功能却做得无比精致。这种“需求镀金”现象在物联网项目中尤其普遍,因为物理世界的接入维度多、约束链长,需求方和开发方都容易忽略工程边界的存在。

IoT DC3 是一个定位明确的开源工业物联网平台。它的设计目标是连接现场设备,覆盖设备管理、数据采集、规则引擎和数据服务等核心能力,而不是试图成为一个包罗万象的“万物互联操作系统”。这一务实定位,使它成为理解物联网平台工程边界的理想参照物。从典型的开源 IoT 平台架构看,核心层通常由设备管理、数据持久化、规则引擎和协议适配等几个职责清晰的模块组成,协议驱动独立部署,通过消息队列与主服务异步通信。这种解耦设计决定了需求定义阶段必须回答:你的场景中,协议驱动需要支撑多少种协议?设备上行数据的峰值吞吐量是多少?规则引擎的实时性要求到什么级别?

这意味着开源的工程边界,不一定是你项目实际需要面对的边界。在需求定义阶段,最关键的产出不是“能做多少”,而是“本轮不做什么”。这需要你在理解平台能力的基础上,对真实业务场景做一次穿透式梳理。

以下把 14.1.1 的方法论落到一个贯穿本章的例子上:基于 IoT DC3 搭建一个智能工厂管理平台。

某中等规模电子制造工厂,拥有约 2000 台设备,包括 SMT 贴片机、回流焊机、AOI(Automated Optical Inspection,自动光学检测仪)和温湿度传感器。当前的工程痛点:设备状态靠人工巡检,数据格式不统一——部分设备支持 Modbus TCP,部分只输出串口数据,还有几台老旧设备走的是自定义二进制协议。生产异常只能等操作工发现再上报,从故障发生到人工确认的平均耗时大约在四十分钟的量级。

与工厂运营团队多次沟通后,业务需求被收敛为四条核心目标:设备统一接入与状态实时采集;历史数据存储与趋势分析;告警规则配置与多通道推送(车间看板、微信、邮件);基于设备数据做预测性维护的初步尝试。这四条需求与工厂的运营痛点一一对应:设备接入解决数据孤岛,存储和分析解决“有数据但看不见”,告警解决响应滞后,预测维护解决被动维修。

针对这一例子,功能模块可以按以下方式划分。

设备接入模块:负责协议适配。智能工厂中涉及 Modbus TCP、串口(自定义协议)以及部分支持 MQTT 的新设备。不同协议对应不同驱动,驱动贴近现场设备运行,采集的数据经消息队列上报云端,不直连核心服务。这一层不做数据存储,只做格式转换和数据转发。

设备管理模块:负责设备的注册、分组、状态跟踪和生命周期管理。启停机、固件版本、在线状态、归属产线等元数据在此维护。

数据中心:负责采集数据的接收、持久化和查询。时序数据库存储设备位号值,关系库或文档库存储设备配置和事件记录。告警引擎与数据中心联动,当数值超过设定阈值时触发告警。

智能分析模块:负责模型训练、推理和规则联动。这一轮做轻量化上线——先用基于统计的方法做异常检测(如离群点识别、趋势偏移),不急于上线深度学习模型。这一模块的具体工程实现将在后续小节展开,它也是后续集成 AI 能力的切入点。

应用与服务层:这一层面向人和业务系统提供能力。现场运维人员通过设备列表、数据看板和告警页面理解设备状态;生产管理系统通过接口读取设备事件、工单和统计结果;MES(Manufacturing Execution System,制造执行系统)与 ERP(Enterprise Resource Planning,企业资源计划)等系统则通过 API(Application Programming Interface,应用程序接口)完成跨系统协同。

功能模块划分完成后,还需要做一件容易被忽视的事:定边界。在这个例子中,以下能力被明确划入第二期或第三期:设备 OTA(Over-the-Air,空中下载)升级、设备影子(Device Shadow)、多租户隔离(当前只有单一工厂)、以及基于强化学习的全自动排产方案。边界定义的意义在于,它让开发团队和业务方都知道这只是一个起点,而不是终点。团队可以聚焦在四条需求上迭代,而不需要为“万能平台”这个虚目标分散精力。每次需求评审时,只要问一句“这个功能是否直接服务于四条核心需求”,大部分镀金需求自己就消失了。

需求定义阶段的交付物,是一份可评审、可争议、可修改的需求文档,辅以明确的功能模块清单和边界说明(包含明确的“不做”清单)。这份文档不追求完美,但必须有优先级、有取舍。需求边界一旦清晰,后续的架构设计、测试与验收就有稳定的判定依据;边界含糊,这些环节都会陷入反复返工。

14.2.2 系统架构设计

IoT DC3 可以按四层理解:南向设备层、协议 Driver 层、平台服务层和应用展现层。这个分层的价值不是画图,而是明确哪些调用可以同步、哪些数据必须异步,以及服务寻址和配置由谁负责。

四层职责

  • 南向设备层:传感器、PLC、控制器和第三方系统,使用 MQTT、Modbus、OPC UA、IEC 104 等协议。
  • 协议 Driver 层:每种协议独立部署,负责连接、编解码、位号读写和状态上报。Driver 可按现场需要下沉到边缘节点。
  • 平台服务层:Auth 负责认证授权;Manager 负责 Driver、设备、模板、位号和属性等元数据;Data 负责位号值、命令、回执、告警数据与查询;Agentic 负责模型、会话和 Spring AI Tools。
  • 应用展现层:Web、第三方应用和 API 客户端,经 Gateway 统一访问平台。

当前服务治理与消息基础设施

IoT DC3 当前没有 Nacos 或其他独立服务注册中心。Gateway 路由和 gRPC Channel 使用固定服务名,Compose 网络通过 DNS 解析,并允许用 CENTER_*_HOSTGATEWAY_ROUTE_*_URI 等环境变量覆盖地址。默认配置保存在项目 YAML 中,部署参数通过环境变量注入。

内部消息通过统一消息端口交换,RabbitMQ 是默认适配器;代码还提供 Kafka、RocketMQ、Pulsar、ActiveMQ 与 MQTT 5 适配器,由 DC3_MQ_TYPE 选择。Data 把位号命令与自定义命令交给消息端口,Driver 消费后执行协议操作,并返回结果回执、位号值、状态和事件。dc3-driver-kafka 是南向数据源驱动,与内部 Kafka 适配器不是一回事。

图 14-5 IoT DC3 系统分层架构北向请求经 Gateway 进入四中心,Driver 经 gRPC 对接 Manager;图中以默认 RabbitMQ 适配器表示 Data 与 Driver 之间的异步消息。图 14-5 IoT DC3 系统分层架构北向 REST/gRPC 同步、南向 RabbitMQ 异步,构成三类真实通信边界应用层Web 控制台运维 / 配置界面第三方应用REST API 集成API 客户端dc3-cli / 脚本REST接入层dc3-gatewayREST 路由 · Token 校验 · Compose DNS 寻址REST 路由到四中心平台服务层Auth鉴权与令牌PostgreSQLManager设备与模型元数据PostgreSQLData最新值与历史值Caffeine + TsdbStoreAgentic对话与受控工具Facade / gRPCData → RabbitMQ → Driver:命令Driver → RabbitMQ → Data:数据 / 回执RabbitMQ(默认)· PointValue / Status / Command / ReceiptDriver 层driver-mqttMQTT 发布 / 订阅driver-modbusTCP / RTU 轮询与写入driver-opcuaOPC UA 订阅采集Driver → Manager:gRPC 注册 / 元数据查询设备层 · 传感器 / 控制器 / 执行器(可下沉边缘)图 14-5 IoT DC3 四层架构:同步管理与异步数据分工,RabbitMQ 表示默认消息适配器。
图 14-5 IoT DC3 系统分层架构

该架构的工程取舍是:管理与元数据查询需要即时结果,因此使用 REST/gRPC;设备命令和上行数据需要异步解耦与削峰,因此通过统一消息端口交换,默认适配器为 RabbitMQ。边界清晰比组件数量更重要。

14.2.3 核心模块实现

理解 IoT DC3 的实现,应沿三条真实链路阅读源码,而不是套用“注册中心 + Kafka + 独立命令服务”的通用模板。

Driver 业务注册与元数据同步

Driver 启动后,DriverRegisterService 通过 gRPC 调用 Manager 的 driverRegister。注册内容是 Driver 的业务身份、配置和元数据,不是向 Nacos 等注册中心登记 IP。设备、位号、模板和属性等运行时元数据也通过 Manager Facade 查询,并缓存在 Driver 进程内的 Caffeine 中。

位号值上报与数据处理

协议实现通过 DriverProtocol 完成真实设备读写。读取或订阅得到的数据转换为统一 PointValue 后,由 DriverSenderService 交给消息端口;默认 RabbitMQ 适配器负责具体发布。Data 的 PointValueReceiver 从同一端口接收消息:低于批处理阈值时直接保存,高于阈值时进入 PointValueIngestBuffer 进程内缓冲后批量写入。Data 同时维护最新值的本地 Caffeine 缓存,并通过 TsdbStore 写入历史数据;默认实现为 TimescaleDB。持久化完成后再触发告警规则处理。

位号命令与结果回执

位号读写入口位于 Data。Data 按 Driver 服务名把命令交给消息端口,Driver 的 PointCommandReceiver 检查 expireAtcommandId,使用设备级锁串行化同一设备的协议操作,然后调用 DriverReadServiceDriverWriteService。成功或失败结果仍经消息端口回传 Data。ack、reject、nack/requeue、TTL 与死信交换机是默认 RabbitMQ 适配器的具体语义;换用其他适配器时,必须验证等价的确认、重试、过期与失败隔离行为。

工程边界

  • 没有独立 Command Service,命令入口和回执处理属于 Data。
  • 默认数据面使用 RabbitMQ;替换 Broker 后,命令、回执、位号值、状态和事件仍走同一消息端口,但确认、顺序、死信与延迟能力要按适配器重新验证。
  • 没有 Redis 两级设备影子;Driver 缓存元数据,Data 用本地 Caffeine 缓存最新位号值。
  • 没有统一 DeviceDriver 或全局 ConnectionManager,协议 Driver 按能力接口与各自连接模型实现。

沿这三条链路阅读代码,可以把“同步管理调用”和“异步设备数据流”准确分开,也能直接定位性能与可靠性责任边界。

14.2.4 设备接入与数据流

设备接入的核心挑战不是网络连通性,而是协议语义收敛。MQTT、Modbus、OPC UA 的连接模型、时序方式、数据表达各不相同——MQTT 依赖设备主动发布,Modbus 由 Driver 轮询,OPC UA 可以订阅节点变化。Driver 层需要将这些异构协议收敛为统一的 PointValue 和命令模型。协议入口不同,进入平台后的数据链路才一致。

从设备报文到位号值

以 MQTT 场景为例,设备报文可以使用 JSON,但 Topic 和字段结构由具体 Driver 定义,不存在全平台唯一的固定 Payload。Driver 完成连接、订阅、反序列化和设备/位号映射,再调用统一发送服务。

下面是一个简化的设备属性上报 JSON 结构示例,仅用于说明字段设计思路,并非 IoT DC3 所有 MQTT Driver 的强制格式:

json
{
  "deviceCode": "device-001",
  "timestamp": 1700000000123,
  "values": {
    "temperature": 25.6,
    "humidity": 68.2,
    "pressure": 1013.2
  },
  "qos": 1,
  "msgId": "a1b2c3d4"
}
  • deviceCode 对应平台已注册的设备身份,Driver 在启动时通过 Manager 元数据同步获得该映射。
  • values 内的键是位号标识符,值可以是数值、字符串或布尔,Driver 根据模板定义判断类型。
  • msgId 用于上行去重,Data 消费侧会根据 msgId(或组合 deviceCode + timestamp)做幂等判断。

实际项目中,如果位号数量超过数百,JSON 解析和序列化的 CPU 开销会变得显著。此时可考虑换用 Protobuf 或 MessagePack——Payload 结构不变,只是序列化/反序列化由 Driver 层替换,Data 侧保持统一消费接口。

数据流各阶段组件与功能说明

表 14-2 展示了上行位号值从设备到消息端口、缓存与时序存储端口的职责和典型风险。

表14-2 上行位号值数据流各阶段的职责与风险

阶段组件主要职责并发/一致性约束关键风险
协议接入设备侧协议(MQTT/Modbus/OPC UA)按照协议规范发送或响应数据设备连接保持、心跳保活网络闪断导致数据丢失;重连后 Topic/节点重复订阅
协议解析Driver(DriverProtocol 实现)反序列化原始报文,按 Manager 元数据转换为 PointValue 对象连接与并发模型由具体协议实现决定;Driver 本地 Caffeine 缓存元数据报文格式变化、阻塞调用或连接状态处理不当导致解析和资源问题
消息投递DriverSenderService → 消息端口发布统一 PointValue;默认 RabbitMQ 映射到相应 Exchange路由、确认、顺序、持久化与批量能力由所选适配器决定生产速率超过消费速率;Broker 容量或保留策略失配
异步消费Data 的 PointValueReceiver从消息端口接收,按阈值直接保存或进入 PointValueIngestBuffer确认与重投语义须和适配器契约一致;缓冲阈值需实测慢消费导致积压;重投产生重复;批量失败扩大影响
缓存更新Data → Caffeine 本地缓存维护本实例可见的最新位号值,供查询快速返回JVM 进程本地状态,多实例间不能假定强一致缓存陈旧、实例间差异、JVM 内存压力
持久化Data → TsdbStore写入历史位号值;默认适配器为 TimescaleDB批量、保留、聚合与查询能力取决于所选时序库适配器写入或查询瓶颈;保留策略、索引或分区配置失配
告警触发Data → 告警规则处理在持久化完成后检查规则并生成告警需要定义重复数据、重试和告警幂等语义规则错误导致误告或漏告;重放触发告警风暴

下行命令的异步回执

下行命令走反向异步链路:客户端经 Gateway 调用 Data 的位号命令接口,Data 将命令体交给消息端口;目标 Driver 消费后执行设备操作,结果回执再经同一端口返回 Data。默认 RabbitMQ 适配器映射到 dc3.e.point_command 等 Exchange。客户端应通过 WebSocket 订阅或轮询 Data 提供的命令状态 API,而不是假定 HTTP 请求会一直阻塞到设备返回。

Driver 在执行前使用 commandId 做去重与过期检查,并以设备级锁串行化同一设备的协议操作。commandId 的生成方、去重状态保留时间以及跨实例是否共享,应以当前接口与实现为准并通过重放测试验证;本地锁和进程内去重都不能自动提供跨 Driver 实例的全局互斥。

容量观测与瓶颈判断

容量设计的原则是:先观测,后优化。默认栈在 RabbitMQ 控制台观察消息速率、积压与未确认消息,在 Data 监控端点观察消费与写入延迟,在 TimescaleDB/PostgreSQL 侧观察 hypertable、查询与磁盘 IO;换用其他适配器时改用对应指标。只有压测证明单一链路成为瓶颈后,才考虑分区、冷热分层或更换适配器。不能因为仓库“支持”某个 Broker 或时序库,就宣称目标负载已经得到验证。

工程检查清单:

  • [ ] 设备连接稳定性:使用 MQTT 遗嘱消息和自动重连策略,Modbus Driver 配置超时重试。
  • [ ] 上行消息幂等:Data 侧按 msgIddeviceCode + timestamp 去重,避免重复写入。
  • [ ] 下行命令防重:客户端生成全局 UUID 作为 commandId;Driver 侧设备锁超时设置(例如 30 秒)。
  • [ ] 积压告警阈值(示例):RabbitMQ 队列深度超过 10,000 且持续 60 秒时告警,实际阈值应按基线和SLA校准。
  • [ ] 数据库写入慢查询(示例参数):监控 dc3_point_value 表的 track_io_timing,设置 PostgreSQL log_min_duration_statement = 200ms,实际参数应按现场负载校准。
图 14-6 设备接入与数据流上行 Device→Driver→消息端口→Data→Caffeine/TsdbStore;下行 Client→Gateway→Data→消息端口→Driver→Device,图中 RabbitMQ 表示默认适配器。图 14-6 设备接入与数据流上行位号值与下行命令分泳道;执行回执沿消息链返回并更新命令状态上行:位号值与状态设备MQTT / Modbus协议 Driver解析与映射RabbitMQ异步队列Data消费与批量存储Caffeine / TsdbStore查询RESTPointValue发布消费缓存/落库读取Data:更新 Caffeine 最新值,经 TsdbStore 批量持久化先保存再处理规则,保证告警基于已落库数据默认 RabbitMQ 解耦 Driver 与 Data;其他适配器需复验上行只向前推进,不等待消费端回执下行:命令与执行回执客户端POST 命令Gateway鉴权 / 路由Data校验 / 发布RabbitMQ命令队列Driver协议写入设备执行POST 命令授权请求发布命令消费设备写入执行回执(按 commandId 返回)完整回执:设备结果 → Driver → RabbitMQ → DataData 按 commandId 更新命令状态并供客户端查询或订阅客户端查询或订阅命令状态accepted / running / succeeded / failed图 14-6 消息端口承载上行、命令与回执;RabbitMQ 表示默认适配器。
图 14-6 设备接入与数据流

14.2.5 AI 运维能力构建(非开箱即用)

IoT DC3 的 987c96d50 源码快照中,Agentic Center 已实现模型配置、会话管理、Spring AI @Tool 调用和 Web/HTTP 对话;Gateway 的 /mcp 端点按 2025-06-18 修订版处理 initializenotifications/initializedpingtools/listtools/call,当前仅声明 Tools 能力,未实现 Resources、Prompts 或 Tasks。项目 Compose 没有 TensorFlow Serving、训练任务、模型卷,也没有 Agentic 订阅 Data 实时位号流的默认链路。因此本节只把预测性维护作为可选工程扩展讨论,不能写成当前开箱即用能力。

先规则,再统计,最后模型

异常检测可以分三档:固定阈值处理明确红线;滑动窗口、IQR、Z-score 等统计方法处理缓慢漂移;有监督或无监督模型处理多变量耦合、时间依赖和难以手写规则的模式。三档不是替代关系。模型只有在基线规则无法满足且数据质量、标签和收益足以支撑时才值得引入。

一个可选的预测性维护扩展

若项目确需模型推理,可以按以下边界设计:

  1. 从 Data 的历史查询接口取得经租户授权的位号数据。
  2. 在平台外完成时间对齐、缺失值处理、窗口化和训练。
  3. 把模型部署为独立、受认证保护的推理服务。
  4. 由授权任务读取 Data 数据并调用推理服务。
  5. 将推理结果写回一个明确的衍生位号,例如 bearing_anomaly_score
  6. 复用现有规则与通知链路判断阈值和持续时间。

模型类型、窗口长度和阈值必须由数据验证。LSTM、窗口 32、阈值 0.85 都只能作为假设示例,不能写成 IoT DC3 默认配置。Spring AI Tools 适合编排查询、解释和受控执行,不等于承担高频流式推理;MCP 也只负责把授权的 Tools 暴露给外部 Agent,不负责训练和部署模型。

图 14-7 预测性维护扩展示例模型在平台外训练和部署,授权任务把推理结果作为衍生位号写回 Data,复用已有规则与通知链路。图 14-7 预测性维护扩展示例外部模型通过受控读写边界接入,平台继续复用既有数据、规则与通知能力工程扩展示例 · 非当前默认能力(当前 Compose 不含训练任务 / 模型服务 / 模型卷)训练Data 历史与实时查询授权只读接口特征工程与模型训练算法、框架与版本由项目选择模型制品部署独立推理服务认证 · 限流 · 模型版本管理与平台服务分开部署失败时不影响基础采集链路推理 API(最小接入边界)受控调用推理回写授权任务 / Agent Tool编排读取、推理与回写Data 衍生位号推理结果按位号模型写回现有规则与通知链路阈值判断 · 告警 · 工单最小接入边界:授权读取 Data → 独立推理 → 衍生位号回写 → 复用规则与通知模型服务不可用时停止扩展推理,但不阻断设备采集、数据持久化和确定性规则当前 MCP 只暴露 Tools,不承载实时数据订阅图 14-7 模型服务不可用时停止扩展推理,但不阻断设备采集、数据持久化和确定性规则。
图 14-7 预测性维护扩展示例

安全边界至少包含输入值域校验、推理端点认证与限流、模型版本审计、租户隔离和衍生位号权限。AI 能力不应绕过现有平台治理逻辑。

14.2.6 部署与测试

部署阶段要验证 IoT DC3 当前实际组件能否在容器网络中完整启动,并跑通 Driver 业务注册、位号值上报和位号命令回执。以 2026-08-29 的 987c96d50 快照为准,开发默认栈以 PostgreSQL/TimescaleDB 与 RabbitMQ 为基础,平台服务包括 Gateway、Auth、Manager、Data、Agentic,协议 Driver 按栈启用。可选栈提供其他 Broker、TSDB 与可观测组件;模板没有 Nacos,也没有模型推理容器或模型卷。

当前 Compose 拓扑

yaml
x-app-runtime-env: &app-runtime-env
  DC3_MQ_TYPE: rabbitmq
  DC3_TSDB_TYPE: timescale
  POSTGRES_HOST: dc3-postgres
  RABBITMQ_HOST: dc3-rabbitmq
  CENTER_AUTH_HOST: dc3-center-auth
  CENTER_MANAGER_HOST: dc3-center-manager
  CENTER_DATA_HOST: dc3-center-data
  CENTER_AGENTIC_HOST: dc3-center-agentic

services:
  postgres:
    container_name: dc3-postgres
  rabbitmq:
    container_name: dc3-rabbitmq
  gateway:
    environment: { <<: *app-runtime-env }
  auth:
    environment: { <<: *app-runtime-env }
  manager:
    environment: { <<: *app-runtime-env }
  data:
    environment: { <<: *app-runtime-env }
  agentic:
    environment: { <<: *app-runtime-env }
  mqtt:
    environment: { <<: *app-runtime-env }

启动时使用 podman composedepends_on 只能表达依赖关系,仍需结合 healthcheck 和应用重试等待 PostgreSQL、RabbitMQ 真正就绪。容器之间使用 dc3-postgresdc3-rabbitmqdc3-center-* 等服务名,不能把 localhost 当作其他容器。敏感变量应由 .env 或密钥管理注入,不提交真实凭据。

从零到第一条位号:版本化验收序列

服务起来不算部署成功,整条链路跑通才算。下面的序列对应 987c96d50 快照,并选择内置 Virtual Driver,避免额外依赖 MQTT Broker、Topic 与厂商 payload。生成的 ID 和 Token 必须用前一步真实返回值替换;若仓库 commit 不同,应先阅读该版本的 README 与官方 “First Device: End to End”,不能混用本节命令。这里给的是可核对的验收顺序,不承诺对未来版本逐字复制仍有效。

第 1 步:取代码。

bash
git clone https://github.com/pnoker/iot-dc3.git && cd iot-dc3

预期:得到含 dc3/dc3-center/dc3-driver/、Makefile 与 .env.example 的完整仓库。

第 2 步:起基础设施。

bash
make up-db        # Make 目标默认 podman compose;国内镜像源可用 make up-db-cn

预期:PostgreSQL 与 RabbitMQ 容器运行;首次启动会按 extensions、common、auth、data、manager、history、agentic 的顺序初始化数据库。

第 3 步:验证服务健康。

bash
podman ps
podman exec dc3-postgres psql -U dc3 -d dc3 -c '\dt dc3_auth.*'

预期:dc3-postgresdc3-rabbitmq 状态 Up;能列出 auth schema 的表。宿主机映射端口以 .env 为准(当前 Quick Start 为 PostgreSQL 35432、RabbitMQ AMQP 35672,容器内仍是 5432/5672)。

第 4 步:起平台服务并换取 Token。

bash
source dc3/env/dev.env.sh
make up-dev       # 等价 make up STACK=dev;启动顺序 Auth 先行、Gateway 最后
curl -s -X POST http://localhost:8000/api/v3/auth/token/salt \
  -H 'Content-Type: application/json' -d '{"tenant":"default","name":"dc3"}'

预期:返回 5 分钟有效的 salt;再用 /api/v3/auth/token/generate(携带 salt 与按规则哈希后的密码,哈希规则以官方 Quick Start 为准)换取 12 小时有效的 Token。此后所有请求统一携带 X-Auth-TenantX-Auth-LoginX-Auth-Token 三个头。Gateway 是唯一外部 HTTP 入口(8000 端口),Auth/Manager/Data 的直连端口只用于调试。

第 5 步:确认 Driver 注册并准备设备元数据。

bash
curl -s -X POST http://localhost:8000/api/v3/manager/driver/list \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" -H 'Content-Type: application/json' -d '{}'

预期:返回随栈启动的 Driver 列表——Driver 能出现在这里,说明 14.2.3 所述的 gRPC 业务注册已经成功。随后按同版本官方 Quick Start 创建 profile、位号(例如 Temperature,FLOAT、READ_WRITE)和绑定 Virtual Driver 的设备,记录 deviceId 与 pointId。要改用 MQTT 或其他协议时,应先确认对应 Driver、南向服务和属性模型已启用,再替换本序列中的 Driver 专属步骤。

第 6 步:配置 Virtual Driver 的位号属性并等待自动上报。

从 Virtual Driver 注册的 Point Attribute 列表中取得真实 attributeId,再通过 /api/v3/manager/point_attribute_config/add 为前一步的 deviceIdpointId 写入 configValue。这个配置完成后,Virtual Driver 会产生位号值,无需伪造一套并不存在的通用 MQTT Topic 或 payload。具体请求体以同版本官方 First Device 页面为准;attributeId 是运行时注册结果,不能写死在书中。

第 7 步:REST 查询位号值。

bash
curl -s -X POST http://localhost:8000/api/v3/data/point_value/latest \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" -H 'Content-Type: application/json' \
  -d '{"deviceId":"<DEVICE_ID>","pointId":"<POINT_ID>","page":{"current":1,"size":10}}'

预期:返回该位号的最新记录(rawValue、calValue、numValue、createTime 等字段)——说明“Driver → 消息端口 → Data → 时序存储端口”上行链路贯通;默认适配器对应 RabbitMQ 与 TimescaleDB。

第 8 步:下发一条写命令。

bash
curl -s -X POST http://localhost:8000/api/v3/data/point_command/write \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN" -H 'Content-Type: application/json' \
  -d '{"deviceId":"<DEVICE_ID>","pointId":"<POINT_ID>","value":"26.5"}'

预期:接口立即返回 commandId,命令异步执行;仅 READ_WRITE/WRITE_ONLY 的位号可写,命令默认约 10 秒过期(expireAt),过期未执行即失败。

第 9 步:查命令回执。

bash
curl -s "http://localhost:8000/api/v3/data/point_command_history/get_by_command_id?commandId=<COMMAND_ID>" \
  -H "$H_TENANT" -H "$H_LOGIN" -H "$H_TOKEN"

预期:能看到命令状态与回执;若状态为过期或失败,拿着 commandId 结合回执信息定位(常见原因见 14.3.5)。

第 10 步:看日志收尾。

bash
podman logs dc3-center-data --tail 50
podman logs dc3-driver-virtual --tail 50  # 以当前 Compose 的实际服务名为准

预期:Data 日志出现位号值消费与保存记录,Driver 日志出现注册与读写执行记录;默认栈再对照 RabbitMQ 管理台检查相关队列的积压和死信。换用其他 DC3_MQ_TYPE 时查看该适配器的等价指标。至此,上行与下行两条链路都有可检查的证据。

冒烟与性能测试

表14-3 冒烟测试场景与预期结果

场景验证动作预期结果
服务启动podman compose ps 与 readiness基础设施和所需服务健康
Driver 注册启动一个协议 DriverManager 收到 gRPC 业务注册
数据上报按第 6 步配置 Virtual Driver 位号属性并等待上报;换协议时使用该 Driver 的同版本官方接入方式位号值经消息端口进入 Data,并通过 TsdbStore 写入所选时序库;默认对应 RabbitMQ 与 TimescaleDB
命令下发调用 Data 位号命令接口消息端口投递到目标 Driver,结果回执返回 Data;默认 RabbitMQ 语义可观测
故障恢复暂停所选 Broker 或消费者后恢复该适配器声明的重投、失败隔离、积压与告警行为符合配置

性能测试应分别观察 Driver 采集与锁等待、所选消息适配器的积压和确认状态、Data 消费与批量保存、所选 TsdbStore 的写入与查询延迟。默认栈对应 RabbitMQ 与 TimescaleDB/PostgreSQL;换用其他适配器必须采集其自身指标,不能拿未运行的调优报告替代实测。

图 14-8 IoT DC3 容器化部署架构外部入口经 Gateway 路由四中心,Data 经 RabbitMQ 与 Driver 双向通信,Driver 向 Manager 注册;PostgreSQL 与 RabbitMQ 使用持久化卷,配置由环境变量注入。图 14-8 IoT DC3 容器化部署架构Compose 拓扑聚焦入口、平台服务、消息链与持久化边界外部入口 · Web / Nginx暴露 8080 / 8443Gateway内部端口 8000 · 统一路由入口请求基础设施PostgreSQL用户 · 元数据 · 历史值RabbitMQ位号值 · 命令 · 状态 · 回执dc3net + ENV固定服务名 · 运行时配置持久化卷(PG / MQ)敏感配置由环境变量注入平台服务GatewayREST 路由 · Token 校验Auth鉴权与令牌Manager设备与模型元数据Data最新值与历史值Agentic对话与受控工具Compose DNS 固定服务名路由南向 Driverdriver-mqttMQTT 发布 / 订阅driver-modbusTCP / RTU 轮询与写入driver-opcuaOPC UA 订阅采集gRPC 注册 / 元数据Data ↔ RabbitMQ ↔ Driver:命令 / 数据 / 回执(异步)持久化图 14-8 IoT DC3 当前容器化部署:PostgreSQL 与 RabbitMQ 提供基础设施,Gateway 和四中心组成平台,协议 Driver 按需启用并通过固定服务名与消息契约接入。
图 14-8 IoT DC3 容器化部署架构

14.2.7 可复现实验、验收指标与证据包

部署成功截图只能证明某一时刻服务启动过,不能证明系统在固定负载、故障和安全约束下可重复工作。出版级实战必须让第三方知道运行了什么版本、使用什么数据、怎样施加负载、指标如何计算,以及原始结果在哪里。没有实测的项目可以写设计和方法,但不能用数值冒充结果。

先冻结环境 manifest

每轮实验保存一份不可变 manifest,至少记录:

  • IoT DC3 Git commit/tag、未提交补丁和仓库状态;
  • 容器镜像 digest、Compose 文件及环境变量模板版本;
  • OS、CPU、内存、磁盘、网络、Podman、JDK、Python;
  • DC3_TSDB_TYPEDC3_MQ_TYPE、对应服务版本、Driver 和设备/模拟器固件版本;
  • 模型 Provider、模型 ID、服务版本、Prompt 哈希和 Tool schema 版本;
  • RAG 语料、切分、Embedding、reranker 和索引版本;
  • 测试数据名称、许可、切分和 SHA-256;
  • seed、时区、NTP/时钟条件和运行时间。

密钥和个人数据不得进入 manifest;使用环境变量名、凭据 ID 或脱敏摘要。外部 Provider 无法保证确定性时,记录区域、请求参数和重复次数,不声称 seed 可以完全复现输出。

工作负载必须可重放

“模拟大量设备”无法复现。应固定设备数、每设备位号数、上报频率、payload 大小、读写比例、命令比例、持续时间和预热时间。故障实验还要固定网络延迟/丢包、断网窗口、消费者暂停、Broker/数据库重启时刻、并发 Agent 会话数,以及模型和 Tool 的超时/错误注入比例。

基线也要明确。例如:纯规则、无 AI;Agent 无 RAG;Copilot 只读;受约束 Agent。一次比较只改变主要变量;若硬件、数据或模型同时变化,就不能把差异全部归因于某一组件。

指标字典:先定义分母,再报告数字

表14-4 指标字典与聚合建议

层面指标分母/窗口建议聚合
设备接入注册成功率、稳定在线率、重连时间目标设备/测试窗口比例、P50/P95
数据链路接收率、重复率、乱序率、端到端时延应上报消息/已接收消息比例、P50/P95/P99
命令链路成功率、确认时延、过期率、重复执行率已提交命令比例、P50/P95
存储写入吞吐、写入/查询时延、增长量固定工作负载和窗口rate、P95、字节
可靠性积压恢复、死信、RTO、RPO、数据缺口每个故障场景时长、计数
RAGRecall@k、忠实性、拒答准确率版本化评测集比例与置信区间
Agent任务成功、参数正确、越权、接管、重复副作用golden tasks/攻击集比例、零容忍项
成本每万条遥测、每任务、每成功任务成本明确计费与资源边界币种、token、CPU时

时延起止点必须固定。例如端到端遥测时延可定义为模拟器生成时间到 Data 持久化确认;命令确认时延可定义为 API 接受 Action 到 Driver 回执。不同章节和图表必须使用同一定义。

重复运行和不确定性

每个场景应多次独立运行,报告样本数、中位数或均值、标准差或置信区间,并为长尾报告 P95/P99。预热数据与正式样本分开。LLM 实验需要保存逐任务结果和 trace,避免一次成功回答代表整体能力。

若样本量不足,应明确限制;若指标尚未运行,填 NA(未执行),而不是 00 表示测量后没有发生,NA 表示没有证据,两者含义完全不同。

故障和安全用例

最小实验包至少覆盖:

  1. 重复遥测和乱序时间戳;
  2. Driver 或网络短暂断开后重连;
  3. 所选消息适配器的消费者暂停与积压恢复;
  4. 数据库不可用和恢复;
  5. 用户权限不足与跨租户请求;
  6. 模型超时、Tool 超时和脏返回;
  7. Action 已执行但回执丢失;
  8. 相同 idempotency_key 重放;
  9. 人工接管和 kill switch。

每个用例记录期望状态、实际状态、副作用、日志和恢复结果。设备控制实验应优先使用模拟器、shadow mode 或非安全关键设备,不能为了演示绕过 PLC/SIS 联锁。

出版证据包

建议为每次书稿引用的实验保存:

text
experiments/EXP-14-E2E-01/
├── README.md              # 复现步骤与已知限制
├── manifest.json          # 版本、环境和数据哈希
├── workload.yaml          # 负载与故障参数
├── commands.txt           # 实际执行命令
├── raw/                   # 原始指标、日志与逐任务 trace
├── summary.json           # 指标定义和汇总
├── failures/              # 失败样本与复盘
└── figures/               # 从 raw 生成图表的方法

正文中的实测数字必须反链实验 ID 和原始结果位置。无法公开的数据应提供脱敏样本或可替代生成器,并说明它与真实数据的差异。实验脚本、数据和第三方组件还要标明许可。

实验卡 EXP-14-E2E-01

  • 假设:在固定设备负载和故障窗口下,系统满足事先定义的数据、命令、安全与恢复门槛;
  • 固定项:commit、镜像 digest、硬件、依赖、数据 hash、seed、模型/Prompt/Tool/RAG 版本;
  • 基线:无 AI、只读 Copilot、受约束 Agent;
  • 指标:本节指标字典中实际执行的项目;
  • 阈值:由场景 SLO 和风险分析确定,高风险无审批执行、跨租户越权、重复设备副作用为零;
  • 结果:当前书稿未附真实实验包时全部标记 NA,不预填宣传性数值。

可复现并不意味着不同环境得到完全相同的微秒级结果,而是第三方能够重建主要条件、复算指标、解释差异,并判断结论是否在声明的边界内成立。

图 14-9 可复现实验的五个环节与证据包可复现实验依次冻结 manifest、固定工作负载、定义指标字典、覆盖故障用例、沉淀出版证据包。图 14-9 可复现实验的五个环节与证据包没有实测就标记 NA,不用数值冒充结果 · 0 与 NA 含义不同① 冻结 manifestcommit/tag、镜像 digest、Compose 版本OS/CPU/内存/网络、JDK/PythonPG/RabbitMQ/Driver 版本模型/Prompt/Tool/RAG 版本密钥不入 manifest,用脱敏摘要② 工作负载可重放固定设备数、位号数、上报频率payload 大小、读写比、命令比例持续时间、预热时间故障:延迟/丢包、断网窗口、重启时刻一次比较只改变主要变量③ 指标字典先定义分母,再报告数字设备接入 / 数据链路 / 命令链路存储 / 可靠性 / RAG / Agent / 成本时延起止点必须固定多次独立运行,报告 P50/P95/P99④ 故障与安全用例重复遥测、乱序时间戳断开重连、积压恢复、数据库故障权限不足、跨租户、模型超时回执丢失、重放、人工接管优先模拟器,不绕过 PLC/SIS 联锁⑤ 证据包README · manifestworkload · commandsraw · summaryfailures · figures正文数字反链实验 ID出版证据包结构(experiments/EXP-14-E2E-01/)├── README.md复现步骤与已知限制├── manifest.json版本、环境和数据哈希├── workload.yaml / commands.txt负载与故障参数 / 实际执行命令├── raw/原始指标、日志与逐任务 trace├── summary.json指标定义和汇总├── failures/ + figures/失败样本与复盘 / 图表生成方法图 14-9 可复现实验依次冻结环境 manifest、固定可重放的工作负载、按指标字典先定义分母再报告、覆盖故障与安全用例,最终沉淀为结构化的出版证据包。
图 14-9 可复现实验的五个环节与证据包

从工业软件到 AI 智能体 · 构建面向智能体演进的多协议、云原生、开源工业物联网平台