LS XGI / XBC / XBM (FEnet) Treiber
Überblick
Kommuniziert mit den PLC-Serien XGI / XBC / XBM von LS ELECTRIC (ehemals LS Industrial Systems) über das Standardprotokoll FEnet (Fast Ethernet).
| Punkt | Wert |
|---|---|
opc_type | LS |
| Implementierungsklasse | plantpulse.driver.protocol.ls.LSDriver |
| Kommunikationsbibliothek | Eigene Java-Implementierung (plantpulse-plc-driver-ls.jar → FEnetClient) |
| read | ✅ |
| write | ❌ (im eigenen Java-Treiber ist keine write-API freigegeben) |
| Sicherheit | keine (Firmennetz vorausgesetzt) |
Früher basierte die Anbindung auf NativeProcessDriver: eine in .NET geschriebene externe native Binärdatei (FENetClient.exe) wurde als
externer Prozess gestartet. Seit 2025 erfolgt die Umstellung auf eine eigene Java-Implementierung
(plantpulse-plc-driver-ls.jar), die direkt per Socket kommuniziert. Dadurch entfallen OS-Abhängigkeiten und der Aufwand für die
Verwaltung externer Prozesse.
OPC-Registrierungsformular
| Feld | Bedeutung | Beispiel |
|---|---|---|
opc_agent_ip | PLC-IP | 192.168.0.80 |
opc_agent_port | FEnet-Port | 2004 (XGT-Standard) |
timecycle | Abfrageintervall (ms) | 1000 |
options.company-id | FEnet Company ID (optional) | (meist leer) |
options.use-checksum | BCC-Prüfsumme verwenden | false (default) |
options.use-hex-bit-index | Bit-Index hexadezimal verwenden | false (default) |
options.connect-timeout | Verbindungs-Timeout (ms) | 3000 |
options.read-timeout | read-Timeout (ms) | 3000 |
In der connect-Phase wird zuerst eine NetUtils.isReachable(ip)-Ping-Prüfung durchgeführt → bei Nichterreichbarkeit sofortiger Fehlschlag.
Tag-Format plc_address
FEnet-Standard (offizielles Format)
%<DeviceType><DataType><Index>
| Beispiel | Bedeutung |
|---|---|
%DW00309 | Data-Bereich, Word, Index 309 |
%DD00600 | Data-Bereich, Double Word (32 Bit), Index 600 |
%DL00800 | Data-Bereich, Long Word (64 Bit), Index 800 |
%MX02704 | Memory-Bereich, Bit, Index 0x2704 (oder dezimal 2704) |
%MW00100 | Memory-Bereich, Word, Index 100 |
DeviceType-Buchstaben: D (Data), M (Memory), K (Keep), F (Flag), T (Timer), C (Counter), R usw. — die Standard-Gerätecodes von LS XGI.
DataType-Buchstaben: X (Bit), B (Byte), W (Word, 16 Bit), D (Double Word, 32 Bit), L (Long
Word, 64 Bit).
Edge Short Form (empfohlen)
Edge akzeptiert die Short Form und wandelt sie automatisch in den FEnet-Standard um.
| Edge-Eingabe | data_type | format | Ergebnis der automatischen Umwandlung |
|---|---|---|---|
D00309 | Integer | (empty) | %DW00309 |
D00600 | Integer | DW | %DD00600 |
D00800 | Long | (empty) | %DL00800 |
M02704 | Boolean | (empty) | %MX02704 |
D00100 | Float | REAL (oder default) | %DD00100 |
D00200 | Double | LREAL | %DL00200 |
D00300 | String | STR[10] | 10 Worte read ab %DW00300 |
Interne Implementierung (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 wird durch data_type und format bestimmt (siehe nächster Abschnitt).
Zuordnung data_type / format (Details)
Der LS-Treiber erlaubt es, bei gleicher Short Address allein über format die Speicherbreite explizit zu ändern, sodass ein
Gerätecode frei auf 16/32/64 Bit abgebildet werden kann.
Integer-Familie (data_type=Integer / Short / Int / Int16 …)
format | Bedeutung | Breite | FEnet DataType | Aufruf |
|---|---|---|---|---|
| (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= (leer) bedeutet 16-Bit signed Word.
Wird 32 Bit benötigt, ist format=DW explizit anzugeben. Das entspricht der üblichen Konvention von LS XGI.
Float / Double
data_type | format | Breite | FEnet | Aufruf |
|---|---|---|---|---|
Float / REAL | (empty) / REAL / FLOAT | 32 Bit IEEE 754 | %DD | client.readFloat |
Double / LREAL | (empty) / LREAL / DOUBLE | 64 Bit IEEE 754 | %DL | client.readDouble |
Auch bei format=DW + data_type=Float wird als 32-Bit-Float interpretiert (Verzweigung isFloat(dt)).
Boolean
data_type | format | Bedeutung |
|---|---|---|
Boolean / Bool / Bit | (empty) / X / BIT | bitweiser read von %MX... oder %PX... |
Short Form unverändert: M02704 → %MX02704.
String
format | Bedeutung | Breite |
|---|---|---|
STR | 1 Wort (2 Zeichen) | 16 Bit × 1 |
STR[N] | N Worte (2N Zeichen) | 16 Bit × N |
ASCII-Kodierung + low byte first (LS-Spezifikation). Abschließende NUL-Bytes werden getrimmt.
// 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]
Liest ein Wort und extrahiert nur Bit idx (gleiche Konvention wie bei Modbus).
format | Verhalten |
|---|---|
BIN | Wort read, dann Bit 0 extrahieren |
BIN[5] | Bit 5 extrahieren |
BIN[F] | Bit hex F (=15) extrahieren (bei einem Zeichen ist Hex zulässig) |
Häufige Fehler + Behebung
| Meldung / Symptom | Ursache | Behebung |
|---|---|---|
LS-PLC Ping failed : <ip> | ICMP nicht erreichbar | Kabel/Firewall prüfen. Bei ICMP-Block-Umgebung Sperrrichtlinie überprüfen |
LS connect failed | FEnet-Modul nicht aktiv / Port-Mismatch | In XG5000 IP/Port des FEnet-Moduls prüfen (meist 2004) |
| Wert ist immer 0 | Short Form → Breiten-Mismatch (Word, aber als DWord gelesen) | format leer lassen oder mit DW exakt angeben |
| 32-Bit-Float fehlerhaft | format fehlt → wird als Word gelesen | data_type=Float + format=REAL angeben |
| Zeichenkette verfälscht | LS low-byte-first nicht berücksichtigt | Edge übernimmt das automatisch. Vorsicht bei externer Byte-Interpretation |
| Bit wird nicht gelesen | Short Form landet als D00100 | M02704 (M-Bereich) verwenden oder format=BIN[idx] |
curl-Registrierungsbeispiel
Beispiel mit gemischten D- und M-Bereichen von XGI / XBC / XBM:
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]"
}
]
}'
Wert read:
curl -s http://<edge-host>/api/v1/tag/OPC_LS_XBM_0001_TAG_00003/value | jq
Hinweise zum Verhalten
- Priorität der Read-Verzweigung (
LSDriver.read()):data_type=StringoderformatistSTR/STR[N]→readStringMultiformatistBINoderBIN[idx]→ Wort read, dann Bit extrahieren- Explizite
format-Verzweigung (UI,DW,UL,LW,REAL,LREALusw.) →readByFormat - Fallback:
data_typeallein (readByDataType)
- Der Aufruf
clientist synchronized, daher erfolgt der read mehrerer Tags einer OPC seriell. Andere OPCs laufen in eigenen Threads. - write ist nicht implementiert. Für die Nutzung von Sparkplug NCMD muss künftig
FEnetClient.write*freigegeben werden.
Beispielsammlung (nach Datentyp)
Alle im Betrieb von LS XGI / XBC / XBM häufig verwendeten Kombinationen in einer Tabelle. plc_address bezieht sich auf die Short Form und wird automatisch in die %D...-Standardnotation umgewandelt.
Bit / Boolean
data_type | format | plc_address | Speicherbreite | Bedeutung / Anmerkung |
|---|---|---|---|---|
Boolean | (empty) | M02704 | 1 Bit | Bit im Memory-Bereich. Häufigste Form |
Boolean | (empty) | P0001F | 1 Bit | Hexadezimaler Bit-Index von LS XGI (F=15). Bei use-hex-bit-index=true |
Boolean | (empty) | K00010 | 1 Bit | Keep-Relay-Bit |
Boolean | (empty) | T0110 | 1 Bit | Timer-Ausgangsbit |
Boolean | BIN | D00309 | 16 Bit → Bit 0 | Wort read, dann Bit 0 extrahieren |
Boolean | BIN[3] | D00309 | 16 Bit → Bit 3 | Bit 3 extrahieren |
Boolean | BIN[F] | D00309 | 16 Bit → Bit 15 | Hex-Index F=15 |
16-Bit-Ganzzahl (Integer/Word)
data_type | format | plc_address | Speicherbreite | Bedeutung |
|---|---|---|---|---|
Integer | (empty) | D00309 | 16 Bit | signed Word (%DW00309) |
Integer | UI | D00300 | 16 Bit | UWord (unsigned, client.readUWord) |
Integer | UWORD | D00300 | 16 Bit | identisch mit UI |
32-Bit-Ganzzahl (DWord)
data_type | format | plc_address | Speicherbreite | Bedeutung |
|---|---|---|---|---|
Integer | DW | D00600 | 32 Bit | DWord signed (Kombination %DD00600, D00600/00601) |
Integer | DWORD | D00600 | 32 Bit | wie oben |
Integer | DUW | D00690 | 32 Bit | UDWord (unsigned 32) |
Integer | UDW | D00690 | 32 Bit | identisch mit UDW |
Integer | UL | D00690 | 32 Bit | ULong = UDWord (Alias) |
64-Bit-Ganzzahl (LWord)
data_type | format | plc_address | Speicherbreite | Bedeutung |
|---|---|---|---|---|
Long | (empty) | D00700 | 64 Bit | LWord signed (%DL00700) |
Long | LW | D00700 | 64 Bit | explizites LWord |
Long | LWORD | D00700 | 64 Bit | identisch |
Long | ULW | D00750 | 64 Bit | ULWord (unsigned 64) |
Gleitkomma (Float / Double)
data_type | format | plc_address | Speicherbreite | Bedeutung |
|---|---|---|---|---|
Float | (empty) | D00450 | 32 Bit | IEEE 754 single (%DD00450) |
Float | REAL | D00450 | 32 Bit | explizit |
Float | DW | D00450 | 32 Bit | Verarbeitung als float über Verzweigung isFloat(dt) |
Double | LREAL | D00500 | 64 Bit | IEEE 754 double (%DL00500) |
Double | (empty) | D00500 | 64 Bit | identisch mit LREAL |
Zeichenkette (String)
data_type | format | plc_address | Speicherbreite | Bedeutung |
|---|---|---|---|---|
String | STR | D00803 | 16 Bit (1 Wort) | 2 chars (low byte first) |
String | STR[5] | D00800 | 80 Bit (5 Worte) | 10 chars, NUL-Trim |
String | STR[10] | D00500 | 160 Bit (10 Worte) | 20 chars |
String | STR[16] | D00100 | 32 Worte | 32 chars (32 Byte) — von XGI empfohlene Ausrichtung |
Einsatz von Formeln (LS-Praxismuster)
| Zweck | data_type | fomula | Eingabe → Ausgabe |
|---|---|---|---|
| Integer-Rohwert → 1 Nachkommastelle | Float | ${VALUE}*0.1 | 1638 → 163.8 |
| Integer-Rohwert → 2 Nachkommastellen | Float | ${VALUE}*0.01 | 12345 → 123.45 |
| signed 16 → unsigned Umwandlung | Integer | ${VALUE}+65536 | Korrektur negativer Rohwerte (16-Bit-Umrechnung) |
| Nullpunktkorrektur (anderes Tag) | Float | ${VALUE}-${TAG_ZERO} | 1024 - 24 = 1000 |
| Kalibrierung (gain × x + offset) | Float | ${VALUE}*${TAG_GAIN}+${TAG_OFFSET} | Korrekturkoeffizienten als eigenes Tag |
| Hz-Umrechnung (rpm → Hz) | Float | ${VALUE}/60 | 1800 → 30.0 |
Details zur Verwendung von Formeln siehe Formel-Referenz.
Umfassendes curl-Beispiel (alle Typen auf einmal)
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"}
]
}'