From ffb0947e32e424da98d76682b22e04c2b09b8c11 Mon Sep 17 00:00:00 2001 From: liyongde <1419499670@qq.com> Date: Wed, 11 Mar 2026 10:34:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20s7=E5=9C=B0=E5=9D=80=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plcs7/PlcS7ProtocolDriverImpl.java | 124 ++++++++++++++++-- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java index 5342e1f..f325a74 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java @@ -214,14 +214,122 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService { */ private PlcS7PointVariable getPointVariable(IotConfig config, String type) { log.debug("Plc S7 Point Attribute Config {}", config); - // todo: DB 块地址解析 -// return new PlcS7PointVariable( -// config.get("dbNum").getValueByClass(Integer.class), -// config.get("byteOffset").getValueByClass(Integer.class), -// config.get("bitOffset").getValueByClass(Integer.class), -// config.get("blockSize").getValueByClass(Integer.class), -// type); - return null; + // DB 块地址解析 + return parseS7Address(config.getRegisterAddress(), type); + } + + // 通用解析方法:支持DB格式(DB1.DBX10.3)和S7-200格式(VB1、VD4、V1.3) + public static PlcS7PointVariable parseS7Address(String address, String type) { + if (address == null || address.trim().isEmpty()) { + throw new IllegalArgumentException("S7地址不能为空!"); + } + String cleanAddress = address.trim().toUpperCase(); + int dbNum = 0; + String area = ""; + int byteOffset = -1; + int bitOffset = -1; + int size = -1; + + // ========== 分支1:解析S7-1200/1500的DB格式(DB1.DBX10.3、DB2.DBD5) ========== + if (cleanAddress.startsWith("DB")) { + area = "DB"; + // 拆分DB编号和地址主体(DB1.DBX10.3 → DB1 和 DBX10.3) + String[] dbAndAddress = cleanAddress.split("\\.DB", 2); + if (dbAndAddress.length != 2) { + throw new IllegalArgumentException("DB地址格式错误:" + address); + } + // 解析DB编号 + String dbNumStr = dbAndAddress[0].replace("DB", ""); + try { + dbNum = Integer.parseInt(dbNumStr); + if (dbNum < 1) throw new IllegalArgumentException("DB编号必须≥1:" + dbNumStr); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("DB编号不是有效数字:" + dbNumStr, e); + } + // 解析类型和偏移(DBX10.3 → X10.3) + String addressBody = "DB" + dbAndAddress[1]; + char typeSuffix = addressBody.charAt(2); + String offsetStr = addressBody.substring(3); + // 按类型解析 + switch (typeSuffix) { + case 'X': // BOOL + String[] byteBit = offsetStr.split("\\."); + if (byteBit.length != 2) throw new IllegalArgumentException("BOOL地址需包含位偏移:" + address); + byteOffset = parseIntCheck(byteBit[0], "字节偏移"); + bitOffset = parseIntCheck(byteBit[1], "位偏移"); + if (bitOffset < 0 || bitOffset > 7) throw new IllegalArgumentException("位偏移需0-7:" + bitOffset); + size = 1; + break; + case 'B': // BYTE + byteOffset = parseIntCheck(offsetStr, "字节偏移"); + size = 1; + break; + case 'W': // WORD + byteOffset = parseIntCheck(offsetStr, "字节偏移"); + size = 2; + break; + case 'D': // DWORD/REAL + byteOffset = parseIntCheck(offsetStr, "字节偏移"); + size = 4; + break; + case 'R': // REAL + byteOffset = parseIntCheck(offsetStr, "字节偏移"); + size = 4; + break; + default: + throw new IllegalArgumentException("不支持的DB类型后缀:" + typeSuffix); + } + + // ========== 分支2:解析S7-200的VB/VD/V1.3格式 ========== + } else { + // 第一步:判断是BOOL位(V1.3、I0.1)还是字节/双字(VB1、VD4) + if (cleanAddress.contains(".")) { + // 处理BOOL位(V1.3、I0.1、M2.5) + String[] areaByteBit = cleanAddress.split("\\."); + if (areaByteBit.length != 2) throw new IllegalArgumentException("S7-200 BOOL地址格式错误:" + address); + // 拆分存储区和字节偏移(V1 → V 和 1) + String areaByteStr = areaByteBit[0]; + area = areaByteStr.substring(0, 1); // 取第一个字符(V/I/Q/M/SM) + String byteOffsetStr = areaByteStr.substring(1); + // 校验存储区合法性 + if (!"VIQMSM".contains(area)) throw new IllegalArgumentException("不支持的S7-200存储区:" + area); + // 解析字节和位偏移 + byteOffset = parseIntCheck(byteOffsetStr, "字节偏移"); + bitOffset = parseIntCheck(areaByteBit[1], "位偏移"); + if (bitOffset < 0 || bitOffset > 7) throw new IllegalArgumentException("位偏移需0-7:" + bitOffset); + size = 1; // BOOL占1字节 + + } else { + // 处理字节/双字(VB1、VD4、IW2、MB3) + // 拆分存储区+类型 和 地址(VB1 → VB 和 1;VD4 → VD 和 4) + char typeSuffix = cleanAddress.charAt(1); // 第二个字符是类型(B/W/D) + area = cleanAddress.substring(0, 1); // 第一个字符是存储区(V/I/Q/M/SM) + String offsetStr = cleanAddress.substring(2); + // 校验存储区和类型 + if (!"VIQMSM".contains(area)) throw new IllegalArgumentException("不支持的S7-200存储区:" + area); + if (!"BWD".contains(String.valueOf(typeSuffix))) throw new IllegalArgumentException("不支持的S7-200类型后缀:" + typeSuffix); + // 解析字节偏移和大小 + byteOffset = parseIntCheck(offsetStr, "字节偏移"); + switch (typeSuffix) { + case 'B': size = 1; break; // BYTE + case 'W': size = 2; break; // WORD + case 'D': size = 4; break; // DWORD/REAL + } + } + } + + // 校验字节偏移合法性 + if (byteOffset < 0) throw new IllegalArgumentException("字节偏移不能为负数:" + byteOffset); + return new PlcS7PointVariable(dbNum, byteOffset, bitOffset, size, type); + } + + // 工具方法:解析数字并捕获异常 + private static int parseIntCheck(String str, String fieldName) { + try { + return Integer.parseInt(str); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(fieldName + "不是有效数字:" + str, e); + } } /**