LS XGI / XBC / XBM (FEnet) 驱动
概述
与 LS ELECTRIC(原 LS 产电)的 XGI / XBC / XBM PLC 系列通过 FEnet (Fast Ethernet) 标准协议进行 通信。
| 项目 | 值 |
|---|---|
opc_type | LS |
| 实现类 | plantpulse.driver.protocol.ls.LSDriver |
| 通信库 | 自研 Java 实现 (plantpulse-plc-driver-ls.jar → FEnetClient) |
| read | ✅ |
| write | ❌ (自研 Java 驱动未开放 write API) |
| 安全 | 无(以厂内网为前提) |
以前采用基于 NativeProcessDriver 的方式,即将用 .NET 编写的外部原生二进制文件 (FENetClient.exe) 作为外部进程启动使用。
自 2025 年起已迁移至自研 Java 实现
(plantpulse-plc-driver-ls.jar),直接通过 socket 通信。因此不再有 OS 依赖及外部
进程管理的负担。
OPC 注册表单
| 字段 | 含义 | 示例 |
|---|---|---|
opc_agent_ip | PLC IP | 192.168.0.80 |
opc_agent_port | FEnet 端口 | 2004 (XGT 默认) |
timecycle | 轮询周期 (ms) | 1000 |
options.company-id | FEnet company ID (可选) | (通常留空) |
options.use-checksum | 使用 BCC 校验和 | false (default) |
options.use-hex-bit-index | 位 index 使用十六进制 | false (default) |
options.connect-timeout | 连接超时 (ms) | 3000 |
options.read-timeout | read 超时 (ms) | 3000 |
connect 阶段会先执行 NetUtils.isReachable(ip) ping 检测 → 不可达时立即失败。
标签 plc_address 格式
FEnet 标准(正式格式)
%<DeviceType><DataType><Index>
| 示例 | 含义 |
|---|---|
%DW00309 | Data 区域,Word,index 309 |
%DD00600 | Data 区域,Double Word (32-bit),index 600 |
%DL00800 | Data 区域,Long Word (64-bit),index 800 |
%MX02704 | Memory 区域,Bit,index 0x2704(或十进制 2704) |
%MW00100 | Memory 区域,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_type | format | 自动转换结果 |
|---|---|---|---|
D00309 | Integer | (empty) | %DW00309 |
D00600 | Integer | DW | %DD00600 |
D00800 | Long | (empty) | %DL00800 |
M02704 | Boolean | (empty) | %MX02704 |
D00100 | Float | REAL (或 default) | %DD00100 |
D00200 | Double | LREAL | %DL00200 |
D00300 | String | STR[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);
}
dataCh 由 data_type 和 format 决定(参见下一节)。
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 | %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=(留空)表示 16-bit signed Word。
如需 32-bit,请用 format=DW 显式指定。这与 LS XGI 的通用约定一致。
Float / Double
data_type | format | 宽度 | FEnet | 调用 |
|---|---|---|---|---|
Float / REAL | (empty) / REAL / FLOAT | 32-bit IEEE 754 | %DD | client.readFloat |
Double / LREAL | (empty) / LREAL / DOUBLE | 64-bit IEEE 754 | %DL | client.readDouble |
即使是 format=DW + data_type=Float 的情况,也按 32-bit float 解析(isFloat(dt) 分支)。
Boolean
data_type | format | 含义 |
|---|---|---|
Boolean / Bool / Bit | (empty) / X / BIT | %MX... 或 %PX... 按位 read |
直接使用 short form:M02704 → %MX02704。
String
format | 含义 | 宽度 |
|---|---|---|
STR | 1 个字(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 | 动作 |
|---|---|
BIN | read 字后提取 bit 0 |
BIN[5] | 提取 bit 5 |
BIN[F] | 提取 hex F (=15) 位(单个字符允许十六进制) |
常见错误及解决方法
| 消息 / 现象 | 原因 | 解决 |
|---|---|---|
LS-PLC Ping failed : <ip> | ICMP 不可达 | 检查线缆/防火墙。若环境屏蔽 ICMP,请检查阻断策略 |
LS connect failed | FEnet 模块未运行 / 端口不匹配 | 在 XG5000 中确认 FEnet 模块 IP/Port(通常为 2004) |
| 值始终为 0 | short form → 宽度不匹配(实际为 Word 却按 DWord 读取) | 留空 format 或用 DW 准确指定 |
| 32-bit float 数据错乱 | 缺少 format → 被按 Word 读取 | 显式指定 data_type=Float + format=REAL |
| 字符串乱码 | 未反映 LS 的 low-byte-first | edge 会自动处理。若在外部直接解析 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()):data_type=String或format为STR/STR[N]→readStringMultiformat为BIN或BIN[idx]→ read 字后提取位format显式分支 (UI、DW、UL、LW、REAL、LREAL等) →readByFormat- 回退:仅
data_type(readByDataType)
client调用为 synchronized,因此同一 OPC 的多标签 read 为串行执行。不同 OPC 使用独立线程。- write 未实现。若要使用 Sparkplug NCMD,后续需要开放
FEnetClient.write*。
示例汇总(按数据类型)
将 LS XGI / XBC / XBM 运行现场常用的所有组合整理为一张表。plc_address 以 short form 为准,会自动转换为 %D... 标准表示法。
位 / 布尔 (Boolean)
data_type | format | plc_address | 内存宽度 | 含义 / 备注 |
|---|---|---|---|---|
Boolean | (empty) | M02704 | 1 bit | 内存区域位。最常见的形式 |
Boolean | (empty) | P0001F | 1 bit | LS XGI 十六进制位索引 (F=15)。当 use-hex-bit-index=true 时 |
Boolean | (empty) | K00010 | 1 bit | Keep relay 位 |
Boolean | (empty) | T0110 | 1 bit | Timer 输出位 |
Boolean | BIN | D00309 | 16 bit → bit 0 | read 字后提取第 0 位 |
Boolean | BIN[3] | D00309 | 16 bit → bit 3 | 提取 bit 3 |
Boolean | BIN[F] | D00309 | 16 bit → bit 15 | hex 索引 F=15 |
16-bit 整数 (Integer/Word)
data_type | format | plc_address | 内存宽度 | 含义 |
|---|---|---|---|---|
Integer | (empty) | D00309 | 16 bit | signed Word (%DW00309) |
Integer | UI | D00300 | 16 bit | UWord (unsigned, client.readUWord) |
Integer | UWORD | D00300 | 16 bit | 与 UI 相同 |
32-bit 整数 (DWord)
data_type | format | plc_address | 内存宽度 | 含义 |
|---|---|---|---|---|
Integer | DW | D00600 | 32 bit | DWord signed (%DD00600、D00600/00601 组合) |
Integer | DWORD | D00600 | 32 bit | 同上 |
Integer | DUW | D00690 | 32 bit | UDWord (unsigned 32) |
Integer | UDW | D00690 | 32 bit | 与 UDW 相同 |
Integer | UL | D00690 | 32 bit | ULong = UDWord (alias) |
64-bit 整数 (LWord)
data_type | format | plc_address | 内存宽度 | 含义 |
|---|---|---|---|---|
Long | (empty) | D00700 | 64 bit | LWord signed (%DL00700) |
Long | LW | D00700 | 64 bit | 显式 LWord |
Long | LWORD | D00700 | 64 bit | 相同 |
Long | ULW | D00750 | 64 bit | ULWord (unsigned 64) |
实数 (Float / Double)
data_type | format | plc_address | 内存宽度 | 含义 |
|---|---|---|---|---|
Float | (empty) | D00450 | 32 bit | IEEE 754 single (%DD00450) |
Float | REAL | D00450 | 32 bit | 显式 |
Float | DW | D00450 | 32 bit | 通过 isFloat(dt) 分支按 float 处理 |
Double | LREAL | D00500 | 64 bit | IEEE 754 double (%DL00500) |
Double | (empty) | D00500 | 64 bit | 与 LREAL 相同 |
字符串 (String)
data_type | format | plc_address | 内存宽度 | 含义 |
|---|---|---|---|---|
String | STR | D00803 | 16 bit (1 word) | 2 chars (low byte first) |
String | STR[5] | D00800 | 80 bit (5 words) | 10 chars, NUL trim |
String | STR[10] | D00500 | 160 bit (10 words) | 20 chars |
String | STR[16] | D00100 | 32 word | 32 chars (32 byte) — XGI 推荐对齐 |
Formula 应用(LS 现场模式)
| 用途 | data_type | fomula | 输入 → 输出 |
|---|---|---|---|
| 整数 raw → 保留 1 位小数 | Float | ${VALUE}*0.1 | 1638 → 163.8 |
| 整数 raw → 保留 2 位小数 | Float | ${VALUE}*0.01 | 12345 → 123.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}/60 | 1800 → 30.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"}
]
}'