跳到主要内容

LS XGI / XBC / XBM (FEnet) 驱动

概述

与 LS ELECTRIC(原 LS 产电)的 XGI / XBC / XBM PLC 系列通过 FEnet (Fast Ethernet) 标准协议进行 通信。

项目
opc_typeLS
实现类plantpulse.driver.protocol.ls.LSDriver
通信库自研 Java 实现 (plantpulse-plc-driver-ls.jarFEnetClient)
read
write❌ (自研 Java 驱动未开放 write API)
安全无(以厂内网为前提)
原生 → Java 迁移

以前采用基于 NativeProcessDriver 的方式,即将用 .NET 编写的外部原生二进制文件 (FENetClient.exe) 作为外部进程启动使用。 自 2025 年起已迁移至自研 Java 实现 (plantpulse-plc-driver-ls.jar),直接通过 socket 通信。因此不再有 OS 依赖及外部 进程管理的负担。


OPC 注册表单

字段含义示例
opc_agent_ipPLC IP192.168.0.80
opc_agent_portFEnet 端口2004 (XGT 默认)
timecycle轮询周期 (ms)1000
options.company-idFEnet company ID (可选)(通常留空)
options.use-checksum使用 BCC 校验和false (default)
options.use-hex-bit-index位 index 使用十六进制false (default)
options.connect-timeout连接超时 (ms)3000
options.read-timeoutread 超时 (ms)3000

connect 阶段会先执行 NetUtils.isReachable(ip) ping 检测 → 不可达时立即失败。


标签 plc_address 格式

FEnet 标准(正式格式)

%<DeviceType><DataType><Index>
示例含义
%DW00309Data 区域,Word,index 309
%DD00600Data 区域,Double Word (32-bit),index 600
%DL00800Data 区域,Long Word (64-bit),index 800
%MX02704Memory 区域,Bit,index 0x2704(或十进制 2704)
%MW00100Memory 区域,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_typeformat自动转换结果
D00309Integer(empty)%DW00309
D00600IntegerDW%DD00600
D00800Long(empty)%DL00800
M02704Boolean(empty)%MX02704
D00100FloatREAL (或 default)%DD00100
D00200DoubleLREAL%DL00200
D00300StringSTR[10]%DW00300 开始 read 10 个字

内部实现 (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);
}

dataChdata_typeformat 决定(参见下一节)。


data_type / format 映射(详解)

LS 驱动即使使用相同的 short address,也可仅通过 format显式改变内存宽度,因此可将同一 设备的设备代码自由映射为 16/32/64-bit。

Integer 系列 (data_type=Integer / Short / Int / Int16 …)

format含义宽度FEnet DataType调用
(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
未指定 format 的含义

D00309 + data_type=Integer + format=(留空)表示 16-bit signed Word。 如需 32-bit,请用 format=DW 显式指定。这与 LS XGI 的通用约定一致。

Float / Double

data_typeformat宽度FEnet调用
Float / REAL(empty) / REAL / FLOAT32-bit IEEE 754%DDclient.readFloat
Double / LREAL(empty) / LREAL / DOUBLE64-bit IEEE 754%DLclient.readDouble

即使是 format=DW + data_type=Float 的情况,也按 32-bit float 解析(isFloat(dt) 分支)。

Boolean

data_typeformat含义
Boolean / Bool / Bit(empty) / X / BIT%MX...%PX... 按位 read

直接使用 short form:M02704%MX02704

String

format含义宽度
STR1 个字(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 个字后仅提取 bit idx(与 Modbus 相同约定)。

format动作
BINread 字后提取 bit 0
BIN[5]提取 bit 5
BIN[F]提取 hex F (=15) 位(单个字符允许十六进制)

常见错误及解决方法

消息 / 现象原因解决
LS-PLC Ping failed : <ip>ICMP 不可达检查线缆/防火墙。若环境屏蔽 ICMP,请检查阻断策略
LS connect failedFEnet 模块未运行 / 端口不匹配在 XG5000 中确认 FEnet 模块 IP/Port(通常为 2004)
值始终为 0short form → 宽度不匹配(实际为 Word 却按 DWord 读取)留空 format 或用 DW 准确指定
32-bit float 数据错乱缺少 format → 被按 Word 读取显式指定 data_type=Float + format=REAL
字符串乱码未反映 LS 的 low-byte-firstedge 会自动处理。若在外部直接解析 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()):
    1. data_type=StringformatSTR / STR[N]readStringMulti
    2. formatBINBIN[idx] → read 字后提取位
    3. format 显式分支 (UIDWULLWREALLREAL 等) → readByFormat
    4. 回退:仅 data_type (readByDataType)
  • client 调用为 synchronized,因此同一 OPC 的多标签 read 为串行执行。不同 OPC 使用独立线程。
  • write 未实现。若要使用 Sparkplug NCMD,后续需要开放 FEnetClient.write*

示例汇总(按数据类型)

将 LS XGI / XBC / XBM 运行现场常用的所有组合整理为一张表。plc_address 以 short form 为准,会自动转换为 %D... 标准表示法。

位 / 布尔 (Boolean)

data_typeformatplc_address内存宽度含义 / 备注
Boolean(empty)M027041 bit内存区域位。最常见的形式
Boolean(empty)P0001F1 bitLS XGI 十六进制位索引 (F=15)。当 use-hex-bit-index=true
Boolean(empty)K000101 bitKeep relay 位
Boolean(empty)T01101 bitTimer 输出位
BooleanBIND0030916 bit → bit 0read 字后提取第 0 位
BooleanBIN[3]D0030916 bit → bit 3提取 bit 3
BooleanBIN[F]D0030916 bit → bit 15hex 索引 F=15

16-bit 整数 (Integer/Word)

data_typeformatplc_address内存宽度含义
Integer(empty)D0030916 bitsigned Word (%DW00309)
IntegerUID0030016 bitUWord (unsigned, client.readUWord)
IntegerUWORDD0030016 bit与 UI 相同

32-bit 整数 (DWord)

data_typeformatplc_address内存宽度含义
IntegerDWD0060032 bitDWord signed (%DD00600D00600/00601 组合)
IntegerDWORDD0060032 bit同上
IntegerDUWD0069032 bitUDWord (unsigned 32)
IntegerUDWD0069032 bit与 UDW 相同
IntegerULD0069032 bitULong = UDWord (alias)

64-bit 整数 (LWord)

data_typeformatplc_address内存宽度含义
Long(empty)D0070064 bitLWord signed (%DL00700)
LongLWD0070064 bit显式 LWord
LongLWORDD0070064 bit相同
LongULWD0075064 bitULWord (unsigned 64)

实数 (Float / Double)

data_typeformatplc_address内存宽度含义
Float(empty)D0045032 bitIEEE 754 single (%DD00450)
FloatREALD0045032 bit显式
FloatDWD0045032 bit通过 isFloat(dt) 分支按 float 处理
DoubleLREALD0050064 bitIEEE 754 double (%DL00500)
Double(empty)D0050064 bit与 LREAL 相同

字符串 (String)

data_typeformatplc_address内存宽度含义
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 byte) — XGI 推荐对齐

Formula 应用(LS 现场模式)

用途data_typefomula输入 → 输出
整数 raw → 保留 1 位小数Float${VALUE}*0.11638163.8
整数 raw → 保留 2 位小数Float${VALUE}*0.0112345123.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}/60180030.0

formula 的详细用法请参考 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"}
]
}'