Skip to main content

LS XGI / XBC / XBM (FEnet) Driver

Overview

Communicates with LS ELECTRIC (formerly LSIS) XGI / XBC / XBM PLC series using the FEnet (Fast Ethernet) standard protocol.

ItemValue
opc_typeLS
Implementation classplantpulse.driver.protocol.ls.LSDriver
Communication libraryIn-house Java implementation (plantpulse-plc-driver-ls.jarFEnetClient)
read
write❌ (no write API exposed in the in-house Java driver)
SecurityNone (assumes internal network)
Native → Java migration

Previously this was based on NativeProcessDriver, which launched an external native binary (FENetClient.exe) written in .NET as a separate process. Since 2025 it has been migrated to an in-house Java implementation (plantpulse-plc-driver-ls.jar) that communicates directly over sockets. As a result, the OS dependency and the burden of managing an external process are gone.


OPC Registration Form

FieldMeaningExample
opc_agent_ipPLC IP192.168.0.80
opc_agent_portFEnet port2004 (XGT default)
timecyclePolling interval (ms)1000
options.company-idFEnet company ID (optional)(usually left empty)
options.use-checksumUse BCC checksumfalse (default)
options.use-hex-bit-indexUse hexadecimal bit indexfalse (default)
options.connect-timeoutConnection timeout (ms)3000
options.read-timeoutRead timeout (ms)3000

During the connect stage a NetUtils.isReachable(ip) ping check is performed first → immediate failure if unreachable.


Tag plc_address Format

FEnet standard (formal notation)

%<DeviceType><DataType><Index>
ExampleMeaning
%DW00309Data area, Word, index 309
%DD00600Data area, Double Word (32-bit), index 600
%DL00800Data area, Long Word (64-bit), index 800
%MX02704Memory area, Bit, index 0x2704 (or decimal 2704)
%MW00100Memory area, Word, index 100

DeviceType letters: D (Data), M (Memory), K (Keep), F (Flag), T (Timer), C (Counter), R, and other standard LS XGI device codes.

DataType letters: X (Bit), B (Byte), W (Word, 16-bit), D (Double Word, 32-bit), L (Long Word, 64-bit).

Edge accepts the short form and converts it automatically to FEnet standard notation.

Edge inputdata_typeformatAutomatic conversion result
D00309Integer(empty)%DW00309
D00600IntegerDW%DD00600
D00800Long(empty)%DL00800
M02704Boolean(empty)%MX02704
D00100FloatREAL (or default)%DD00100
D00200DoubleLREAL%DL00200
D00300StringSTR[10]10-word read starting at %DW00300

Internal implementation (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 is determined by data_type and format (see the next section).


data_type / format Mapping (details)

The LS driver lets you explicitly change the memory width using format alone, even for the same short address, so a single device code can be freely mapped as 16/32/64-bit.

Integer family (data_type=Integer / Short / Int / Int16 …)

formatMeaningWidthFEnet DataTypeCall
(empty)Word (signed)16-bit%DWclient.readWord
UI / UW / UWORDUWord (unsigned)16-bit%DWclient.readUWord
DW / DWORD / DOUBLE_WORDDWord (signed)32-bit%DDclient.readDWord
UL / UDW / DUW / UDWORDUDWord (unsigned)32-bit%DDclient.readUDWord
LW / LWORD / LONG_WORDLWord (signed)64-bit%DLclient.readLWord
ULW / ULWORDULWord (unsigned)64-bit%DLclient.readULWord
What an unspecified format means

D00309 + data_type=Integer + format= (empty) means 16-bit signed Word. If you need 32-bit, specify format=DW explicitly. This matches the usual LS XGI convention.

Float / Double

data_typeformatWidthFEnetCall
Float / REAL(empty) / REAL / FLOAT32-bit IEEE 754%DDclient.readFloat
Double / LREAL(empty) / LREAL / DOUBLE64-bit IEEE 754%DLclient.readDouble

Even with format=DW + data_type=Float, the value is interpreted as a 32-bit float (isFloat(dt) branch).

Boolean

data_typeformatMeaning
Boolean / Bool / Bit(empty) / X / BITBit-level read of %MX... or %PX...

Short form as-is: M02704%MX02704.

String

formatMeaningWidth
STR1 word (2 characters)16-bit × 1
STR[N]N words (2N characters)16-bit × N

ASCII encoding + low byte first (LS spec). Trailing NUL bytes are trimmed.

// 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]

Reads one word and extracts only bit idx (same convention as Modbus).

formatBehavior
BINRead word, then extract bit 0
BIN[5]Extract bit 5
BIN[F]Extract hex F (=15) bit (a single character may be hexadecimal)

Common Errors and Solutions

Message / SymptomCauseSolution
LS-PLC Ping failed : <ip>ICMP unreachableCheck cabling/firewall. If ICMP is blocked in your environment, review the blocking policy
LS connect failedFEnet module not running / port mismatchCheck the FEnet module IP/Port in XG5000 (usually 2004)
Value is always 0short form → width mismatch (read as DWord although it is a Word)Leave format empty or specify it exactly as DW
32-bit float is corruptedformat missing → read as WordSpecify data_type=Float + format=REAL
String characters are garbledLS low-byte-first not appliedEdge handles this automatically. Be careful when interpreting bytes directly from outside
Bit is not readshort form was entered as D00100Use M02704 (M area) or format=BIN[idx]

curl Registration Example

Example mixing the D and M areas of 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]"
}
]
}'

