欢迎光临
我们一直在努力

DNP3 系列(十三):工程实战——点表设计、网络部署与协议网关

核心目标:具备实际工程部署和问题排查能力,掌握点表设计、北美 SCADA 网络架构、DNP3 ↔ IEC 104 / Modbus 网关设计,能在北美项目与国内调度体系间架桥。

前置知识:Part 6(对象组速查)、Part 8(机制)、Part 9(控制)、Part 11(开发)。


13.1 系统集成流程

DNP3 项目(无论北美还是跨境)的标准交付流程:

需求确认 → 点表设计 → 通信联调 → 对点测试 → 投运验收
│ │ │ │ │
│ 设备型号/协议能力 │ 主站↔从站 │ 双端签字
│ 对象组/变化选择 │ 控制/事件/时钟 │
▼ ▼ ▼ ▼ ▼
[设备接入] [点表模板] [联调记录] [对点表] [验收报告]

阶段关键活动输出物
需求确认 设备类型、数据量、实时性要求、冗余要求 功能需求表
点表设计 对象组/变化/索引/事件类规划(13.2) 点表(Excel/CSV)
通信联调 链路、轮询、命令、时钟逐项验证 联调记录
对点测试 逐点核对值与品质 对点表(双端签字)
投运验收 稳定性观察、性能测试 验收报告

工程铁律:点表是协议工程的"契约"。投运后改点表 = 重新对点,成本极高。设计阶段宁可多花时间,不要漏点、错类。


13.2 点表设计与对象规划

13.2.1 对象组/变化选择策略

数据类型推荐对象变化选择理由
遥信(单点) G1 V1(带品质) 标准、省带宽
遥信(开关位置) G3 V1(双点带品质) 表达中间态/故障(Part 6)
遥测 G30 V3(32 位浮点) 直接工程量,TCP 链路首选
遥测(大量低速串口) G30 V1(16 位定标) 带宽减 40%,需标度表
电度/累计 G20 V0(32 位无符号) 标准电度
遥控 G12(CROB) V1 唯一标准遥控对象
遥调 G41 V3(浮点) 工程量设点

13.2.2 索引分区规划

索引分区(示例,0x0000-0xFFFF):
0x0000-0x0FFF 遥信(4096 点)
0x1000-0x1FFF 遥测(4096 点)
0x2000-0x2FFF 电度/累计(4096 点)
0x3000-0x3FFF 遥控输出(4096 点)
0x4000-0x4FFF 遥调输出(4096 点)

原则:
– 预留扩容区(每类留 30% 空档)
– 固定分区便于对点与排障(看到索引即知类型)
– 事件对象与静态对象共享索引(同一物理点)

13.2.3 事件类与死区配置

事件类型事件类死区建议
保护动作/跳闸 Class 1 —
遥控回执 Class 1 —
一般遥信变位 Class 2 —
遥测越限 Class 3 额定值的 1%-2%
电度变化 Class 3 与抄表周期匹配

死区(G32 Deadband)是遥测事件产生的"闸门":死区太小 → 事件风暴(缓冲溢出);太大 → 重要变化丢失。工程上按测量精度 + 控制需求的 2-3 倍设置。

13.2.4 点表模板示例

# dnp3_point_map.csv —— 索引,类型,对象组,变化,事件类,描述,标度,偏移,死区
0,BI,G1,V1,Class2,断路器分闸状态,1,0,-
1,BI,G1,V1,Class2,隔离开关位置,1,0,-
1000,AI,G30,V3,Class3,线路电压 Ua,1,0,1.0
1001,AI,G30,V3,Class3,线路电流 Ia,1,0,0.5
2000,CTR,G20,V0,Class3,正向有功电度,1,0,10
3000,CROB,G12,V1,-,断路器分/合闸遥控,-,-,-
4000,AO,G41,V3,-,有功功率设定,-,-,-

点表应至少包含:索引、类型、对象组/变化、事件类、描述、标度/偏移(定标时)、死区。自动化工具(Python/Excel 宏)从点表生成主站/从站两侧配置,避免手抄错误。


13.3 网络架构与部署

13.3.1 北美 SCADA 典型架构

