Skip to main content

LS XGI / XBC / XBM (FEnet) Driver

Overview​

Communicates with LS ELECTRIC (formerly LS Industrial Systems) 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.jar → FEnetClient)
read✅
write✅ (Bit · Byte · Word · DWord · Float · LWord · Double · String; BIN[n] is read-modify-write)
SecurityNone (assumes internal network)
Native → Java migration

Previously this was NativeProcessDriver-based, launching 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, OS dependencies 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

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


Tag plc_address Format​

FEnet standard (formal format)​

%<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 the FEnet standard.

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 with the same short address, so a single device code can be freely mapped to 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) is a 16-bit signed Word. If you need 32-bit, specify format=DW explicitly. This matches the general 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 when format=DW + data_type=Float is used, 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
BINWord read, then extract bit 0
BIN[5]Extract bit 5
BIN[F]Extract hex F (=15) bit (single characters may be hexadecimal)

Common Errors + Resolution​

Message / SymptomCauseResolution
LS-PLC Ping failed : <ip>ICMP unreachableCheck cabling/firewall. In ICMP-blocked environments, 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 when it is a Word)Leave format empty or specify DW exactly
32-bit float is garbledformat missing → read as a WordSpecify data_type=Float + format=REAL
String characters are garbledLS low-byte-first not accounted forEdge handles this automatically. Take care if you interpret the bytes directly outside Edge
Bit is not readshort form is 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]"
}
]
}'

Reading values:

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

Operational Notes​

  • Read branch priority (LSDriver.read()):
    1. data_type=String or format is STR / STR[N] → readStringMulti
    2. format is BIN or BIN[idx] → word read, then bit extraction
    3. Explicit format branch (UI, DW, UL, LW, REAL, LREAL, etc.) → readByFormat
    4. Fallback: data_type alone (readByDataType)
  • client calls are synchronized, so multi-tag reads on a single OPC are serialized. Other OPCs run on separate threads.
  • write is implemented. Bit-level writes (BIN[n]) read the word, change only the target bit, and write it back — if the same word is written concurrently from elsewhere, the writes overwrite each other.

Example Collection (by data type)​

All combinations frequently used in LS XGI / XBC / XBM field operations, gathered in one 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 0Word read, 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, 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)

Real Numbers (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 word32 chars (32 bytes) — XGI recommended alignment

Using Formulas (LS field patterns)​

Purposedata_typefomulaInput → Output
Integer raw → 1 decimal placeFloat${VALUE}*0.11638 → 163.8
Integer raw → 2 decimal placesFloat${VALUE}*0.0112345 → 123.45
signed 16 → unsigned conversionInteger${VALUE}+65536Negative raw correction (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 a separate tag
Hz conversion (rpm → Hz)Float${VALUE}/601800 → 30.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"}
]
}'