Read values:

curl -s http://<edge-host>/api/v1/tag/OPC_LS_XBM_0001_TAG_00003/value | jq

Operating Notes

  • Read branch priority (LSDriver.read()):
    1. data_type=String or format is STR / STR[N]readStringMulti
    2. format is BIN or BIN[idx] → read word, then extract bit
    3. Explicit format branch (UI, DW, UL, LW, REAL, LREAL, etc.) → readByFormat
    4. Fallback: data_type alone (readByDataType)
  • client calls are synchronized, so multiple tag reads on one OPC are serialized. Different OPCs run on separate threads.
  • write is not implemented. To use Sparkplug NCMD, FEnetClient.write* must be exposed in a follow-up.

Example Collection (by data type)

All combinations frequently used in LS XGI / XBC / XBM field operations, in a single table. plc_address is given in short form and is automatically converted to %D... standard notation.

Bit / Boolean

data_typeformatplc_addressMemory widthMeaning / Notes
Boolean(empty)M027041 bitMemory area bit. The most common form
Boolean(empty)P0001F1 bitLS XGI hexadecimal bit index (F=15). When use-hex-bit-index=true
Boolean(empty)K000101 bitKeep relay bit
Boolean(empty)T01101 bitTimer output bit
BooleanBIND0030916 bit → bit 0Read word, then extract bit 0
BooleanBIN[3]D0030916 bit → bit 3Extract bit 3
BooleanBIN[F]D0030916 bit → bit 15Hex index F=15

16-bit Integer (Integer/Word)

data_typeformatplc_addressMemory widthMeaning
Integer(empty)D0030916 bitsigned Word (%DW00309)
IntegerUID0030016 bitUWord (unsigned, client.readUWord)
IntegerUWORDD0030016 bitSame as UI

32-bit Integer (DWord)

data_typeformatplc_addressMemory widthMeaning
IntegerDWD0060032 bitDWord signed (%DD00600 and D00600/00601 combined)
IntegerDWORDD0060032 bitSame as above
IntegerDUWD0069032 bitUDWord (unsigned 32)
IntegerUDWD0069032 bitSame as UDW
IntegerULD0069032 bitULong = UDWord (alias)

64-bit Integer (LWord)

data_typeformatplc_addressMemory widthMeaning
Long(empty)D0070064 bitLWord signed (%DL00700)
LongLWD0070064 bitExplicit LWord
LongLWORDD0070064 bitSame
LongULWD0075064 bitULWord (unsigned 64)

Floating Point (Float / Double)

data_typeformatplc_addressMemory widthMeaning
Float(empty)D0045032 bitIEEE 754 single (%DD00450)
FloatREALD0045032 bitExplicit
FloatDWD0045032 bitHandled as float via the isFloat(dt) branch
DoubleLREALD0050064 bitIEEE 754 double (%DL00500)
Double(empty)D0050064 bitSame as LREAL

String

data_typeformatplc_addressMemory widthMeaning
StringSTRD0080316 bit (1 word)2 chars (low byte first)
StringSTR[5]D0080080 bit (5 words)10 chars, NUL trim
StringSTR[10]D00500160 bit (10 words)20 chars
StringSTR[16]D0010032 words32 chars (32 bytes) — recommended XGI alignment

Using Formulas (LS field patterns)

Purposedata_typefomulaInput → Output
Integer raw → 1 decimal placeFloat${VALUE}*0.11638163.8
Integer raw → 2 decimal placesFloat${VALUE}*0.0112345123.45
signed 16 → unsigned conversionInteger${VALUE}+65536Corrects negative raw values (16-bit conversion)
Zero-point correction (another tag)Float${VALUE}-${TAG_ZERO}1024 - 24 = 1000
Calibration (gain × x + offset)Float${VALUE}*${TAG_GAIN}+${TAG_OFFSET}Correction coefficients in separate tags
Hz conversion (rpm → Hz)Float${VALUE}/60180030.0

For detailed formula usage, see the Formula Reference.

Comprehensive curl Example (all types at once)

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"}
]
}'