#mermaid-svg-0L6jCHXkKx0wbY2t{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0L6jCHXkKx0wbY2t .error-icon{fill:#552222;}#mermaid-svg-0L6jCHXkKx0wbY2t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0L6jCHXkKx0wbY2t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0L6jCHXkKx0wbY2t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0L6jCHXkKx0wbY2t .marker.cross{stroke:#333333;}#mermaid-svg-0L6jCHXkKx0wbY2t svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0L6jCHXkKx0wbY2t p{margin:0;}#mermaid-svg-0L6jCHXkKx0wbY2t .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster-label text{fill:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster-label span{color:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster-label span p{background-color:transparent;}#mermaid-svg-0L6jCHXkKx0wbY2t .label text,#mermaid-svg-0L6jCHXkKx0wbY2t span{fill:#333;color:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t .node rect,#mermaid-svg-0L6jCHXkKx0wbY2t .node circle,#mermaid-svg-0L6jCHXkKx0wbY2t .node ellipse,#mermaid-svg-0L6jCHXkKx0wbY2t .node polygon,#mermaid-svg-0L6jCHXkKx0wbY2t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0L6jCHXkKx0wbY2t .rough-node .label text,#mermaid-svg-0L6jCHXkKx0wbY2t .node .label text,#mermaid-svg-0L6jCHXkKx0wbY2t .image-shape .label,#mermaid-svg-0L6jCHXkKx0wbY2t .icon-shape .label{text-anchor:middle;}#mermaid-svg-0L6jCHXkKx0wbY2t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0L6jCHXkKx0wbY2t .rough-node .label,#mermaid-svg-0L6jCHXkKx0wbY2t .node .label,#mermaid-svg-0L6jCHXkKx0wbY2t .image-shape .label,#mermaid-svg-0L6jCHXkKx0wbY2t .icon-shape .label{text-align:center;}#mermaid-svg-0L6jCHXkKx0wbY2t .node.clickable{cursor:pointer;}#mermaid-svg-0L6jCHXkKx0wbY2t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0L6jCHXkKx0wbY2t .arrowheadPath{fill:#333333;}#mermaid-svg-0L6jCHXkKx0wbY2t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0L6jCHXkKx0wbY2t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0L6jCHXkKx0wbY2t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0L6jCHXkKx0wbY2t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0L6jCHXkKx0wbY2t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0L6jCHXkKx0wbY2t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster text{fill:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t .cluster span{color:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0L6jCHXkKx0wbY2t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0L6jCHXkKx0wbY2t rect.text{fill:none;stroke-width:0;}#mermaid-svg-0L6jCHXkKx0wbY2t .icon-shape,#mermaid-svg-0L6jCHXkKx0wbY2t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0L6jCHXkKx0wbY2t .icon-shape p,#mermaid-svg-0L6jCHXkKx0wbY2t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0L6jCHXkKx0wbY2t .icon-shape .label rect,#mermaid-svg-0L6jCHXkKx0wbY2t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0L6jCHXkKx0wbY2t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0L6jCHXkKx0wbY2t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0L6jCHXkKx0wbY2t :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

厂站侧

通信网

主站侧

DNP3 over TCP

DNP3 TCP/串口

SCADA 主站

前置机 FEPDNP3 Master

路由器 / 调度数据网

路由器 / 专线 / 4G / 卫星

RTU / 通信管理机DNP3 Outstation

保护 IED

测控装置

智能电表

13.3.2 传输介质选择

介质典型场景波特率/带宽注意
以太网 TCP(端口 20000) 厂站-主站、站内 10/100M NAT/防火墙需放行
EIA-485 串口 站内 RTU 总线 9600-19200 半双工,轮询周期预算
4G/无线 偏远站点 受限 注意 Keep-Alive 与休眠唤醒
卫星 管道/偏远 很低 最小化报文,慎用 Unsolicited

13.3.3 部署参数速查

参数建议值说明
事件轮询周期 1-5 s TCP 取 1s,串口按总线折算
完整性轮询周期 5-15 min 兜底同步全量状态
响应超时 2-5 s 按最大帧时延 + 余量
重连间隔 1-30 s(退避) TCP 断链自动恢复
Keep-Alive 60 s 检测静默链路
广播 慎用 仅校时/复位等

13.4 Wireshark 现场排障

13.4.1 排障决策树

故障现象:数据不上送 / 轮询无响应
├── 链路层通吗?
│ ├── 否:抓包看 TCP 握手 / 串口物理层
│ │ ├── TCP 三次握手失败 → 防火墙/端口/地址
│ │ └── 串口无帧 → 波特率/线序/地址模式(1B vs 2B)
│ └── 是:看链路层应答
│ ├── NACK → CRC 错误/地址不匹配
│ └── 无应答 → DFC 卡死/从站忙
├── 应用层有响应吗?
│ ├── 否:请求超时 → 检查 SEQ/功能码/对象编码
│ └── 是:看 IIN
│ ├── CLASS1/2/3 置位 → 有事件待读,检查事件轮询
│ ├── NEED_TIME 常置 → 时钟同步未完成
│ ├── LOCAL_CONTROL → 就地/远方切换未远方
│ └── EVENT_BUFFER_OVERFLOW → 缓冲溢出,做完整性轮询
└── 数据有了但值不对?
├── 品质位异常 → 检查采集/标度(Part 6)
└── 浮点乱值 → 变化 V3 vs V1 解析错位

