跳到主要内容

AB EtherNet/IP 通讯

Darra PLC 通过 EtherNetIPProtocolAdapter 支持 Allen-Bradley (Rockwell) EtherNet/IP 协议, 基于 CIP (Common Industrial Protocol) 实现 Tag 级数据访问。

支持型号

系列型号说明
ControlLogix1756-L6x/L7x/L8x机架式, 大型应用
CompactLogix1769-L1x/L2x/L3x紧凑型
Micro800Micro820/850小型应用
PLC-51785-Lx传统型号 (SLC 兼容)
SLC 5001747-Lx传统型号

协议特点

EtherNet/IP 采用 Tag-Based 寻址方式, 与传统寄存器寻址不同:

特性说明
寻址方式Tag 名称 (符号寻址)
数据封装CIP (Common Industrial Protocol)
传输层TCP 端口 44818
隐式消息I/O 连接 (周期性数据交换)
显式消息消息连接 (读写请求/响应)
Tag 寻址

EtherNet/IP 使用 Tag 名称直接访问变量, 例如 MyMotor.SpeedTemperatureArray[5]。不需要知道内存地址偏移。

Scanner 与 Adapter 模式

EtherNet/IP 定义了两种角色:

角色别名职责示例
Scanner (主站)Originator发起连接, 主动读写 IO 数据PLC、PAC、PC 控制软件
Adapter (从站)Target响应连接, 提供 IO 数据远程 IO 模块、驱动器、阀岛

Darra PLC 默认作为 Scanner 接入 AB PLC 和远程 IO 设备。Darra 也可作为 Adapter 被第三方 Scanner 访问 (需配置, 见下文)。

Scanner 模式

// Darra 作为 Scanner, 读取 AB PLC 的 Tag
var scanner = new EtherNetIPScanner();
scanner.AddTarget(new EIPTargetConfig {
Host = "192.168.1.40",
Slot = 0,
PlcType = EIPPlcType.ControlLogix,
RpiMs = 50, // 50 ms 周期
TransportClass = TransportClass.Class1 // 隐式 I/O 连接
});
await scanner.StartAsync();

Adapter 模式

// Darra 作为 Adapter, 暴露 IO 给外部 Scanner
var adapter = new EtherNetIPAdapter();
adapter.Configure(new EIPAdapterConfig {
Port = 44818,
AssemblyInput = new AssemblyConfig { // 输入 (T→O)
Instance = 100,
Size = 512,
Mapping = "%IX200.0~%IX263.7" // PLC 地址映射
},
AssemblyOutput = new AssemblyConfig { // 输出 (O→T)
Instance = 101,
Size = 512,
Mapping = "%QX200.0~%QX263.7"
},
AssemblyConfig = new AssemblyConfig { // 配置数据
Instance = 102,
Size = 0
}
});

隐式与显式报文

隐式报文 (I/O 连接)

隐式报文是 CIP 的周期性数据交换通道, 使用 UDP 2222 端口, 特点是低延迟、高频率:

属性说明
传输UDP 多播/单播
端口2222 (IP 协议)
周期可配置 RPI (Request Packet Interval), 典型 1~100 ms
方向双向 (输入/输出)
连接建立后持续刷新, 无需重复握手
数据由 Assembly 对象定义

隐式报文格式 (CPF - Common Packet Format):

┌─────────────────┐
│ ENIP Header │ 4 字节
├─────────────────┤
│ CPF Item Count │ 2 字节
├─────────────────┤
│ Item 1: O→T │ 序列号 + 类型 + 数据
├─────────────────┤
│ Item 2: T→O │ 序列号 + 类型 + 数据
├─────────────────┤
│ Pad (可选) │ 对齐
└─────────────────┘

显式报文 (消息连接)

显式报文是按需的请求/响应式通讯, 使用 TCP 44818 端口:

属性说明
传输TCP
端口44818
触发按需调用
方向请求/响应
连接可选 Unconnected 或 Connected
数据CIP 服务包 (读/写/创建/删除)

显式报文适合 Tag 读写、配置、诊断等非周期性操作。

选择指南

场景推荐方式理由
远程 IO 数据隐式报文 (Class 1)周期确定, 低延迟
周期性 Tag 采集隐式报文 (Class 0/1)减少 CPU 开销
配方读写显式报文 (Class 3)按需, 大数据量
设备诊断显式报文 (Unconnected)低频, 无需建连接
报警/事件显式报文 (Connected)可靠传输

CIP 对象模型

CIP 的核心是面向对象的设备模型。每个 CIP 设备都是一个对象集合, 对象由 Class (类), Instance (实例), Attribute (属性) 三层结构描述。

标准 CIP 对象

对象 (Class ID)功能必须
Identity (0x01)设备标识 (厂商、型号、序列号、版本)
Message Router (0x02)消息路由, 将请求分发到各对象
Connection Manager (0x06)连接管理 (Forward Open/Close)
Assembly (0x04)IO 数据打包, 隐式报文的载体
Connection (0x05)连接状态
Register (0x07)注册服务
Parameter (0x0F)设备参数

对象访问示例

// 读取 Identity 对象 (Class 0x01, Instance 1)
var identity = await adapter.ReadObjectAsync(0x01, 1);
Console.WriteLine($"Vendor: 0x{identity.GetAttribute(1):X4}"); // Vendor ID
Console.WriteLine($"Device Type: 0x{identity.GetAttribute(2):X4}");
Console.WriteLine($"Product Code: {identity.GetAttribute(3)}");
Console.WriteLine($"Serial: {identity.GetAttribute(6)}");

// 读取 Assembly 对象 (Class 0x04, Instance 100)
var assembly = await adapter.ReadObjectAsync(0x04, 100);
Console.WriteLine($"Assembly Data: {BitConverter.ToString(assembly.Data)}");

厂商扩展对象

CIP 允许厂商自定义对象 (Class ID ≥ 0x64), 用于设备特有功能:

Rockwell 扩展对象示例:
Class 0x64: 轴对象 (Axis Object)
Class 0x65: 运动控制对象 (Motion Control)
Class 0x6B: 伺服驱动器对象 (Servo Drive)
Class 0x6C: 配置对象 (Configuration)

连接配置

Forward Open 参数

隐式连接建立时, 通过 Forward Open 服务协商参数:

var fwd = new ForwardOpenConfig {
ConnPath = new byte[] { 0x20, 0x04, 0x24, 0x01 }, // Assembly 100
RpiTicks = 50000, // 50 ms (us)
ConnTimeoutMultiplier = 4, // 超时 = 4 × RPI
ConnSize = 500, // 连接数据大小 (字节)
TransportClass = TransportClass.Class1, // 隐式 I/O
Priority = Priority.Low,
Owner = ConnectionOwner.Exclusive // 排他连接
};
var connId = await scanner.ForwardOpenAsync(fwd);

连接类型

Transport Class类型确认重传
Class 0多播 I/O
Class 1点对点 I/O
Class 2点对点 I/O
Class 3显式连接

连接超时行为

连接超时 (Connection Timeout) 可通过 ConnTimeoutMultiplier 配置:

超时时间 = ConnTimeoutMultiplier × RPI
例: RPI = 50 ms, Multiplier = 4 → 超时 = 200 ms

超时后的行为:

  • Scanner 模式: 触发 OnConnectionTimeout 事件, 报告 IOPS Bad
  • Adapter 模式: 输出保持最后值 (可配置安全值)

实时 IO 数据交换

IO 数据流

Darra 作为 Scanner 时的实时 IO 数据流:

