3. 工业设备软件追踪数据模型

在工业设备软件中,追踪数据模型是诊断平台的数据骨架。它决定了我们能否把一次设备动作、一次控制指令、一次异常停机,还原成一条可读、可查、可关联的调用链。与互联网微服务不同,工业设备软件的追踪对象往往同时包含软件进程、PLC 扫描周期、现场总线通信、边缘网关和云端服务,因此数据模型必须从一开始就考虑跨进程与跨设备的语义一致性。

Trace 与 Span:追踪的基本单元

Trace 表示一次完整的业务或控制事务,例如“启动 3 号注塑机合模动作”或“下发一次温度设定值”。它由一个全局唯一的 traceId 标识,并包含若干 Span。Span 是 Trace 中的一段有名字、有起止时间、有状态的操作,例如“接收 MQTT 指令”“调用运动控制库”“等待 PLC 应答”“写入本地日志”。每个 Span 至少应包含:

  • spanId:Span 自身唯一标识;
  • parentSpanId:父 Span 标识,根 Span 为空;
  • traceId:所属 Trace 标识;
  • name:操作名称,建议采用“设备.模块.动作”命名,如 injection.motion.close_mold;
  • startTime / endTime:起止时间戳,建议统一使用 UTC 毫秒或微秒;
  • status:OK、ERROR、TIMEOUT、CANCELLED;
  • attributes:键值对,如 deviceId、stationId、plcAddress、setpoint、actualValue;
  • events:Span 内的时间点事件,如“收到 ACK”“触发急停”。

在工业场景中,Span 的时间语义需要特别约定。设备控制服务通常同时存在“墙钟时间”和“设备周期时间”。建议以墙钟时间作为统一时间轴,同时用 attribute 记录 PLC 扫描周期号或设备本地 tick,便于后续对齐。若设备时钟与服务器时钟存在偏差,应在采集端记录 clockOffset,而不是在查询时猜测。

上下文传播:跨进程与跨设备的关键

上下文传播解决的是“父 Span 如何把 traceId、spanId 和采样标记传给子 Span”。在单进程内,通常通过线程本地变量或异步上下文传递;跨进程时,需要把上下文注入到消息头、HTTP Header 或工业协议的自定义字段中。常见做法是采用 W3C Trace Context 的 traceparent 格式:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
             版本- traceId - parentSpanId - 采样标志

在工业设备软件中,跨设备传播往往不能依赖 HTTP。例如设备控制服务通过 OPC UA 写入 PLC,再由 PLC 触发边缘网关上报,此时 traceparent 无法直接写入 PLC 寄存器。可行的做法是:在控制服务侧生成 traceId,并将其写入设备侧可观测的“工单号”或“批次号”字段;边缘网关收到该字段后,重新以该 traceId 创建根 Span,并标注 trace.origin=device-control。这样虽然链路中间存在“上下文断点”,但通过业务字段仍可完成关联。

工业设备软件追踪模型:Trace / Span / 上下文传播 Trace: traceId = 4bf92f35...4736 业务事务:3号注塑机合模动作 Root Span injection.cycle.start Child Span mqtt.receive.command Child Span motion.close_mold Child Span plc.write.setpoint Child Span plc.wait.ack 跨进程 / 跨设备上下文传播 设备控制服务 生成 traceId OPC UA / PLC 写入工单号字段 边缘网关 以 traceId 重建根 Span 云端诊断平台 关联日志与指标 traceparent 工单号 traceId 上下文断点通过业务字段关联,保证跨设备链路可还原

工业设备软件中的调用链特点

工业设备软件的调用链与互联网服务有显著差异。第一,实时性约束强:运动控制、PLC 扫描、安全回路往往要求在毫秒级完成,追踪埋点不能引入明显抖动。因此 Span 的创建和上报应尽量异步,或采用环形缓冲区批量上报。第二,调用链深度浅但宽度大:一台设备可能同时有几十个轴、上百个 IO 点,Trace 更像“扇出”结构,而不是长链。第三,存在周期性重复:同一动作每个生产节拍都会执行,需要区分“单次 Trace”和“周期聚合”。第四,设备侧资源受限:PLC 和嵌入式控制器无法运行完整 SDK,只能通过轻量字段或日志间接参与追踪。

追踪数据与日志的关联

追踪和日志不是两套独立数据。推荐在日志中显式写入 traceId 和 spanId,例如:

2025-03-18T10:22:31.482Z INFO [traceId=4bf92f35...4736 spanId=00f067aa0ba902b7]
  device=injection-03 event="mold closed" actualPos=120.4mm setpoint=120.0mm

这样在诊断平台中,既可以按 traceId 拉出完整调用链,也可以从某条错误日志反查其所属 Span 和 Trace。对于无法写入 traceId 的 PLC 日志,可通过时间窗口 + 设备 ID + 工单号进行近似关联,并在结果中标注关联置信度。

实战任务:为设备控制服务设计追踪模型

假设我们有一个设备控制服务,负责接收 MQTT 指令、调用运动控制库、通过 OPC UA 写入 PLC,并等待 PLC 应答。下面给出一份可直接参考的追踪模型设计表,字段和取值均为教学示例,实际项目应按设备协议和平台规范调整。

Span 名称 父 Span 关键 attributes status 判断 关联日志字段
injection.cycle.start — deviceId, stationId, cycleNo OK / ERROR traceId, spanId, cycleNo
mqtt.receive.command injection.cycle.start topic, qos, payloadSize OK / TIMEOUT traceId, spanId, topic
motion.close_mold injection.cycle.start axisId, setpoint, actualValue, durationMs OK / ERROR traceId, spanId, axisId
plc.write.setpoint motion.close_mold plcAddress, value, protocol OK / ERROR traceId, spanId, plcAddress
plc.wait.ack motion.close_mold timeoutMs, retryCount, ackCode OK / TIMEOUT traceId, spanId, ackCode

实现时,控制服务在收到 MQTT 指令后创建根 Span,并把 traceId 写入日志 MDC 和 OPC UA 工单号字段。运动控制库和 PLC 通信模块分别创建子 Span,记录 setpoint、actualValue、ackCode 等关键参数。边缘网关从工单号中提取 traceId,重建根 Span 并继续向下传播。最终在诊断平台中,按 traceId 查询即可看到从指令接收到 PLC 应答的完整链路,同时可下钻到每条日志和每个参数变化。

设计追踪模型时,先确定“一次 Trace 对应什么业务事务”,再确定 Span 的粒度和命名规范。工业设备软件中,Span 粒度不宜过细,否则高频周期动作会产生大量数据;也不宜过粗,否则无法定位到具体轴、具体 IO 或具体协议阶段。
跨设备传播时,不要假设所有设备都支持 W3C Trace Context。对于 PLC、现场总线等无法注入 header 的环节,应使用业务字段(工单号、批次号、cycleNo)作为关联键,并在诊断平台中明确标注“近似关联”,避免误判为精确链路。