13.4.2 七类典型故障速查

#现象报文特征根因处理
1 链路建立失败 TCP 通但链路层 NACK 地址不匹配/地址模式不一致 核对 LocalAddr/RemoteAddr 与 1B/2B
2 轮询无数据 RESPONSE 正常但无对象 事件类分配错误/对象组不匹配 查 ASSIGN CLASS 与 G60 请求
3 时间总不对 IIN.NEED_TIME 常置 校时流程失败 查 DELAY MEASURE + G50 下发
4 遥控失败 SELECT 被拒/超时 SBO 时序/控制码/就地模式 查 G12 控制码与 LOCAL_CONTROL
5 Unsolicited 丢失 FC=130 反复重发 主站未回 CONFIRM 实现 CONFIRM(功能码 0)
6 浮点数据异常 数值巨大/乱码 变化编号解析错位 核对 G30V1 vs V3
7 文件传输中断 OPEN 成功但分块失败 块大小协商不一致/认证失败 查 G71 块号连续性与 AUTHENTICATE

13.4.3 现场排障脚本

# triage.py —— 抓包 + 统计 + 快速定位(依赖 pydnp3 测试台)
import subprocess, sys
from pydnp3 import opendnp3, openpal, asiopal, asiodnp3

def capture_and_analyze(interface="eth0", duration=30, remote="10.0.0.1"):
# 1. tshark 抓取 DNP3 报文
p = subprocess.Popen(["tshark", "-i", interface, "-f", "tcp port 20000",
"-w", "capture.pcapng", "-a", "duration:%d" % duration])
p.wait()
# 2. 统计链路层 ACK/NACK
out = subprocess.check_output(
["tshark", "-r", "capture.pcapng", "-Y", "dnp3", "-T", "fields",
"-e", "dnp3.ctrl.func"]).decode()
funcs = [int(x) for x in out.split() if x.isdigit()]
from collections import Counter
print("链路功能码统计:", Counter(funcs))
# 3. 检查 IIN 置位情况(NEED_TIME / 溢出)
out2 = subprocess.check_output(
["tshark", "-r", "capture.pcapng", "-Y", "dnp3.app.iin",
"-T", "fields", "-e", "dnp3.app.iin"]).decode()
print("IIN 采样:", out2[:200])


13.5 协议网关:DNP3 ↔ IEC 104

13.5.1 为什么需要网关

北美存量设备(RTU/IED)只支持 DNP3,而国内/国际调度体系用 IEC 104。跨境集控、北美设备接入国内平台时,必须有双向语义转换。

13.5.2 网关架构

