4. 报警信息采集与数据链路

机器人工作站的报警信息并不只存在于一个地方。控制器内部有事件日志,PLC 侧有报警位与状态字,现场总线承载着安全信号与设备状态,上位机往往通过 OPC UA 或 MQTT 汇聚数据,最终写入数据库供追溯与看板使用。若采集链路设计不当,就会出现“现场已报警、看板无记录”“日志时间与 PLC 时间相差数秒”“同一报警重复入库”等问题。本节围绕一条完整的采集链路,说明各环节的采集对象、接口方式、时间同步与数据落地方法。

一、报警源分层与采集对象

按数据产生的层次,可把报警源分为四类。第一类是控制器日志,例如 KUKA 的 $ALARM 事件、ABB 的 Event Log、FANUC 的 Alarm History,它们包含报警号、文本、发生与消失时刻,是诊断根因最直接的依据。第二类是 PLC 变量,如西门子 S7-1500 的 DB_Alarm.AlarmCode、DB_Alarm.AlarmActive、DB_Status.SafetyOK,这类数据是布尔位或整型字,刷新快、语义明确,适合做实时判断。第三类是现场总线数据,如 PROFINET、EtherCAT、Modbus TCP 上的设备状态字、诊断报文,反映的是通信与从站健康度。第四类是上位汇聚数据,通过 OPC UA 或 MQTT 把前几类数据统一为带时间戳的消息。

采集对象确定后,需要为每个报警点定义唯一标识。推荐采用“设备编号 + 源类型 + 报警号”的命名方式,例如 R1_CTRL_ALM_1024、R1_PLC_ALM_2001、R1_BUS_DIAG_0x0A。这样在后续入库、去重和关联时不会混淆。

二、采集链路与协议选择

一条典型的采集链路是:控制器日志与 PLC 变量先由边缘网关或采集程序读取,再通过 OPC UA 或 MQTT 上传,最后由入库服务写入数据库。选择协议时,若需要结构化、可浏览的地址空间和订阅机制,OPC UA 更合适;若需要轻量、跨网络、易与云平台对接,MQTT 更合适。实际工程中常两者并用:OPC UA 负责设备侧语义化采集,MQTT 负责向上位平台分发。

下面给出一个采集配置示例,使用 Python 的 asyncua 订阅 PLC 报警节点,并把消息发布到 MQTT。示例参数为教学假设,实际地址需按现场组态替换。

from asyncua import Client
import paho.mqtt.client as mqtt
import json, time

# OPC UA 连接与订阅
opc = Client("opc.tcp://192.168.10.20:4840")
await opc.connect()
node = opc.get_node("ns=3;s=DB_Alarm.AlarmCode")

# MQTT 发布
mq = mqtt.Client("gw01")
mq.connect("192.168.10.50", 1883)

async def handler(node, val, data):
    msg = {
        "device": "R1",
        "source": "PLC",
        "alarm_code": int(val),
        "ts": time.time_ns() // 1_000_000,  # 毫秒
        "quality": "good"
    }
    mq.publish("station/R1/alarm", json.dumps(msg))

sub = await node.subscribe(handler, 100)  # 100ms 采样

该示例中,ts 使用采集端本地时间,若采集端与 PLC 未对时,时间戳会偏移。因此时间同步必须单独处理。

三、时间同步:从源头到入库

报警时间同步的目标是让控制器、PLC、网关、数据库使用同一时间基准。常用做法是:PLC 与控制器通过 NTP 或 PTP 与同一时间服务器对时;网关采集时记录“源时间戳”和“采集时间戳”两个字段;入库时以源时间戳为准,采集时间戳用于判断链路延迟。若源设备不支持时间戳,则只能使用采集时间戳,但需在数据中标注 ts_source=gateway,避免误判。

时间同步的检查方法:在 PLC 中读取系统时间,与网关时间比较,偏差应小于 100 ms;若使用 PTP,偏差可控制在 1 ms 以内。若偏差超过阈值,应在报警记录中标记 time_sync=false,提醒运维人员先处理对时问题。

四、数据落地与去重

报警数据入库时,建议使用“报警实例 ID + 发生时间”作为唯一键。报警实例 ID 可由设备编号、报警号和发生时间戳组合生成。对于持续存在的报警,只在状态变化时写入一条记录,避免每 100 ms 重复入库。数据库表可包含以下字段:alarm_id、device_id、source_type、alarm_code、alarm_text、active、ts_source、ts_gateway、quality。其中 active 表示报警当前是否有效,quality 表示数据质量。

下面是一段入库 SQL 示例,使用 PostgreSQL 的 ON CONFLICT 实现去重:

INSERT INTO alarm_record
(alarm_id, device_id, source_type, alarm_code, alarm_text,
 active, ts_source, ts_gateway, quality)
VALUES
('R1_PLC_2001_1710000000000', 'R1', 'PLC', 2001, '安全门打开',
 true, 1710000000000, 1710000000120, 'good')
ON CONFLICT (alarm_id) DO UPDATE
SET active = EXCLUDED.active,
    ts_gateway = EXCLUDED.ts_gateway;

这样,同一报警在持续期间只会更新状态,不会产生重复记录;当报警消失时,再写入一条 active=false 的记录,形成完整的报警生命周期。

五、链路可视化

下图把本节涉及的采集对象、协议路径、时间同步点和入库去重关系集中呈现,便于对照现场组态检查每一段链路是否完整。

报警采集链路与时间同步关系 控制器日志 $ALARM / Event Log 报警号、文本、时刻 PLC 变量 AlarmCode / AlarmActive SafetyOK / 状态字 现场总线 PROFINET / EtherCAT 从站诊断、通信状态 时间基准 NTP / PTP 服务器 统一时钟源 边缘采集网关 / 采集程序 读取控制器日志、订阅 PLC 变量、解析总线诊断;记录 ts_source 与 ts_gateway 生成唯一 alarm_id = 设备编号 + 源类型 + 报警号 + 发生时间戳 对时 OPC UA 地址空间、订阅、语义化节点 适合设备侧结构化采集 MQTT 主题 station/R1/alarm 轻量、跨网络、易对接平台 数据库 alarm_record 唯一键 alarm_id;ON CONFLICT 去重;active 标记报警生命周期 时间同步检查 偏差 < 100 ms 超限标记 time_sync=false

对照上图检查时,重点看三处:一是每个报警源是否都有明确的采集方式;二是时间基准是否覆盖控制器、PLC 和网关;三是入库是否以 alarm_id 去重并保留 active 状态。只要这三处完整,报警数据链路就具备了可追溯、可判断、可恢复的基础。