CANopen 驱动
概述
CANopen — CAN 总线之上的 EN 50325-4 / CiA 301 标准应用层(普遍用于电机驱动器 / 编码器 / IO 模块)。2026-07 原生重写 — 已移除旧的 Apache PLC4j canopen 委派方式(PLC4X CANopen 仅支持本地 SocketCAN transport,无法通过 TCP 承载,在没有物理 CAN 总线的边缘侧 connect 会失败)。现在使用 plantpulse-plc-protocol 中自研的 SDO expedited upload 实现(CanopenSdoClient)+ transport 抽象(CanTransport)——与 AB-ETH / BACnet 相同的原生模式。
| 项目 | 值 |
|---|---|
opc_type | CANOPEN |
| 实现类 | plantpulse.driver.protocol.canopen.CANOpenDriver |
| 库 | 自研原生(plantpulse-plc-protocol 的 canopen — SDO expedited upload) |
| 继承 | BaseProtocolDriver(非 PLC4j) |
getProtocol() | "canopen:tcp"(沿用旧 PLC4X scheme 表记 — 兼容日志/诊断) |
| 默认端口 | 20200(CAN-over-TCP 网关) |
| 状态 | 稳定(SDO expedited 轮询) |
| read | OK(SDO expedited upload,值 ≤4 字节) |
| write | ❌(只读 — 未实现 SDO download) |
| 安全 | 无 |
默认 transport(tcp)与 CAN ↔ TCP 网关 之后的 CANopen 节点通信。它将 CAN 帧以 canId(4B BE) + dlc(1B) + data[dlc] 自有的二进制帧格式承载到 TCP 上(无握手/总线名协商 — 与模拟器采用相同帧格式)。transport=socketcan(本地 can0)目前为桩实现(需要 AF_CAN 原生绑定/JNI),因此 connect 会失败 —— 物理 CAN 接口路线为后续工作。
类结构
BaseProtocolDriver
└── CANOpenDriver — connect() 에서 CanTransport(tcp/socketcan) + CanopenSdoClient 생성
read 失败(Abort/超时/套接字)会使套接字失效(connected=false + close),促使边缘侧重新连接。
wire 格式摘要(自研实现 — SDO expedited upload)
- 请求:COB-ID
0x600+nodeId,data[0x40, index LE(2B), subindex, 0×4]。 - 响应:COB-ID
0x580+nodeId,command0x43(4B)/0x47(3B)/0x4B(2B)/0x4F(1B)——值为 little-endian。0x80= Abort(代码 u32 LE)→ 通信异常。 - 不支持 segmented / block transfer(值 >4B,例如长字符串)——对标量标签轮询而言,expedited 已足够。
OPC 注册选项
| 字段 | 含义 | 默认 |
|---|---|---|
opc_agent_ip | CAN-TCP 网关 IP | 192.168.1.20 |
opc_agent_port | 网关 TCP 端口 | 20200 |
options.read-timeout | SDO 响应超时(ms) | 3000 |
options.transport | tcp / socketcan(桩实现) | tcp |
options.can-interface | socketcan 模式接口名 | can0 |
旧表单/文档中的 node-id 选项已废弃 —— 节点 ID 现在是标签地址的第 1 个字段(见下文)。request-timeout/heartbeat 选项原生实现同样不接受(超时使用 read-timeout)。
标签 plc_address 格式(新约定)
<nodeId>:<index>:<subindex>[:<TYPE>]
| 字段 | 含义 | 表记 |
|---|---|---|
nodeId | CANopen 节点 id(1..127) | 十进制 |
index | 对象字典 index(0..0xFFFF) | 0x 前缀十六进制(0x6041)或十进制 |
subindex | 子 index(0..255) | 0x 前缀十六进制或十进制 |
TYPE(可选) | LE 字节解释类型 | 省略时按 data_type 推断 → 若也没有则为 UINT16 |
| 表记 | 含义 |
|---|---|
1:0x6041:0:UINT16 | 节点 1,DS402 Statusword |
3:0x2000:0:REAL | 节点 3,制造商区域 float32 |
1:0x6064:0:INT32 | Position actual value(32-bit signed) |
5:8192:1 | 节点 5,index 十进制 8192(=0x2000),省略 TYPE → UINT16 |
TYPE 记号(含别名)
| 记号(别名) | 大小 | 解释 |
|---|---|---|
BOOL(BOOLEAN) | 1 | 非 0 即 true |
UINT8(U8,USINT,BYTE) / INT8(I8,SINT) | 1 | u8 / i8 |
UINT16(U16,UINT,WORD) / INT16(I16,INT) | 2 | u16 / i16(默认 UINT16) |
UINT32(U32,UDINT,DWORD) / INT32(I32,DINT) | 4 | u32 / i32 |
REAL(FLOAT,F32,REAL32) | 4 | IEEE-754 float(LE) |
STRING(STR,VISIBLE_STRING) | ≤4 | ASCII(去除尾部 NUL) |
省略 TYPE 时按 data_type 推断:Boolean→BOOL、Integer→INT16、Long→INT32、Float→REAL、Double→REAL(4B 近似)、String→STRING。
支持 / 不支持
- read:SDO expedited upload(≤4 字节)— OK。
- write:不支持 —
isWriteSupported() = false、write()始终为false。(SDO download 为后续工作。) - segmented / block SDO、PDO 映射、NMT / Heartbeat 监视、SYNC、EMCY、LSS:不支持。
- 直连 SocketCAN / USB-CAN:桩实现(以 CAN-over-TCP 网关为前提)。
curl 注册示例
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_CANOPEN_DRIVE1",
"opc_type": "CANOPEN",
"opc_name": "Servo Drive 1",
"opc_agent_ip": "192.168.1.20",
"opc_agent_port": "20200",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "read-timeout": "3000" },
"tag_list": [
{"tag_id":"OPC_CANOPEN_DRIVE1_T01","tag_name":"Statusword","plc_address":"1:0x6041:0:UINT16","data_type":"Integer"},
{"tag_id":"OPC_CANOPEN_DRIVE1_T02","tag_name":"ActualPos","plc_address":"1:0x6064:0:INT32","data_type":"Long"}
]
}'
参考
- CiA 301(Application Layer)/ CiA 402(Drives)标准。
- 代码:
plantpulse-edge-driver/src/plantpulse/driver/protocol/canopen/CANOpenDriver.java+plantpulse-plc-protocol的canopen包。 - 运维:网关帧格式为自有二进制(
canId+dlc+data)——与 socketcand 文本协议不同。建议依据设备 EDS 文件校验地址。