LS XGI / XBC / XBM (FEnet) ドライバ
概要
LS ELECTRIC (旧 LS産電) の XGI / XBC / XBM PLC シリーズと FEnet (Fast Ethernet) 標準プロトコルで 通信します。
| 項目 | 値 |
|---|---|
opc_type | LS |
| 実装クラス | plantpulse.driver.protocol.ls.LSDriver |
| 通信ライブラリ | 独自 Java 実装 (plantpulse-plc-driver-ls.jar → FEnetClient) |
| read | ✅ |
| write | ❌ (独自 Java ドライバに write API 未公開) |
| セキュリティ | なし (社内網前提) |
以前は .NET で書かれた外部ネイティブバイナリ (FENetClient.exe) を外部プロセスとして起動して利用する
NativeProcessDriver ベースでした。2025 年から独自 Java 実装
(plantpulse-plc-driver-ls.jar) に移行し、socket による直接通信を行います。結果として OS 依存 / 外部
プロセス管理の負担がなくなりました。
OPC 登録フォーム
| フィールド | 意味 | 例 |
|---|---|---|
opc_agent_ip | PLC IP | 192.168.0.80 |
opc_agent_port | FEnet ポート | 2004 (XGT デフォルト) |
timecycle | ポーリング周期 (ms) | 1000 |
options.company-id | FEnet company ID (任意) | (通常は空欄) |
options.use-checksum | BCC チェックサム使用 | false (default) |
options.use-hex-bit-index | ビット index の 16 進使用 | false (default) |
options.connect-timeout | 接続タイムアウト (ms) | 3000 |
options.read-timeout | read タイムアウト (ms) | 3000 |
connect 段階で NetUtils.isReachable(ip) ping 検査を先に実行します → 到達不可の場合は即時失敗。
タグ plc_address 形式
FEnet 標準 (正式形式)
%<DeviceType><DataType><Index>
| 例 | 意味 |
|---|---|
%DW00309 | Data 領域、Word、index 309 |
%DD00600 | Data 領域、Double Word (32-bit)、index 600 |
%DL00800 | Data 領域、Long Word (64-bit)、index 800 |
%MX02704 | Memory 領域、Bit、index 0x2704 (または 10 進 2704) |
%MW00100 | Memory 領域、Word、index 100 |
DeviceType の文字: D (Data)、M (Memory)、K (Keep)、F (Flag)、T (Timer)、C (Counter)、R など LS XGI の標準デバイスコード。
DataType の文字: X (Bit)、B (Byte)、W (Word, 16-bit)、D (Double Word, 32-bit)、L (Long
Word, 64-bit)。
Edge short form (推奨)
Edge は short form を受け取り、自動的に FEnet 標準へ変換します。
| Edge 入力 | data_type | format | 自動変換結果 |
|---|---|---|---|
D00309 | Integer | (empty) | %DW00309 |
D00600 | Integer | DW | %DD00600 |
D00800 | Long | (empty) | %DL00800 |
M02704 | Boolean | (empty) | %MX02704 |
D00100 | Float | REAL (または default) | %DD00100 |
D00200 | Double | LREAL | %DL00200 |
D00300 | String | STR[10] | %DW00300 から 10 ワード read |
内部実装 (LSDriver.toFEnet):
// raw plc_address (D00309) → %D<dataCh>00309
static String toFEnet(String rawAddr, char dataCh) {
if (rawAddr.charAt(0) == '%') return rawAddr; // 이미 표준이면 유지
return "%" + Character.toUpperCase(rawAddr.charAt(0)) + dataCh + rawAddr.substring(1);
}
dataCh は data_type と format で決定 (次節を参照)。
data_type / format マッピング (詳細)
LS ドライバは同一の short address でも format だけで メモリ幅を明示的に変更 できるため、1 つの
デバイスのデバイスコードを 16/32/64-bit へ自由にマッピングできます。
Integer 系 (data_type=Integer / Short / Int / Int16 …)
format | 意味 | 幅 | FEnet DataType | 呼び出し |
|---|---|---|---|---|
| (empty) | Word (signed) | 16-bit | %DW | client.readWord |
UI / UW / UWORD | UWord (unsigned) | 16-bit | %DW | client.readUWord |
DW / DWORD / DOUBLE_WORD | DWord (signed) | 32-bit | %DD | client.readDWord |
UL / UDW / DUW / UDWORD | UDWord (unsigned) | 32-bit | %DD | client.readUDWord |
LW / LWORD / LONG_WORD | LWord (signed) | 64-bit | %DL | client.readLWord |
ULW / ULWORD | ULWord (unsigned) | 64-bit | %DL | client.readULWord |
D00309 + data_type=Integer + format= (空欄) は 16-bit signed Word です。
32-bit が必要な場合は format=DW で明示します。これは LS XGI の一般的な慣例と一致します。
Float / Double
data_type | format | 幅 | FEnet | 呼び出し |
|---|---|---|---|---|
Float / REAL | (empty) / REAL / FLOAT | 32-bit IEEE 754 | %DD | client.readFloat |
Double / LREAL | (empty) / LREAL / DOUBLE | 64-bit IEEE 754 | %DL | client.readDouble |
format=DW + data_type=Float の場合も 32-bit float として解釈します (isFloat(dt) 分岐)。
Boolean
data_type | format | 意味 |
|---|---|---|
Boolean / Bool / Bit | (empty) / X / BIT | %MX... または %PX... のビット単位 read |
short form のまま: M02704 → %MX02704。
String
format | 意味 | 幅 |
|---|---|---|
STR | 1 ワード (2 文字) | 16-bit × 1 |
STR[N] | N ワード (2N 文字) | 16-bit × N |
ASCII エンコーディング + low byte first (LS spec)。末尾の NUL バイトは trim 処理されます。
// readStringMulti: N 워드 연속 read, low byte 먼저
for (int i = 0; i < wordCount; i++) {
int w = client.readUWord("%" + dev + "W" + (startIdx + i));
bytes[b++] = (byte) (w & 0xFF);
bytes[b++] = (byte) ((w >> 8) & 0xFF);
}
BIN / BIN[idx]
ワード 1 個を読み取り、ビット idx のみを抽出 (Modbus と同じ慣例)。
format | 動作 |
|---|---|
BIN | ワード read 後に bit 0 を抽出 |
BIN[5] | bit 5 を抽出 |
BIN[F] | hex F (=15) bit を抽出 (1 文字は 16 進を許容) |
よくあるエラー + 解決
| メッセージ / 症状 | 原因 | 解決 |
|---|---|---|
LS-PLC Ping failed : <ip> | ICMP 到達不可 | ケーブル/ファイアウォールを確認。ICMP block 環境の場合は遮断ポリシーを検討 |
LS connect failed | FEnet モジュール未稼働 / ポート mismatch | XG5000 で FEnet モジュールの IP/Port を確認 (通常 2004) |
| 値が常に 0 | short form → 幅 mismatch (Word なのに DWord で読み取り) | format を空欄にするか DW で正確に明示 |
| 32-bit float が壊れる | format が欠落 → Word として読まれる | data_type=Float + format=REAL を明示 |
| 文字列が文字化けする | LS の low-byte-first が未反映 | edge 側で自動処理。外部で bytes を直接解釈する場合は注意 |
| Bit が読めない | short form が D00100 として入っている | M02704 (M 領域) を使用、または format=BIN[idx] |
curl 登録例
XGI / XBC / XBM の D、M 領域混在の例:
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_LS_XBM_0001",
"opc_type": "LS",
"opc_name": "XBM Line A",
"opc_agent_ip": "192.168.0.80",
"opc_agent_port": "2004",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": {
"connect-timeout": "3000",
"read-timeout": "3000",
"use-checksum": "false"
},
"tag_list": [
{
"tag_id": "OPC_LS_XBM_0001_TAG_00001",
"tag_name": "Counter",
"plc_address": "D00309",
"data_type": "Integer"
},
{
"tag_id": "OPC_LS_XBM_0001_TAG_00002",
"tag_name": "Production32",
"plc_address": "D00600",
"data_type": "Integer",
"format": "DW"
},
{
"tag_id": "OPC_LS_XBM_0001_TAG_00003",
"tag_name": "Pressure",
"plc_address": "D00100",
"data_type": "Float",
"format": "REAL"
},
{
"tag_id": "OPC_LS_XBM_0001_TAG_00004",
"tag_name": "Status",
"plc_address": "M02704",
"data_type": "Boolean"
},
{
"tag_id": "OPC_LS_XBM_0001_TAG_00005",
"tag_name": "BatchName",
"plc_address": "D00500",
"data_type": "String",
"format": "STR[10]"
},
{
"tag_id": "OPC_LS_XBM_0001_TAG_00006",
"tag_name": "Bit3OfStatus",
"plc_address": "D00400",
"data_type": "Integer",
"format": "BIN[3]"
}
]
}'
値の read:
curl -s http://<edge-host>/api/v1/tag/OPC_LS_XBM_0001_TAG_00003/value | jq
動作メモ
- Read 分岐の優先順位 (
LSDriver.read()):data_type=StringまたはformatがSTR/STR[N]→readStringMultiformatがBINまたはBIN[idx]→ ワード read 後にビット抽出formatの明示分岐 (UI,DW,UL,LW,REAL,LREALなど) →readByFormat- フォールバック:
data_type単独 (readByDataType)
clientの呼び出しは synchronized されているため、1 つの OPC 内の複数タグ read は直列。別の OPC は別スレッド。- write は未実装。Sparkplug NCMD を使用するには、今後
FEnetClient.write*の公開が必要です。
例集 (データタイプ別)
LS XGI / XBC / XBM の運用現場で頻繁に使う組み合わせをすべて 1 つの表にまとめました。plc_address は short form 基準であり、自動的に %D... 標準表記へ変換されます。
ビット / ブール (Boolean)
data_type | format | plc_address | メモリ幅 | 意味 / 備考 |
|---|---|---|---|---|
Boolean | (empty) | M02704 | 1 bit | メモリ領域のビット。最も一般的な形 |
Boolean | (empty) | P0001F | 1 bit | LS XGI の 16 進ビットインデックス (F=15)。use-hex-bit-index=true の場合 |
Boolean | (empty) | K00010 | 1 bit | Keep relay ビット |
Boolean | (empty) | T0110 | 1 bit | Timer 出力ビット |
Boolean | BIN | D00309 | 16 bit → bit 0 | ワード read 後に 0 ビット抽出 |
Boolean | BIN[3] | D00309 | 16 bit → bit 3 | bit 3 抽出 |
Boolean | BIN[F] | D00309 | 16 bit → bit 15 | hex インデックス F=15 |
16-bit 整数 (Integer/Word)
data_type | format | plc_address | メモリ幅 | 意味 |
|---|---|---|---|---|
Integer | (empty) | D00309 | 16 bit | signed Word (%DW00309) |
Integer | UI | D00300 | 16 bit | UWord (unsigned, client.readUWord) |
Integer | UWORD | D00300 | 16 bit | UI と同一 |
32-bit 整数 (DWord)
data_type | format | plc_address | メモリ幅 | 意味 |
|---|---|---|---|---|
Integer | DW | D00600 | 32 bit | DWord signed (%DD00600、D00600/00601 を結合) |
Integer | DWORD | D00600 | 32 bit | 上と同一 |
Integer | DUW | D00690 | 32 bit | UDWord (unsigned 32) |
Integer | UDW | D00690 | 32 bit | UDW と同一 |
Integer | UL | D00690 | 32 bit | ULong = UDWord (alias) |
64-bit 整数 (LWord)
data_type | format | plc_address | メモリ幅 | 意味 |
|---|---|---|---|---|
Long | (empty) | D00700 | 64 bit | LWord signed (%DL00700) |
Long | LW | D00700 | 64 bit | 明示的 LWord |
Long | LWORD | D00700 | 64 bit | 同一 |
Long | ULW | D00750 | 64 bit | ULWord (unsigned 64) |
実数 (Float / Double)
data_type | format | plc_address | メモリ幅 | 意味 |
|---|---|---|---|---|
Float | (empty) | D00450 | 32 bit | IEEE 754 single (%DD00450) |
Float | REAL | D00450 | 32 bit | 明示 |
Float | DW | D00450 | 32 bit | isFloat(dt) 分岐で float 処理 |
Double | LREAL | D00500 | 64 bit | IEEE 754 double (%DL00500) |
Double | (empty) | D00500 | 64 bit | LREAL と同一 |
文字列 (String)
data_type | format | plc_address | メモリ幅 | 意味 |
|---|---|---|---|---|
String | STR | D00803 | 16 bit (1 word) | 2 chars (low byte first) |
String | STR[5] | D00800 | 80 bit (5 words) | 10 chars、NUL trim |
String | STR[10] | D00500 | 160 bit (10 words) | 20 chars |
String | STR[16] | D00100 | 32 word | 32 chars (32 byte) — XGI 推奨アライメント |
Formula の活用 (LS 現場パターン)
| 用途 | data_type | fomula | 入力 → 出力 |
|---|---|---|---|
| 整数 raw → 小数 1 桁 | Float | ${VALUE}*0.1 | 1638 → 163.8 |
| 整数 raw → 小数 2 桁 | Float | ${VALUE}*0.01 | 12345 → 123.45 |
| signed 16 → unsigned 変換 | Integer | ${VALUE}+65536 | 負値 raw の補正 (16-bit 換算) |
| ゼロ点補正 (別タグ) | Float | ${VALUE}-${TAG_ZERO} | 1024 - 24 = 1000 |
| キャリブレーション (gain × x + offset) | Float | ${VALUE}*${TAG_GAIN}+${TAG_OFFSET} | 補正係数は別タグ |
| Hz 変換 (rpm → Hz) | Float | ${VALUE}/60 | 1800 → 30.0 |
fomula の詳しい使い方は Formula リファレンス を参照してください。
curl 総合例 (すべての型を一度に)
curl -X POST http://<edge-host>/api/v1/opc \
-H "Content-Type: application/json" \
-d '{
"opc_id": "OPC_LS_FULL",
"opc_type": "LS",
"opc_name": "LS XGI Full",
"opc_agent_ip": "192.168.0.80",
"opc_agent_port": "2004",
"site_id": "SITE_00001",
"auto_collect": true,
"timecycle": 1000,
"options": { "use-hex-bit-index": "true" },
"tag_list": [
{"tag_id":"OPC_LS_FULL_T01", "tag_name":"BitM", "plc_address":"M02704", "data_type":"Boolean"},
{"tag_id":"OPC_LS_FULL_T02", "tag_name":"BitPHex", "plc_address":"P0001F", "data_type":"Boolean"},
{"tag_id":"OPC_LS_FULL_T03", "tag_name":"WordSigned", "plc_address":"D00309", "data_type":"Integer"},
{"tag_id":"OPC_LS_FULL_T04", "tag_name":"WordU", "plc_address":"D00300", "data_type":"Integer", "format":"UI"},
{"tag_id":"OPC_LS_FULL_T05", "tag_name":"DWordS", "plc_address":"D00600", "data_type":"Integer", "format":"DW"},
{"tag_id":"OPC_LS_FULL_T06", "tag_name":"DWordU", "plc_address":"D00690", "data_type":"Integer", "format":"DUW"},
{"tag_id":"OPC_LS_FULL_T07", "tag_name":"LWord", "plc_address":"D00700", "data_type":"Long"},
{"tag_id":"OPC_LS_FULL_T08", "tag_name":"Real", "plc_address":"D00450", "data_type":"Float", "format":"REAL"},
{"tag_id":"OPC_LS_FULL_T09", "tag_name":"LReal", "plc_address":"D00500", "data_type":"Double", "format":"LREAL"},
{"tag_id":"OPC_LS_FULL_T10", "tag_name":"Str1Word", "plc_address":"D00803", "data_type":"String", "format":"STR"},
{"tag_id":"OPC_LS_FULL_T11", "tag_name":"Str10Word", "plc_address":"D00500", "data_type":"String", "format":"STR[10]"},
{"tag_id":"OPC_LS_FULL_T12", "tag_name":"BitOfWord", "plc_address":"D00309", "data_type":"Boolean", "format":"BIN[3]"},
{"tag_id":"OPC_LS_FULL_T13", "tag_name":"PressScale", "plc_address":"D00100", "data_type":"Float", "format":"REAL", "fomula":"${VALUE}*0.1"}
]
}'