PLC批量读取方案

PLC批量读取方案 上位机批量读取plc信号两个方案方案一: plc不改动上位机动态读取解析偏移量和数据类型方案二: plc改动将不通DB块写入一个连续的字节数组中效率最高方案一代码[ { varName: 测试1, dataType: BOOL, byteOffset: 0, bitOffset: 0, arrayLength: 1 }, { varName: 测试2, dataType: BOOL, byteOffset: 2, bitOffset: 0, arrayLength: 3 }, { varName: 测试3, dataType: BYTE, byteOffset: 4, bitOffset: 0, arrayLength: 3 }, { varName: 测试4, dataType: BYTE, byteOffset: 8, bitOffset: 0, arrayLength: 1 }, { varName: 测试5, dataType: BOOL, byteOffset: 9, bitOffset: 0, arrayLength: 1 } ]package org.jeecg.plcTest; import lombok.Data; /** * Author: 赵志强 * CreateTime: 2026-06-30 08:42 * Description: PLC DB变量元数据,可从JSON/数据库加载完全动态配置偏移、类型、数组长度 * Version: 1.0 */ Data public class PlcDbVarMeta { // 变量名称 private String varName; // 数据类型BOOL / BYTE / SHORT / USHORT / INT / UINT / FLOAT / DOUBLE private String dataType; // 字节偏移偏移X.Y中的X private int byteOffset; // 位偏移偏移X.Y中的Y仅BOOL生效 private int bitOffset; // 数组长度非数组1Array[0..2]3 private int arrayLength; }package org.jeecg.plcTest; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.util.List; /** * 加载JSON配置文件自动转为变量元数据列表完全动态无需改代码 */ public class PlcMetaConfigLoader { private static final ObjectMapper OBJECT_MAPPER new ObjectMapper(); /** * 从JSON文件加载DB变量配置 */ public static ListPlcDbVarMeta loadMetaFromJsonFile(String jsonPath) throws Exception { File jsonFile new File(jsonPath); return OBJECT_MAPPER.readValue(jsonFile, new TypeReferenceListPlcDbVarMeta() {}); } /** * JSON字符串直接加载数据库读取配置字符串场景 */ public static ListPlcDbVarMeta loadMetaFromJsonStr(String jsonStr) throws Exception { return OBJECT_MAPPER.readValue(jsonStr, new TypeReferenceListPlcDbVarMeta() {}); } }package org.jeecg.plcTest; import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; import java.util.List; import java.util.Map; public class PlcParseTestMain { public static void main(String[] args) throws Exception { // 1. 加载动态变量配置修改JSON即可更新DB偏移/变量无需改Java代码 // Windows示例 String absolutePath E:\\javaspace\\zzq_v4\\ircm\\ircm-parent\\ircm-web-start\\src\\test\\java\\org\\jeecg\\plcTest\\db1_config.json; ListPlcDbVarMeta metaList PlcMetaConfigLoader.loadMetaFromJsonFile(absolutePath); S7PLC plc new S7PLC(EPlcType.S1200, 192.168.1.158); byte[] dbRawBytes plc.readByte(DB1.0.0,10); // 3. 原始字节转十进制数组和你上位机界面输出一致 ListInteger decimalByteArr S7DbParseUtil.byteToDecimalList(dbRawBytes); System.out.println(DB原始10字节十进制数组 decimalByteArr); // 4. 动态解析所有变量 MapString, Object varResultMap S7DbParseUtil.parseDbAllVars(dbRawBytes, metaList); System.out.println( 变量解析结果 ); for (Map.EntryString, Object entry : varResultMap.entrySet()) { String name entry.getKey(); String printVal S7DbParseUtil.formatPrintValue(entry.getValue()); System.out.printf(%s %s%n, name, printVal); } } }package org.jeecg.plcTest; import java.nio.ByteOrder; import java.util.*; import java.nio.ByteOrder; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.HashMap; /** * S7 DB字节解析工具数组返回原生对象外部统一格式化打印 */ public class S7DbParseUtil { private static final ByteOrder S7_BYTE_ORDER ByteOrder.BIG_ENDIAN; /** * 批量解析全部变量返回变量-原始值Map数组返回数组对象 */ public static MapString, Object parseDbAllVars(byte[] dbRawBytes, ListPlcDbVarMeta metaList) { MapString, Object resultMap new HashMap(); for (PlcDbVarMeta meta : metaList) { Object value parseSingleVar(dbRawBytes, meta); resultMap.put(meta.getVarName(), value); } return resultMap; } /** * 解析单个变量返回原生对象数组返回数组实例 */ public static Object parseSingleVar(byte[] rawBytes, PlcDbVarMeta meta) { int byteOff meta.getByteOffset(); int arrLen meta.getArrayLength(); String type meta.getDataType().toUpperCase(); // 越界校验 int singleByte calcTypeSingleByte(type); int needByte byteOff singleByte * arrLen; if (rawBytes.length needByte) { return null; } switch (type) { case BOOL: return parseBool(rawBytes, byteOff, meta.getBitOffset(), arrLen); case BYTE: return parseByte(rawBytes, byteOff, arrLen); case SHORT: return parseShort(rawBytes, byteOff, arrLen, false); case USHORT: return parseShort(rawBytes, byteOff, arrLen, true); case INT: return parseInt(rawBytes, byteOff, arrLen, false); case UINT: return parseInt(rawBytes, byteOff, arrLen, true); case FLOAT: return parseFloat(rawBytes, byteOff, arrLen); case DOUBLE: return parseDouble(rawBytes, byteOff, arrLen); default: throw new IllegalArgumentException(不支持类型 meta.getDataType()); } } /** * 获取单个基础类型占用字节 */ private static int calcTypeSingleByte(String type) { switch (type) { case BOOL: case BYTE: return 1; case SHORT: case USHORT: return 2; case INT: case UINT: case FLOAT: return 4; case DOUBLE: return 8; default: throw new IllegalArgumentException(未知类型); } } // BOOL private static Object parseBool(byte[] raw, int baseByte, int baseBit, int arrLen) { if (arrLen 1) { return getBit(raw[baseByte], baseBit); } else { Boolean[] arr new Boolean[arrLen]; for (int i 0; i arrLen; i) { arr[i] getBit(raw[baseByte], i); } return arr; } } private static boolean getBit(byte b, int bitIndex) { return (b (1 bitIndex)) ! 0; } // BYTE private static Object parseByte(byte[] raw, int baseOff, int arrLen) { if (arrLen 1) { return Byte.toUnsignedInt(raw[baseOff]); } else { Integer[] arr new Integer[arrLen]; for (int i 0; i arrLen; i) { arr[i] Byte.toUnsignedInt(raw[baseOff i]); } return arr; } } // SHORT / USHORT private static Object parseShort(byte[] raw, int baseOff, int arrLen, boolean unsigned) { if (arrLen 1) { if (unsigned) { return ((raw[baseOff] 0xFF) 8) | (raw[baseOff 1] 0xFF); } else { return (short) ((raw[baseOff] 8) | (raw[baseOff 1] 0xFF)); } } else { Object[] arr new Object[arrLen]; for (int i 0; i arrLen; i) { int off baseOff i * 2; if (unsigned) { arr[i] ((raw[off] 0xFF) 8) | (raw[off 1] 0xFF); } else { arr[i] (short) ((raw[off] 8) | (raw[off 1] 0xFF)); } } return arr; } } // INT / UINT private static Object parseInt(byte[] raw, int baseOff, int arrLen, boolean unsigned) { if (arrLen 1) { int val (raw[baseOff] 24) | ((raw[baseOff 1] 0xFF) 16) | ((raw[baseOff 2] 0xFF) 8) | (raw[baseOff 3] 0xFF); return unsigned ? Integer.toUnsignedLong(val) : val; } else { Object[] arr new Object[arrLen]; for (int i 0; i arrLen; i) { int off baseOff i * 4; int val (raw[off] 24) | ((raw[off 1] 0xFF) 16) | ((raw[off 2] 0xFF) 8) | (raw[off 3] 0xFF); arr[i] unsigned ? Integer.toUnsignedLong(val) : val; } return arr; } } // FLOAT private static Object parseFloat(byte[] raw, int baseOff, int arrLen) { if (arrLen 1) { int bits (raw[baseOff] 24) | ((raw[baseOff 1] 0xFF) 16) | ((raw[baseOff 2] 0xFF) 8) | (raw[baseOff 3] 0xFF); return Float.intBitsToFloat(bits); } else { Float[] arr new Float[arrLen]; for (int i 0; i arrLen; i) { int off baseOff i * 4; int bits (raw[off] 24) | ((raw[off 1] 0xFF) 16) | ((raw[off 2] 0xFF) 8) | (raw[off 3] 0xFF); arr[i] Float.intBitsToFloat(bits); } return arr; } } // DOUBLE private static Object parseDouble(byte[] raw, int baseOff, int arrLen) { if (arrLen 1) { long bits ((long) raw[baseOff] 56) | ((long) (raw[baseOff 1] 0xFF) 48) | ((long) (raw[baseOff 2] 0xFF) 40) | ((long) (raw[baseOff 3] 0xFF) 32) | ((long) (raw[baseOff 4] 0xFF) 24) | ((long) (raw[baseOff 5] 0xFF) 16) | ((long) (raw[baseOff 6] 0xFF) 8) | (raw[baseOff 7] 0xFF); return Double.longBitsToDouble(bits); } else { Double[] arr new Double[arrLen]; for (int i 0; i arrLen; i) { int off baseOff i * 8; long bits ((long) raw[off] 56) | ((long) (raw[off 1] 0xFF) 48) | ((long) (raw[off 2] 0xFF) 40) | ((long) (raw[off 3] 0xFF) 32) | ((long) (raw[off 4] 0xFF) 24) | ((long) (raw[off 5] 0xFF) 16) | ((long) (raw[off 6] 0xFF) 8) | (raw[off 7] 0xFF); arr[i] Double.longBitsToDouble(bits); } return arr; } } /** * 原始byte数组转十进制数字列表上位机打印原始报文 */ public static ListInteger byteToDecimalList(byte[] raw) { ListInteger list new java.util.ArrayList(); for (byte b : raw) { list.add(Byte.toUnsignedInt(b)); } return list; } /** * 格式化任意值数组自动转为可读字符串解决 [Lxxx;xxx 打印问题 */ public static String formatPrintValue(Object val) { if (val instanceof Boolean[]) { return Arrays.toString((Boolean[]) val); } else if (val instanceof Integer[]) { return Arrays.toString((Integer[]) val); } else if (val instanceof Short[]) { return Arrays.toString((Short[]) val); } else if (val instanceof Long[]) { return Arrays.toString((Long[]) val); } else if (val instanceof Float[]) { return Arrays.toString((Float[]) val); } else if (val instanceof Double[]) { return Arrays.toString((Double[]) val); } else if (val instanceof Object[]) { return Arrays.toString((Object[]) val); } else { return String.valueOf(val); } } }方案二: