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 非対応) |
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 の 1 回 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/