|
|
@@ -151,112 +151,251 @@ public class ModbusUtil {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 生成设备请求列表
|
|
|
+ * 按IP:端口 -> 协议ID -> 从机ID -> 功能码 -> 数据类型的层次结构分组
|
|
|
+ */
|
|
|
public static List<DeviceRequest> getDeviceRequests(List<EquipmentParam> equipmentParamList) {
|
|
|
- // 保存modbus请求信息
|
|
|
+ if (equipmentParamList == null || equipmentParamList.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
List<DeviceRequest> deviceRequestList = new ArrayList<>();
|
|
|
|
|
|
- // 根据IP地址和端口进行分组
|
|
|
- Map<String, List<EquipmentParam>> ipPortGroupMap = new HashMap<>();
|
|
|
+ // 按IP:端口分组
|
|
|
+ Map<String, List<EquipmentParam>> ipPortGroups = groupByIpPort(equipmentParamList);
|
|
|
|
|
|
- // 按IP地址和端口分组设备参数
|
|
|
- for (EquipmentParam equipment : equipmentParamList) {
|
|
|
+ // 处理每个IP:端口组
|
|
|
+ for (Map.Entry<String, List<EquipmentParam>> entry : ipPortGroups.entrySet()) {
|
|
|
+ String[] ipPort = parseIpPort(entry.getKey());
|
|
|
+ String ipAddress = ipPort[0];
|
|
|
+ Integer port = Integer.parseInt(ipPort[1]);
|
|
|
+ List<EquipmentParam> groupEquipmentList = entry.getValue();
|
|
|
+
|
|
|
+ // 按协议ID分组
|
|
|
+ Map<Integer, List<EquipmentParam>> protocolGroups = groupByProtocol(groupEquipmentList);
|
|
|
+
|
|
|
+ // 处理每个协议组
|
|
|
+ for (Map.Entry<Integer, List<EquipmentParam>> protocolEntry : protocolGroups.entrySet()) {
|
|
|
+ Integer protocolId = protocolEntry.getKey();
|
|
|
+ List<EquipmentParam> protocolEquipmentList = protocolEntry.getValue();
|
|
|
+
|
|
|
+ // 按从机ID分组
|
|
|
+ Map<Integer, List<EquipmentParam>> slaveGroups = groupBySlaveId(protocolEquipmentList);
|
|
|
+
|
|
|
+ // 处理每个从机组
|
|
|
+ for (Map.Entry<Integer, List<EquipmentParam>> slaveEntry : slaveGroups.entrySet()) {
|
|
|
+ Integer unitId = slaveEntry.getKey();
|
|
|
+ List<EquipmentParam> slaveEquipmentList = slaveEntry.getValue();
|
|
|
+
|
|
|
+ // 生成该从机的设备请求
|
|
|
+ List<DeviceRequest> requests = generateDeviceRequestsForSlave(
|
|
|
+ slaveEquipmentList, ipAddress, port, unitId, protocolId);
|
|
|
+ deviceRequestList.addAll(requests);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return deviceRequestList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按IP和端口分组
|
|
|
+ */
|
|
|
+ private static Map<String, List<EquipmentParam>> groupByIpPort(List<EquipmentParam> equipmentList) {
|
|
|
+ Map<String, List<EquipmentParam>> groups = new HashMap<>();
|
|
|
+ for (EquipmentParam equipment : equipmentList) {
|
|
|
String ipAddress = equipment.getIpAddress();
|
|
|
Integer port = equipment.getPort();
|
|
|
if (ipAddress != null && port != null) {
|
|
|
String key = ipAddress + ":" + port;
|
|
|
- ipPortGroupMap.computeIfAbsent(key, k -> new ArrayList<>()).add(equipment);
|
|
|
+ groups.computeIfAbsent(key, k -> new ArrayList<>()).add(equipment);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按协议ID分组
|
|
|
+ */
|
|
|
+ private static Map<Integer, List<EquipmentParam>> groupByProtocol(List<EquipmentParam> equipmentList) {
|
|
|
+ Map<Integer, List<EquipmentParam>> groups = new HashMap<>();
|
|
|
+ for (EquipmentParam equipment : equipmentList) {
|
|
|
+ Integer protocolId = equipment.getProtocolId();
|
|
|
+ if (protocolId != null) {
|
|
|
+ groups.computeIfAbsent(protocolId, k -> new ArrayList<>()).add(equipment);
|
|
|
}
|
|
|
}
|
|
|
+ return groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按从机ID分组
|
|
|
+ */
|
|
|
+ private static Map<Integer, List<EquipmentParam>> groupBySlaveId(List<EquipmentParam> equipmentList) {
|
|
|
+ Map<Integer, List<EquipmentParam>> groups = new HashMap<>();
|
|
|
+ for (EquipmentParam equipment : equipmentList) {
|
|
|
+ Integer unitId = equipment.getUnitId();
|
|
|
+ if (unitId != null) {
|
|
|
+ groups.computeIfAbsent(unitId, k -> new ArrayList<>()).add(equipment);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析IP:端口字符串
|
|
|
+ */
|
|
|
+ private static String[] parseIpPort(String ipPort) {
|
|
|
+ return ipPort.split(":");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为单个从机生成设备请求
|
|
|
+ */
|
|
|
+ private static List<DeviceRequest> generateDeviceRequestsForSlave(
|
|
|
+ List<EquipmentParam> slaveEquipmentList,
|
|
|
+ String ipAddress,
|
|
|
+ Integer port,
|
|
|
+ Integer unitId,
|
|
|
+ Integer protocolId) {
|
|
|
|
|
|
- // 对每个IP地址和端口组合进行处理
|
|
|
- for (Map.Entry<String, List<EquipmentParam>> entry : ipPortGroupMap.entrySet()) {
|
|
|
- String[] ipPort = entry.getKey().split(":");
|
|
|
- String ipAddress = ipPort[0];
|
|
|
- Integer port = Integer.parseInt(ipPort[1]);
|
|
|
- List<EquipmentParam> groupEquipmentList = entry.getValue();
|
|
|
-
|
|
|
- // 根据功能码分类的设备信息
|
|
|
- Map<String, List<EquipmentParam>> responseMap = new HashMap<>();
|
|
|
- // 根据功能码分类的设备地址信息
|
|
|
- Map<String, Map<String, List<Integer>>> addressMap = new HashMap<>();
|
|
|
+ List<DeviceRequest> requests = new ArrayList<>();
|
|
|
+
|
|
|
+ // 按功能码和数据类型分组
|
|
|
+ Map<String, Map<String, List<Integer>>> addressGroups = groupByFunctionCodeAndDataType(slaveEquipmentList);
|
|
|
+
|
|
|
+ // 为每个功能码和数据类型组合生成请求
|
|
|
+ for (Map.Entry<String, Map<String, List<Integer>>> functionEntry : addressGroups.entrySet()) {
|
|
|
+ String functionCode = functionEntry.getKey();
|
|
|
+ Map<String, List<Integer>> dataTypeGroups = functionEntry.getValue();
|
|
|
|
|
|
- // 线圈数据
|
|
|
- responseMap.put(COIL_FUNCTION_CODE, new ArrayList<>());
|
|
|
- // 输入寄存器数据
|
|
|
- responseMap.put(INPUT_FUNCTION_CODE, new ArrayList<>());
|
|
|
- // 保持寄存器数据
|
|
|
- responseMap.put(HOLDING_FUNCTION_CODE, new ArrayList<>());
|
|
|
+ for (Map.Entry<String, List<Integer>> dataTypeEntry : dataTypeGroups.entrySet()) {
|
|
|
+ String dataType = dataTypeEntry.getKey();
|
|
|
+ List<Integer> addresses = dataTypeEntry.getValue();
|
|
|
+
|
|
|
+ if (addresses.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成分段读取请求
|
|
|
+ List<DeviceRequest> segmentRequests = generateSegmentRequests(
|
|
|
+ addresses, functionCode, dataType, ipAddress, port, unitId, protocolId);
|
|
|
+ requests.addAll(segmentRequests);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return requests;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按功能码和数据类型分组地址
|
|
|
+ */
|
|
|
+ private static Map<String, Map<String, List<Integer>>> groupByFunctionCodeAndDataType(List<EquipmentParam> equipmentList) {
|
|
|
+ Map<String, Map<String, List<Integer>>> groups = new HashMap<>();
|
|
|
+
|
|
|
+ // 初始化分组结构
|
|
|
+ initializeGroupStructure(groups);
|
|
|
+
|
|
|
+ // 填充数据
|
|
|
+ for (EquipmentParam equipment : equipmentList) {
|
|
|
+ String registerCode = equipment.getRegisterCode();
|
|
|
+ String dataType = equipment.getDataType();
|
|
|
+ Integer address = equipment.getAddress();
|
|
|
|
|
|
- // 线圈数据存储地址
|
|
|
- Map<String, List<Integer>> coilAddressMap = new HashMap<>();
|
|
|
- coilAddressMap.put(COIL_DATA_TYPE, new ArrayList<>());
|
|
|
- addressMap.put(COIL_FUNCTION_CODE, coilAddressMap);
|
|
|
+ if (registerCode != null && !registerCode.isEmpty() && dataType != null && address != null) {
|
|
|
+ groups.get(registerCode).get(dataType).add(address);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return groups;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化分组结构
|
|
|
+ */
|
|
|
+ private static void initializeGroupStructure(Map<String, Map<String, List<Integer>>> groups) {
|
|
|
+ // 线圈数据
|
|
|
+ Map<String, List<Integer>> coilGroups = new HashMap<>();
|
|
|
+ coilGroups.put(COIL_DATA_TYPE, new ArrayList<>());
|
|
|
+ groups.put(COIL_FUNCTION_CODE, coilGroups);
|
|
|
+
|
|
|
+ // 输入寄存器数据
|
|
|
+ Map<String, List<Integer>> inputGroups = new HashMap<>();
|
|
|
+ inputGroups.put(INT_DATA_TYPE, new ArrayList<>());
|
|
|
+ inputGroups.put(FLOAT_DATA_TYPE, new ArrayList<>());
|
|
|
+ groups.put(INPUT_FUNCTION_CODE, inputGroups);
|
|
|
+
|
|
|
+ // 保持寄存器数据
|
|
|
+ Map<String, List<Integer>> holdingGroups = new HashMap<>();
|
|
|
+ holdingGroups.put(INT_DATA_TYPE, new ArrayList<>());
|
|
|
+ holdingGroups.put(FLOAT_DATA_TYPE, new ArrayList<>());
|
|
|
+ groups.put(HOLDING_FUNCTION_CODE, holdingGroups);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成分段读取请求
|
|
|
+ */
|
|
|
+ private static List<DeviceRequest> generateSegmentRequests(
|
|
|
+ List<Integer> addresses,
|
|
|
+ String functionCode,
|
|
|
+ String dataType,
|
|
|
+ String ipAddress,
|
|
|
+ Integer port,
|
|
|
+ Integer unitId,
|
|
|
+ Integer protocolId) {
|
|
|
+
|
|
|
+ List<DeviceRequest> requests = new ArrayList<>();
|
|
|
+
|
|
|
+ // 排序地址
|
|
|
+ addresses.sort(Integer::compareTo);
|
|
|
+
|
|
|
+ // 计算地址范围
|
|
|
+ int firstAddress = addresses.get(0);
|
|
|
+ int lastAddress = addresses.get(addresses.size() - 1) + 1;
|
|
|
+ int addressRange = lastAddress - firstAddress;
|
|
|
+
|
|
|
+ // 计算数据类型长度
|
|
|
+ int dataTypeLength = DATA_ADDRESS_LENGTH_MAP.get(dataType);
|
|
|
+ int maxReadLength = DATA_TYPE_LENGTH_MAP.get(dataType);
|
|
|
+
|
|
|
+ // 计算总读取长度
|
|
|
+ int totalReadLength = MathUtil.ceil(addressRange, dataTypeLength);
|
|
|
+
|
|
|
+ // 计算分段数量
|
|
|
+ int segmentCount = MathUtil.ceil(totalReadLength, maxReadLength);
|
|
|
+
|
|
|
+ // 生成每个分段的请求
|
|
|
+ for (int i = 1; i <= segmentCount; i++) {
|
|
|
+ DeviceRequest request = new DeviceRequest();
|
|
|
|
|
|
- // 输入寄存器数据存储地址
|
|
|
- Map<String, List<Integer>> inputAddressMap = new HashMap<>();
|
|
|
- inputAddressMap.put(INT_DATA_TYPE, new ArrayList<>());
|
|
|
- inputAddressMap.put(FLOAT_DATA_TYPE, new ArrayList<>());
|
|
|
- addressMap.put(INPUT_FUNCTION_CODE, inputAddressMap);
|
|
|
+ // 设置基本参数
|
|
|
+ request.setResource(functionCode);
|
|
|
+ request.setDataType(dataType);
|
|
|
+ request.setIpAddress(ipAddress);
|
|
|
+ request.setPort(port);
|
|
|
+ request.setUnitId(unitId);
|
|
|
+ request.setProtocol(protocolId);
|
|
|
|
|
|
- // 保持寄存器数据存储地址
|
|
|
- Map<String, List<Integer>> holdingAddressMap = new HashMap<>();
|
|
|
- holdingAddressMap.put(INT_DATA_TYPE, new ArrayList<>());
|
|
|
- holdingAddressMap.put(FLOAT_DATA_TYPE, new ArrayList<>());
|
|
|
- addressMap.put(HOLDING_FUNCTION_CODE, holdingAddressMap);
|
|
|
+ // 计算偏移量
|
|
|
+ int offset = firstAddress + (maxReadLength * (i - 1) * dataTypeLength);
|
|
|
+ request.setOffsetOrIndex(offset);
|
|
|
|
|
|
- // 保存寄存器数据和地址数据
|
|
|
- for (EquipmentParam equipment : groupEquipmentList) {
|
|
|
- String registerCode = equipment.getRegisterCode();
|
|
|
- if (registerCode != null && !registerCode.isEmpty()) {
|
|
|
- responseMap.get(registerCode).add(equipment);
|
|
|
- addressMap.get(registerCode).get(equipment.getDataType()).add(equipment.getAddress());
|
|
|
- }
|
|
|
+ // 计算读取数量
|
|
|
+ int quantity;
|
|
|
+ if (totalReadLength < maxReadLength * i) {
|
|
|
+ // 最后一个分段
|
|
|
+ quantity = (int) Math.ceil(totalReadLength - (maxReadLength * (i - 1)));
|
|
|
+ } else {
|
|
|
+ // 完整分段
|
|
|
+ quantity = (int) Math.ceil(maxReadLength);
|
|
|
}
|
|
|
-
|
|
|
- // 将地址排序并计算分段读取
|
|
|
- addressMap.forEach((registerCode, dataTypeMap) -> {
|
|
|
- if (dataTypeMap == null || dataTypeMap.isEmpty()) { // dataTypeMap为空时,跳过
|
|
|
- return;
|
|
|
- }
|
|
|
- for (Map.Entry<String, List<Integer>> dataTypeEntry : dataTypeMap.entrySet()) {
|
|
|
- if (dataTypeEntry.getValue() == null || dataTypeEntry.getValue().isEmpty()) { // 地址为空时,跳过
|
|
|
- continue;
|
|
|
- }
|
|
|
- String dataType = dataTypeEntry.getKey();
|
|
|
- List<Integer> addresses = dataTypeEntry.getValue();
|
|
|
- addresses.sort(Integer::compareTo);
|
|
|
- int lastAddress = addresses.get(addresses.size() - 1) + 1;
|
|
|
- int firstAddress = addresses.get(0);
|
|
|
- // 需要读取的长度
|
|
|
- int readQuantityLength = MathUtil.ceil((lastAddress - firstAddress), DATA_ADDRESS_LENGTH_MAP.get(dataType));
|
|
|
- // 根据读取长度分段读取 超过一次可读取的长度的地址将被分为多个请求
|
|
|
- for (int i = 1; i <= MathUtil.ceil(readQuantityLength, DATA_TYPE_LENGTH_MAP.get(dataType)); i++){
|
|
|
- DeviceRequest deviceRequest = new DeviceRequest();
|
|
|
- // TODO 设备从机地址需要从数据库中获取
|
|
|
- deviceRequest.setUnitId(1);
|
|
|
- deviceRequest.setResource(registerCode);
|
|
|
- deviceRequest.setDataType(dataType);
|
|
|
- deviceRequest.setOffsetOrIndex(addresses.get(0) + (DATA_TYPE_LENGTH_MAP.get(dataType) * (i - 1) * DATA_ADDRESS_LENGTH_MAP.get(dataType)));
|
|
|
- int quantity;
|
|
|
- // 读取长度是否超过一次可读取的长度
|
|
|
- if (readQuantityLength < DATA_TYPE_LENGTH_MAP.get(dataType) * i) {
|
|
|
- // 未超过
|
|
|
- quantity = (int) Math.ceil(readQuantityLength - (DATA_TYPE_LENGTH_MAP.get(dataType) * (i - 1)));
|
|
|
- } else {
|
|
|
- // 超过
|
|
|
- quantity = (int) Math.ceil(DATA_TYPE_LENGTH_MAP.get(dataType));
|
|
|
- }
|
|
|
- deviceRequest.setLengthOrQos(quantity);
|
|
|
- // 设置IP地址和端口
|
|
|
- deviceRequest.setIpAddress(ipAddress);
|
|
|
- deviceRequest.setPort(port);
|
|
|
- deviceRequestList.add(deviceRequest);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
+ request.setLengthOrQos(quantity);
|
|
|
+
|
|
|
+ requests.add(request);
|
|
|
}
|
|
|
|
|
|
- return deviceRequestList;
|
|
|
+ return requests;
|
|
|
}
|
|
|
|
|
|
private static ModbusMaster getMaster(String host, Integer port) throws DeviceIOException {
|
|
|
@@ -532,13 +671,6 @@ public class ModbusUtil {
|
|
|
for (Object value : values) {
|
|
|
System.out.println(value);
|
|
|
}
|
|
|
-// int i = 1;
|
|
|
-// int num = 5;
|
|
|
-// int offset = 0;
|
|
|
-// float[] holdingRegistersFloatValues = getHoldingRegistersFloatValues(ip, port, 1, offset, num);
|
|
|
-// for (float f : holdingRegistersFloatValues) {
|
|
|
-// System.out.println("第 " + i++ + " Value: " + f);
|
|
|
-// }
|
|
|
disconnect(ip, port);
|
|
|
}
|
|
|
}
|