扫描周期开始:
1. 从 AB PLC 接收隐式报文 (输入)
2. 解析 Assembly 数据 → 写入 PLC 输入映像 (%IX / %IW)
3. 执行 PLC 用户程序
4. 从 PLC 输出映像 (%QX / %QW) 读取数据
5. 封装为 Assembly 数据 → 发送隐式报文 (输出)
扫描周期结束

Assembly 映射

Assembly 对象是隐式报文的载体, 将多个 IO 数据填充到连续缓冲区:

// 配置 Assembly 映射
scanner.AssemblyMappings = new List<AssemblyMapping> {
new AssemblyMapping {
Instance = 100, // 输入 Assembly
Direction = AssemblyDirection.Input,
PlcStartAddress = "%IX100",
DataLayout = new[] {
new DataField { Offset = 0, Size = 32, PlcType = "BOOL", Count = 32 }, // 32 DI
new DataField { Offset = 4, Size = 16, PlcType = "INT", Count = 8 }, // 8 AI (INT)
new DataField { Offset = 20, Size = 32, PlcType = "DINT", Count = 8 }, // 8 DINT
}
},
new AssemblyMapping {
Instance = 101, // 输出 Assembly
Direction = AssemblyDirection.Output,
PlcStartAddress = "%QX100",
DataLayout = new[] {
new DataField { Offset = 0, Size = 16, PlcType = "BOOL", Count = 16 }, // 16 DO
new DataField { Offset = 2, Size = 16, PlcType = "INT", Count = 4 }, // 4 AO (INT)
}
}
};

小数据优化

Assembly 数据量较小时, 启用 Header 压缩 减少以太网开销:

// 启用 Header 压缩 (仅用于小数据, < 32 字节)
scanner.EnableHeaderCompression = true;
// 压缩后的帧: 去掉 Sequence Number 和 Status 字段

诊断与故障排除

连接诊断

var stats = scanner.GetConnectionStats();
Console.WriteLine($"状态: {stats.ConnectionState}"); // Running / Idle / Timeout
Console.WriteLine($"RPI: {stats.ActualRpiUs} μs"); // 实际 RPI
Console.WriteLine($"输入计数: {stats.InputPacketCount}");
Console.WriteLine($"输出计数: {stats.OutputPacketCount}");
Console.WriteLine($"丢包: {stats.LostPacketCount}");
Console.WriteLine($"IOPS: {stats.IopsProvider}"); // Good / Bad / Uncertain

连接状态机

         ┌──────────────┐
│ CLOSED │
└──────┬───────┘
│ Forward Open
┌──────┴───────┐
┌───►│ Establishing │
│ └──────┬───────┘
│ │
│ ┌──────┴───────┐
│ │ RUNNING │◄──── 正常数据交换
│ └──────┬───────┘
│ │ 超时 / 断线
│ ┌──────┴───────┐
│ │ TIMED_OUT │──► 重连 (自动/手动)
│ └──────┬───────┘
│ │ Forward Close
│ ┌──────┴───────┐
└────┤ CLOSED │
└──────────────┘

诊断 API

// 获取设备诊断
var diag = adapter.GetDiagnosis();
Console.WriteLine($"Identity: {diag.Vendor} {diag.DeviceType}");
Console.WriteLine($"ENIP Session: 0x{diag.SessionHandle:X8}");
Console.WriteLine($"连接数: {diag.ActiveConnectionCount}");

// 逐个连接诊断
foreach (var conn in diag.Connections)
{
Console.WriteLine($" O→T: {conn.OTPacketRate} pkt/s, 延迟={conn.OTLatencyUs} μs");
Console.WriteLine($" T→O: {conn.TOPacketRate} pkt/s, 延迟={conn.TOLatencyUs} μs");
Console.WriteLine($" IOPS: {conn.IopsProvider}/{conn.IopsConsumer}");
}

常见问题

症状原因处理
Register Session 失败端口 44818 被防火墙阻挡检查防火墙规则
Forward Open 失败 0x02连接数已达上限减少连接数或增大控制器连接上限
Read Tag 返回 0x04Tag 名称拼写错误或大小写不对ReadSymbolList() 验证 Tag 名
隐式 I/O 无数据UDP 2222 被防火墙阻挡开放 UDP 2222 端口
连接频繁断开 (IOPS Bad)RPI 太短或网络抖动增大 RPI 或超时倍数
大 UDT 读取失败CIP PDU 超出 500 字节改用 ReadFragmentedAsync
数据交换延迟 > RPI 的 2 倍网络负载过高检查交换机 QoS, 增加带宽
Assembly 长度不匹配控制器侧和数据侧配置不一致检查 GSDML/EDS 或手动的 Assembly 配置
IOPS 显示 Uncertain数据质量不可靠, 连接不稳定检查物理层, 确认网线/交换机正常
多播风暴大量隐式多播连接使用单播模式 (UDP 单播替代多播)

性能优化建议

场景优化
多个小 Tag 频繁读取合并为多服务包 (Multiple Service Packet), 减少往返
大 UDT 或长数组用分段读 (Read Fragmented)
周期性 Tag 采集用隐式 I/O 连接替代显式请求
ControlLogix 连接持久化 Forward Open, 避免反复建连
符号表初始化首次连接后缓存符号表, 后续验证 Tag 名
高吞吐采集启用连接分片, 增大 CIP PDU 大小

连接参数

Forward Open 时需要协商:

参数常见值
RPI (Request Packet Interval)10 ms ~ 100 ms, 周期数据间隔
Transport ClassClass 3 (显式), Class 1 (隐式)
PriorityLow / High
Timeout Multiplier4 × RPI = 超时
Connection Size500 字节默认

路由

CIP 支持通过消息路由器访问子网上的设备, 路径 (Path) 格式:

ControlLogix 机架里的 DCS 板卡 (插槽 4, 端口 3 到子网 1 的 Node 5):
Path = [01 04] [04 01] [01 05]
端口 1, 槽 4 端口 4, 子 1 端口 1, 地址 5

常见场景不需手写路径, 适配器的 Slot 参数已覆盖 "CPU 在本机架插槽 N" 这个最常用情况。

与 ControlLogix 的优化

  1. 用 Tag 名直接访问: 不要按地址偏移, CIP 本身用 Tag
  2. 读取符号表: 首次连接后用 ReadSymbolList() 缓存全部 Tag 名, 后续验证
  3. 大 UDT 分片: 单 UDT > 500B 时用 Read Fragmented
  4. 多服务批量: 小 Tag 合并到一个请求
  5. 连接持久化: 持有 Forward Open, 避免反复建连

与 Micro800 的差异

Micro800 系列走 符号化路由, 需要启用 "CIP Generic" 消息模式, 且不支持全部 CIP 服务。建议:

  • 只用简单 Tag (无 UDT 嵌套)
  • 禁用连接分片
  • 用显式消息, 不用周期 I/O

错误码

状态码含义
0x00成功
0x01连接失败 (通用)
0x02资源不可用 (连接数上限)
0x04无效路径
0x05无效路径 (段)
0x08无效服务 (不支持的命令)
0x0C对象状态冲突
0x0E属性不可设
0x10设备状态冲突
0x11响应数据过大
0x13请求数据不足
0x1C属性列表包含无效属性
0x1E服务出错 (通用)
0x26Path 大小无效

排错

症状处理
Register Session 失败端口 44818 被挡, 或设备不支持 ENIP
Forward Open 失败 0x02连接数上限
Read Tag 返回 0x04Tag 名拼写错或大小写
STRING 读乱码需要按 LEN+DATA 结构读, 不能直接当字节
连接频繁断开RPI 太小, 或网络抖动; 增大超时倍数
隐式 I/O 无数据UDP 2222 被防火墙阻
Assembly 数据错位输入/输出 Assembly 映射配置不对应