LS XGI / XBC / XBM (FEnet) Treiber
Überblick
Kommuniziert mit den PLC-Serien XGI / XBC / XBM von LS ELECTRIC (früher LS Industrial Systems) über das Standardprotokoll FEnet (Fast Ethernet).
| Position | Wert |
|---|---|
opc_type | LS |
| Implementierungsklasse | plantpulse.driver.protocol.ls.LSDriver |
| Kommunikationsbibliothek | Eigene Java-Implementierung (plantpulse-plc-driver-ls.jar → FEnetClient) |
| read | ✅ |
| write | ✅ (Bit · Byte · Word · DWord · Float · LWord · Double · String, BIN[n] als read-modify-write) |
| Sicherheit | keine (Firmennetz vorausgesetzt) |
Früher basierte die Anbindung auf NativeProcessDriver: ein in .NET geschriebenes externes natives Binary (FENetClient.exe) wurde als eigener Prozess gestartet. Seit 2025 erfolgt die Umstellung auf eine eigene Java-Implementierung (plantpulse-plc-driver-ls.jar) mit direkter Socket-Kommunikation. Damit entfallen die OS-Abhängigkeit 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 | 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-plc_address-Format
FEnet-Standard (offizielles Format)
%<DeviceType><DataType><Index>
| Beispiel | Bedeutung |
|---|---|
%DW00309 | Bereich Data, Word, Index 309 |
%DD00600 | Bereich Data, Double Word (32 Bit), Index 600 |
%DL00800 | Bereich Data, Long Word (64 Bit), Index 800 |
%MX02704 | Bereich Memory, Bit, Index 0x2704 (oder dezimal 2704) |
%MW00100 | Bereich Memory, Word, Index 100 |
DeviceType-Zeichen: D (Data), M (Memory), K (Keep), F (Flag), T (Timer), C (Counter), R usw. — die Standard-Gerätecodes von LS XGI.
DataType-Zeichen: 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 folgender Abschnitt).
data_type / format Mapping (Details)
Der LS-Treiber erlaubt es, bei gleicher short address allein über format die Speicherbreite explizit zu ändern; damit lässt sich ein Gerätecode frei auf 16/32/64 Bit abbilden.
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, muss format=DW explizit angegeben werden. 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 über %MX... oder %PX... |
short form direkt: M02704 → %MX02704.
String
format | Bedeutung | Breite |
|---|---|---|
STR | 1 Wort (2 Zeichen) | 16 Bit × 1 |
STR[N] | N Worte (2N Zeichen) | 16 Bit × N |
ASCII-Codierung + 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] | hex F (=15) Bit extrahieren (ein Zeichen darf hexadezimal sein) |
Häufige Fehler + Behebung
| Meldung / Symptom | Ursache | Behebung |
|---|---|---|
LS-PLC Ping failed : <ip> | ICMP nicht erreichbar | Kabel/Firewall prüfen. In Umgebungen mit ICMP-Blockierung 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 verstümmelt | low-byte-first von LS nicht berücksichtigt | Edge erledigt das automatisch. Vorsicht, wenn Bytes extern direkt interpretiert werden |
| Bit wird nicht gelesen | short form wird als D00100 übernommen | M02704 (Bereich M) verwenden oder format=BIN[idx] |
curl-Registrierungsbeispiel
Beispiel mit gemischten Bereichen D und M bei 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; der read mehrerer Tags eines OPC läuft daher seriell. Andere OPC laufen in eigenen Threads. - write ist implementiert. Beim bitweisen Schreiben (
BIN[n]) wird das Wort gelesen, nur das betreffende Bit geändert und zurückgeschrieben — wird dasselbe Wort gleichzeitig von anderer Stelle geschrieben, überschreiben sich die Zugriffe gegenseitig.
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 Standardnotation %D... 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 bei LS XGI (F=15). Wenn 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 zu 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 zu 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 | explizit 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 | Behandlung als float über Verzweigung isFloat(dt) |
Double | LREAL | D00500 | 64 Bit | IEEE 754 double (%DL00500) |
Double | (empty) | D00500 | 64 Bit | identisch zu 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 Dezimalstelle | Float | ${VALUE}*0.1 | 1638 → 163.8 |
| Integer-Rohwert → 2 Dezimalstellen | Float | ${VALUE}*0.01 | 12345 → 123.45 |
| Umwandlung signed 16 → unsigned | 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} | Korrekturkoeffizient als separates Tag |
| Hz-Umrechnung (rpm → Hz) | Float | ${VALUE}/60 | 1800 → 30.0 |
Zur ausführlichen Nutzung 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"}
]
}'