协议桥接总览
概述
工业现场通常存在多种异构协议: 旧设备用 Modbus RTU, 中控用 OPC UA, 云平台用 MQTT, 办公系统走 REST / SQL。DarraRT 的协议网关 (GatewayManager) 运行在 Service 层, 将 PLC 全局变量区 (M/I/Q/DB) 与外部协议双向映射, 实现一次配置、多向互通。
网关是一个独立子系统, 与 PLC 扫描周期解耦: PLC 只负责更新变量, 网关在自己的工作线程按订阅 / 轮询 / 事件三种模式向外转发, 既不拖慢扫描周期, 也避免网络阻塞引发 PLC 停机。
适用场景
| 场景 | 源端 | 目标端 | 模式 |
|---|---|---|---|
| 旧仪表上云 | Modbus RTU 电表 | MQTT Broker | 采集 → 发布 |
| MES 读写配方 | PLC DB | SQL Server | 拉取 / 写回 |
| SCADA 集成 | PLC 全量 | OPC UA Server | 订阅 |
| REST 控制面板 | PLC 状态 | HTTP 接口 | 查询 / 命令 |
| 跨产线联动 | A 线 PLC | B 线 PLC | MQTT 桥 |
架构图
┌─────────────────────────────────────────┐
│ DarraRT Service (永驻) │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ PLC 变量区 │◄─►│ GatewayMgr │ │
│ │ M/I/Q/DB │ │ (调度/映射) │ │
│ └─────────────┘ └──────┬───────┘ │
│ │ │
│ ┌──────────┬───────┼──────┬────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ [MQTT] [OPC UA] [SQL] [REST] [MB]│
└────────┬─────────┬─────────┬──────┬─────┘
│ │ │ │
Broker Client DB Client
前置条件
- Service 已启动, 版本 ≥ 2.4
- 目标端口开放 (MQTT 1883/8883, OPC UA 4840, SQL 1433, HTTP 80/443)
- 证书齐备 (TLS/mTLS 场景)
- PLC 变量表导出
variables.csv供自动映射
配置 GatewayManager
网关配置位于 <Service>/config/gateway.yaml。顶层三个数组: sources (来源)、sinks (去向)、routes (路由规则)。
# gateway.yaml
sources:
- id: plc_main
type: plc
scan_interval_ms: 100
variables:
- M100.0
- DB1.temperature
- DB1.pressure
sinks:
- id: cloud_mqtt
type: mqtt
url: ssl://mqtt.example.com:8883
client_id: line1_gw
username: plc_line1
password: ${MQTT_PASS}
keepalive: 30
lwt:
topic: factory/line1/status
payload: "offline"
qos: 1
retain: true
- id: mes_sql
type: mssql
connstr: "Server=sql.lan;Database=MES;User=plc;Password=${SQL_PASS}"
table: t_plc_telemetry
batch_size: 200
flush_interval_ms: 1000
routes:
- from: plc_main.DB1.temperature
to: cloud_mqtt
topic: factory/line1/temp
qos: 1
retain: false
transform: "x * 0.1" # 原始 0.1 精度 → 真实摄氏度
filter: "abs(x - last) > 0.5" # 死区 0.5 度
- from: plc_main.*
to: mes_sql
column_map:
timestamp: $now_utc
var_name: $path
value: $value
quality: $quality
方向 / 过滤 / 变换
| 字段 | 说明 | 示例 |
|---|---|---|
direction | push (PLC→外) / pull (外→PLC) / bi (双向) | push |
filter | 布尔表达式, 真才发送 | x > 0 and x < 100 |
transform | 数据变换表达式 | x * 1.8 + 32 (℃→℉) |
deadband | 死区, 小于此阈值不发 | 0.5 |
throttle_ms | 最小发送间隔 | 200 |
数据规范化
网关统一做 3 件事:
- 时间戳: 统一成 UTC 毫秒 (
long类型), 原始不带时间戳的点补当前时间 - 单位: 按变量表
unit字段标注, 上云前强制转换到 SI 单位 - 类型映射:
BOOL/BYTE/WORD/DWORD/INT/DINT/REAL/LREAL/STRING映射到 JSON / SQL / OPC UA 原生类型
// Darra.PLC.Service / Gateway / Normalizer.cs
public sealed class Normalizer
{
public GatewayPoint Normalize(RawPoint raw, VarMeta meta)
{
var value = TypeMapper.Convert(raw.Value, meta.PlcType, meta.TargetType);
var ts = raw.Timestamp ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var unit = UnitConverter.ToSI(value, meta.Unit);
return new GatewayPoint(meta.Path, unit.Value, ts, raw.Quality);
}
}
容错策略
| 故障 | 处理 | 可配置项 |
|---|---|---|
| 网络断开 | 本地环形缓冲区 (默认 10 万条) 暂存 | buffer_size / overflow_policy |
| 重连 | 指数退避, 最大间隔 60s | reconnect_max_ms |
| 消息重发 | QoS 1 / 2 自动, REST 需自定义幂等键 | idempotency_key |
| 去重 | 基于 var_path + timestamp 的布隆过滤器 | dedup_window_ms |
| 反压 | 上游降采样或丢弃, 不可阻塞 PLC | `backpressure: downsample |
缓冲区落盘
长时间断线 (>1 小时) 时, 内存缓冲满了会溢出到磁盘:
sinks:
- id: cloud_mqtt
buffer:
mode: memory_then_disk
memory_size: 100000
disk_path: ./data/mqtt_spool
disk_max_mb: 500
监控与排错
| 指标 | 位置 | 正常值 |
|---|---|---|
gw_queue_depth | /api/gateway/metrics | <1000 |
gw_send_rate | 同上 | 取决业务 |
gw_drop_count | 同上 | 0 |
gw_reconnect_count | 同上 | 偶发 |
gw_latency_p95_ms | 同上 | <200ms |
curl http://localhost:8080/api/gateway/metrics | jq .
排错快速表:
| 现象 | 可能原因 | 措施 |
|---|---|---|
| 云端收不到数据 | filter 过严 / 证书失效 | 查日志 gateway_*.log, 搜 publish fail |
| 数据延迟大 | throttle_ms 太大 / 网络慢 | 降低 throttle, 查网络 RTT |
| SQL 写入慢 | 批量太小 / 索引缺失 | 增大 batch_size, DBA 加索引 |
| 反向命令不生效 | 方向配置为 push | 改为 bi 并设 target_path |
高级技巧
- 冷热分流: 高频变量 (10Hz+) 发 MQTT, 低频配置类走 SQL 心跳
- 边缘聚合: 先走 边缘预处理 压缩 90% 流量再上云
- 影子变量: 网关写入不直接落 PLC, 走影子 DB, PLC 扫描周期里决定是否接受
- 多租户: 一台 Service 多条产线, 用
tenant_id作 MQTT topic 前缀隔离 - 热更新配置:
PATCH /api/gateway/routes不需重启 Service