PLC批量读取方案
2026/7/1 18:04:04 网站建设 项目流程

上位机批量读取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; // 数组长度:非数组=1,Array[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 List<PlcDbVarMeta> loadMetaFromJsonFile(String jsonPath) throws Exception { File jsonFile = new File(jsonPath); return OBJECT_MAPPER.readValue(jsonFile, new TypeReference<List<PlcDbVarMeta>>() {}); } /** * JSON字符串直接加载(数据库读取配置字符串场景) */ public static List<PlcDbVarMeta> loadMetaFromJsonStr(String jsonStr) throws Exception { return OBJECT_MAPPER.readValue(jsonStr, new TypeReference<List<PlcDbVarMeta>>() {}); } }
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"; List<PlcDbVarMeta> metaList = PlcMetaConfigLoader.loadMetaFromJsonFile(absolutePath); S7PLC plc = new S7PLC(EPlcType.S1200, "192.168.1.158"); byte[] dbRawBytes =plc.readByte("DB1.0.0",10); // 3. 原始字节转十进制数组(和你上位机界面输出一致) List<Integer> decimalByteArr = S7DbParseUtil.byteToDecimalList(dbRawBytes); System.out.println("DB原始10字节十进制数组:" + decimalByteArr); // 4. 动态解析所有变量 Map<String, Object> varResultMap = S7DbParseUtil.parseDbAllVars(dbRawBytes, metaList); System.out.println("===== 变量解析结果 ====="); for (Map.Entry<String, 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 Map<String, Object> parseDbAllVars(byte[] dbRawBytes, List<PlcDbVarMeta> metaList) { Map<String, 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 List<Integer> byteToDecimalList(byte[] raw) { List<Integer> 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); } } }

方案二:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询