Beckhoff TwinCAT/ADS 驱动
概述
Beckhoff TwinCAT 的 AMS/ADS over TCP。不依赖 JNI / 外部库,仅基于 Beckhoff 公开的 infosys AMS/ADS wire spec, 在单个 JVM 内通过 socket 直接通信。
| 项目 | 值 |
|---|---|
opc_type | ADS |
| 实现类 | plantpulse.driver.protocol.ads.ADSDriver |
| 库 | (无 —— 自主实现。Beckhoff infosys 公开 spec) |
| read | 良好(indexGroup/Offset 或符号句柄) |
| write | 良好(isWriteSupported() = true) |
| 默认端口 | 48898 (ADS over TCP) |
| 默认 AMS 端口 | 851 (PLC Runtime 1) |
| 安全 | 无(TCP 直连,不支持 ADS Secure) |
TwinCAT 路由访问
ADS 通信需要在 PC/PLC 的 AMS Router 中注册静态路由。在 PLC 侧的 TwinCAT System Manager 中 将 Edge 的 AmsNetId 注册为路由后,本驱动即以该 NetId 发起请求。
类 / 结构
ADSDriver (BaseProtocolDriver)
├── AmsTcpHeader 6 byte reserved + length(LE)
├── AmsHeader 32 byte target/source NetId+Port + cmd + state + dataLen + err + invokeId
├── AdsRead.buildRequest(indexGroup, indexOffset, length)
├── AdsWrite.buildRequest(indexGroup, indexOffset, data)
├── AdsResponse.parse(payload) — errorCode + data
├── AdsCommand — READ / WRITE / READ_STATE / READ_WRITE 상수, IGRP_*
└── AdsCodec — putUInt16/32LE, getUInt16/32LE, getInt32LE
请求-响应匹配通过 AtomicInteger invokeIdCounter 发放的 32-bit invokeId 完成,并在单个 socket 上
以 synchronized sendCommand(...) 串行化。符号名 → handle 的转换结果缓存在 symbolHandleCache (HashMap) 中。
wire 格式摘要
AMS/TCP Header (6) | AMS Header (32) | ADS payload (가변)
└ reserved(2) len(4)│└ tgtNetId(6) tgtPort(2) srcNetId(6) srcPort(2) cmd(2) state(2) dataLen(4) err(4) invokeId(4)
- 所有整数字段均为 little-endian。
AmsNetId为 6 byte(例5.40.40.116.1.1)。cmd:READ=2、WRITE=3、READ_STATE=4、READ_WRITE=9。stateFlags:STATEFLAG_REQ_RESP = 0x0004(要求响应)。
OPC 注册选项 (options)
| 键 | 含义 | 默认值 |
|---|---|---|
target-netid | PLC AmsNetId,例 5.40.40.116.1.1 | host + .1.1 |
target-port | AMS 目标端口 (PLC Runtime) | 851 |
source-netid | 本地 AmsNetId | 127.0.0.1.1.1 |
source-port | 本地 AMS 端口 | 32905 |
connect-timeout | 连接超时 (ms) | 3000 |
read-timeout | 响应等待超时 (ms) | 3000 |
在 connect() 之后立即调用 READ_STATE 以验证握手 —— 失败时 connected=false。
标签地址格式
| 表示 | 含义 | indexGroup |
|---|---|---|
M0:4 | %M offset 0, 4 byte | IGRP_PLC_RW_MB (0x4020) |
I0:2 | %I offset 0, 2 byte | IGRP_PLC_RW_IB (0x4000) |
Q0:1 | %Q offset 0 | IGRP_PLC_RW_QB (0x4030) |
DB10:4 | DB10 offset 0 | IGRP_PLC_RW_DB (0x4040) |
0x4020:0x10:4 | 直接 indexGroup:indexOffset:length | (原样) |
MAIN.fCounter:REAL | 符号方式 (ReadWrite + IGRP_GET_SYMHANDLE_BYNAME → 缓存) | (基于 handle) |
: 之后可写 byte 长度,或 BOOL / INT / DINT / REAL / LREAL / STRING 等 TwinCAT 类型关键字,
由 sizeOfTypeKeyword(...) 决定 byte 长度。
数据编码 / 解码
decode(byte[], data_type) / encode(value, data_type, hintLen) 按 little-endian 处理:
data_type | byte | 备注 |
|---|---|---|
| Boolean / Bool | 1 | data[0] != 0 |
| Byte | 1 | unsigned |
| Short / Int / Integer / Word | 2 | signed 16 |
| UInt16 | 2 | unsigned |
| Int32 / DWord | 4 | signed 32 |
| UInt32 | 4 | unsigned |
| Float / REAL | 4 | IEEE 754 |
| Long / Int64 / LWord | 8 | signed |
| Double / LREAL | 8 | IEEE 754 |
| String | N | UTF-8,按 NUL-terminated 处理 |
支持 / 不支持
- ✅ Memory area read/write (M / I / Q / DB)
- ✅ 符号名 → handle(
IGRP_GET_SYMHANDLE_BYNAME=0xF003)→IGRP_RW_SYMVAL_BYHANDLE(0xF005) - ✅
READ_STATE(ADS state + Device state) - ❌ ADS Notification(订阅)
- ❌ SUMUP(多次 read 单次 wrapping)
- ❌ ADS Secure / TLS
测试覆盖率
test/java/plantpulse/driver/protocol/ads/
| 类 | 测试数 |
|---|---|
AdsAddressParseTest | 10 |
AdsCodecTest | 11 |
AdsReadTest | 5 |
AdsResponseTest | 5 |
AdsSpecComplianceTest | 23 |
AdsWriteTest | 5 |
AmsHeaderTest | 6 |
AmsNetIdTest | 7 |
AmsTcpHeaderTest | 7 |
共 79 项测试。
参考
- Beckhoff infosys: TwinCAT ADS / AMS over TCP wire format
- 代码:
src/plantpulse/driver/protocol/ads/