#mermaid-svg-FYIj3yE8TsRTdWSt{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FYIj3yE8TsRTdWSt .error-icon{fill:#552222;}#mermaid-svg-FYIj3yE8TsRTdWSt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FYIj3yE8TsRTdWSt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FYIj3yE8TsRTdWSt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FYIj3yE8TsRTdWSt .marker.cross{stroke:#333333;}#mermaid-svg-FYIj3yE8TsRTdWSt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FYIj3yE8TsRTdWSt p{margin:0;}#mermaid-svg-FYIj3yE8TsRTdWSt .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster-label text{fill:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster-label span{color:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster-label span p{background-color:transparent;}#mermaid-svg-FYIj3yE8TsRTdWSt .label text,#mermaid-svg-FYIj3yE8TsRTdWSt span{fill:#333;color:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt .node rect,#mermaid-svg-FYIj3yE8TsRTdWSt .node circle,#mermaid-svg-FYIj3yE8TsRTdWSt .node ellipse,#mermaid-svg-FYIj3yE8TsRTdWSt .node polygon,#mermaid-svg-FYIj3yE8TsRTdWSt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FYIj3yE8TsRTdWSt .rough-node .label text,#mermaid-svg-FYIj3yE8TsRTdWSt .node .label text,#mermaid-svg-FYIj3yE8TsRTdWSt .image-shape .label,#mermaid-svg-FYIj3yE8TsRTdWSt .icon-shape .label{text-anchor:middle;}#mermaid-svg-FYIj3yE8TsRTdWSt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FYIj3yE8TsRTdWSt .rough-node .label,#mermaid-svg-FYIj3yE8TsRTdWSt .node .label,#mermaid-svg-FYIj3yE8TsRTdWSt .image-shape .label,#mermaid-svg-FYIj3yE8TsRTdWSt .icon-shape .label{text-align:center;}#mermaid-svg-FYIj3yE8TsRTdWSt .node.clickable{cursor:pointer;}#mermaid-svg-FYIj3yE8TsRTdWSt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FYIj3yE8TsRTdWSt .arrowheadPath{fill:#333333;}#mermaid-svg-FYIj3yE8TsRTdWSt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FYIj3yE8TsRTdWSt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FYIj3yE8TsRTdWSt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FYIj3yE8TsRTdWSt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FYIj3yE8TsRTdWSt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FYIj3yE8TsRTdWSt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster text{fill:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt .cluster span{color:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FYIj3yE8TsRTdWSt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FYIj3yE8TsRTdWSt rect.text{fill:none;stroke-width:0;}#mermaid-svg-FYIj3yE8TsRTdWSt .icon-shape,#mermaid-svg-FYIj3yE8TsRTdWSt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FYIj3yE8TsRTdWSt .icon-shape p,#mermaid-svg-FYIj3yE8TsRTdWSt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FYIj3yE8TsRTdWSt .icon-shape .label rect,#mermaid-svg-FYIj3yE8TsRTdWSt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FYIj3yE8TsRTdWSt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FYIj3yE8TsRTdWSt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FYIj3yE8TsRTdWSt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

调度侧

网关

北美侧

DNP3

IEC 104

北美 RTUDNP3 Outstation

DNP3 Master 侧

点表映射引擎双向 + 品质/时间转换

IEC 104 Server 侧

国内调度主站IEC 104 Client

13.5.3 对象映射表

DNP3 对象IEC 104 TI转换要点
G1V1/G3V1 遥信 M_SP_NA_1 / M_DP_NA_1 品质位映射(见下)
G2 事件(CTO) M_SP_TB_1(SOE) CTO+相对时间 → CP56Time2a
G30V3 遥测 M_ME_NC_1 浮点直传
G20V0 电度 M_IT_NA_1 32 位直传
G12 CROB 遥控 C_SC_NA_1 / C_DC_NA_1 SBO 两阶段 → S/E 位
G41 遥调 C_SE_NC_1 WRITE → 激活语义
G50 时钟同步 C_CS_NA_1 时间基准换算(1970 vs 1984)

13.5.4 关键转换细节

品质位映射:

DNP3 品质位 → IEC 104 QDS:
ONLINE(bit0) → IV=0(有效)
RESTART(bit1) → 同步后清除(等效)
COMM_LOST(bit2) → IV=1(无效)建议
LOCAL_FORCED(bit4) → 保持,映射到 QDS 保留位或丢弃
OVERFLOW(bit5) → OV=1(溢出)

IEC 104 QDS → DNP3 品质位:
IV=1(无效) → 清 ONLINE(离线)
OV=1(溢出) → 置 OVERFLOW

时间基准换算(易错点):

DNP3 绝对时间:自 1970-01-01 起毫秒(48 位)
CP56Time2a: 1984 年基准 + 毫秒(7 字节,含星期)

转换:先解出绝对时刻(Unix ms),再编码为 CP56Time2a
Unix_ms = DNP3_ms(直接等价,基准相同都是 1970)?
注意:CP56Time2a 的"年份"字段是 0-99 循环(1984 起)
跨世纪(2084 年)需特殊处理——工程上罕见但要知道

事件语义转换:

DNP3 侧:主站轮询 Class 1/2/3 拉事件(或 Unsolicited)
IEC 104 侧:突发(COT=3)上送 + 总召唤(COT=20)响应

网关策略:
DNP3 事件 → IEC 104 突发(立即转发)
IEC 104 总召唤 → DNP3 完整性轮询(Class 0)
DNP3 Unsolicited → 网关立即转 IEC 104 突发

13.5.5 网关代码骨架

# gateway.py —— DNP3 ↔ IEC 104 双向网关(伪代码骨架,双栈需两个协议栈实例)
class Dnp3Iec104Gateway:
def __init__(self, point_map):
self.point_map = point_map # 双向映射表
self.dnp3_master = Dnp3Master() # 对接北美 RTU(Part 11 模式)
self.iec104_server = Iec104Server() # 对接调度主站(lib60870 等)

# DNP3 事件 → IEC 104 突发
def on_dnp3_event(self, index, value, quality, timestamp):
ti, ioa = self.point_map.dnp3_to_iec104(index)
self.iec104_server.send_float(ioa, value,
quality_to_qds(quality),
timestamp_to_cp56(timestamp))

# IEC 104 遥控 → DNP3 CROB
def on_iec104_command(self, ioa, command):
index, op = self.point_map.iec104_to_dnp3(ioa)
self.dnp3_master.select_and_operate(index, op) # SBO


13.6 协议网关:DNP3 ↔ Modbus / OPC UA

13.6.1 DNP3 ↔ Modbus

维度DNP3 侧Modbus 侧
数据 G1/G30 对象 线圈/保持寄存器
事件 Class 轮询/Unsolicited 无事件机制(轮询拉取)
命令 CROB SBO 写线圈/寄存器
  • 典型场景:Modbus 存量 PLC 接入 DNP3 主站
  • 转换要点:事件语义由网关"比较变化"模拟(轮询 Modbus → 与上次比较 → 生成 DNP3 事件)

13.6.2 DNP3 ↔ OPC UA

  • OPC UA 是信息模型协议(类似 IEC 61850 思路),映射需定义"节点树 ↔ 对象组/索引"
  • 参考 IEEE 1815.1(DNP3 ↔ IEC 61850 映射)的设计思路
  • 适合:工厂-电网融合场景(能源管理系统)

13.7 典型工程案例

案例 1:北美配电站 RTU 接入 SCADA 主站

场景:美国某配电站,RTU(DNP3 Outstation)接入调度 SCADA(DNP3 Master)
配置:TCP 20000,完整性轮询 10min + 事件轮询 1s
点表:256 遥信(G1V1)+ 128 遥测(G30V3)+ 32 电度(G20V0)+ 16 遥控(G12 SBO)
要点:Unsolicited 使能(Class 1 跳闸信号秒级上送);双主站冗余(主备分工)

案例 2:光伏电站对接北美运营商 DNP3 接口

场景:中国厂商光伏逆变器 → 站控网关 → 北美运营商 SCADA
难点:运营商要求 DNP3 特定对象组(G30V3 浮点 + G12 CROB 并网/离网)
要点:网关侧严格按运营商点表模板配置;对点测试逐点验收

案例 3:DNP3 ↔ IEC 104 网关跨境集控

场景:北美变电站数据接入国内集团调度中心
架构:北美侧 DNP3 Master(网关)↔ 国内 IEC 104 Server
要点:品质位/时间基准转换(13.5.4);跨洋链路延迟对事件时标的影响补偿

案例 4:EIA-485 串口总线 32 台 RTU 轮询优化

场景:站内 32 台 RTU 共享一条 19200 bps 串口总线
计算:单帧 ~50 字节 ≈ 25ms;单台轮询 2 帧 ≈ 60ms
一轮全部轮询 ≈ 2s → 事件轮询周期只能 2s+ 或分区调度
优化:Unsolicited 上报关键事件(Class 1),轮询降频;总线分段

案例 5:文件传输远程固件升级

场景:偏远 RTU 通过 DNP3 文件传输(G70-G74)远程升级固件
流程:OPEN → 分块 GET → AUTHENTICATE → CLOSE → 重启确认版本
要点:断点续传;升级失败回滚(保留旧固件分区);升级期间禁止遥控


小结与导航

本篇覆盖工程交付全链路:

  • 集成流程 —— 需求→点表→联调→对点→投运
  • 点表设计 —— 对象/变化/索引/事件类/死区,模板化
  • 网络部署 —— 北美 SCADA 架构、介质选型、参数速查
  • 现场排障 —— 决策树 + 七类典型故障 + tshark 脚本
  • 协议网关 —— DNP3↔IEC 104 完整设计(映射/品质/时间/事件)
  • 工程案例 —— 5 个真实形态案例
  • 下期预告

    [Part 14:安全认证与演进] 为系列收官:

    • DNP3 威胁模型与攻击场景
    • SAv5 安全认证机制(挑战-响应、会话密钥)
    • TLS 与安全部署清单
    • DNP3 vs IEC 104 vs Modbus vs OPC UA 全面对比
    • DNP3 在新能源时代的演进

    参考标准

    • IEEE 1815.1: Mapping DNP3 to IEC 61850(网关映射参考)
    • DNP Users Group Volume 7: Network/Transport Mappings
    赞(0)
    未经允许不得转载:171主机测评 » DNP3 系列(十三):工程实战——点表设计、网络部署与协议网关
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址