Prechádzať zdrojové kódy

添加测试modbus tcp

klzhangweiya 2 týždňov pred
rodič
commit
869ffc49b7

+ 1 - 0
SERVER/ChickenFarmV3/.script/sql/20260108/data.sql

@@ -303,6 +303,7 @@ INSERT INTO `sys_menu` VALUES (1731, '查询鸡笼', 262, 0, '#', NULL, '', 1, 0
 INSERT INTO `sys_menu` VALUES (1732, '新增鸡笼', 262, 0, '#', NULL, '', 1, 0, 'F', '0', '0', 'breeding:chickenCage:add', 'plus-square', 'btn btn-light-primary', 'handleCreate', 100, 1, '2025-07-17 09:21:59', NULL, '2025-07-17 09:21:59', '');
 INSERT INTO `sys_menu` VALUES (1733, '修改鸡笼', 262, 0, '#', NULL, '', 1, 0, 'F', '0', '0', 'breeding:chickenCage:edit', 'pencil-square', 'btn btn-light-success', 'handleUpdate@1', 100, 1, '2025-07-17 09:21:59', NULL, '2025-07-17 09:21:59', '');
 INSERT INTO `sys_menu` VALUES (1734, '删除鸡笼', 262, 0, '#', NULL, '', 1, 0, 'F', '1', '0', 'breeding:chickenCage:remove', 'dash-square', 'btn btn-light-danger', 'handleDelete@0', 100, 1, '2025-07-17 09:21:59', NULL, '2025-07-17 09:21:59', '');
+INSERT INTO `sys_menu` VALUES (1735, '导入鸡笼', 262, 5, '#', NULL, '', 1, 0, 'F', '1', '0', 'breeding:chickenCage:import', 'plus-square', 'btn btn-light-danger', 'handleImport', 100, 1, '2025-07-17 09:21:59', NULL, '2025-07-17 09:21:59', '');
 
 INSERT INTO `sys_menu` VALUES (1741, '查询品系', 263, 0, '#', NULL, '', 1, 0, 'F', '0', '0', 'breeding:variety:query', 'eye', '', '', 100, 1, '2025-07-17 11:33:35', NULL, '2025-07-17 11:33:35', '');
 INSERT INTO `sys_menu` VALUES (1742, '新增品系', 263, 0, '#', NULL, '', 1, 0, 'F', '0', '0', 'breeding:variety:add', 'plus-square', 'btn btn-light-primary', 'handleCreate', 100, 1, '2025-07-17 11:33:35', NULL, '2025-07-17 11:33:35', '');

+ 5 - 0
SERVER/ChickenFarmV3/vb-collection-modules/vb-collection-core/pom.xml

@@ -19,6 +19,11 @@
             <groupId>com.vap</groupId>
             <artifactId>vb-common-core</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.ghgande</groupId>
+            <artifactId>j2mod</artifactId>
+            <version>2.7.0</version>
+        </dependency>
 
     </dependencies>
 

+ 153 - 0
SERVER/ChickenFarmV3/vb-collection-modules/vb-collection-core/src/main/java/cn/vber/collection/service/ModbusTcpService.java

@@ -0,0 +1,153 @@
+package cn.vber.collection.service;
+
+import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
+import com.ghgande.j2mod.modbus.msg.ReadCoilsRequest;
+import com.ghgande.j2mod.modbus.msg.ReadCoilsResponse;
+import com.ghgande.j2mod.modbus.msg.WriteCoilRequest;
+import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersRequest;
+import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersResponse;
+import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.net.InetAddress;
+
+@Service
+@Slf4j
+public class ModbusTcpService  {
+    /**
+     * 通用连接方法(抽取公共逻辑,避免重复代码)
+     */
+    private TCPMasterConnection getConnection(String ip, int port, int timeout) throws Exception {
+        TCPMasterConnection connection = new TCPMasterConnection(InetAddress.getByName(ip));
+        connection.setPort(port);
+        connection.setTimeout(timeout); // 超时时间(采集器维护时可配置)
+        connection.connect();
+        if (!connection.isConnected()) {
+            throw new Exception("采集器连接失败:IP=" + ip + ", Port=" + port);
+        }
+        return connection;
+    }
+// ==================== 线圈读取(核心业务场景) ====================
+    /**
+     * 读取线圈状态(功能码01)
+     * @param ip 采集器IP
+     * @param port 采集器端口
+     * @param slaveId PLC从站ID(MBAP单元标识)
+     * @param startAddr 线圈起始地址(注意:Modbus地址00001对应j2mod的0)
+     * @param readLength 读取线圈数量(按位计数)
+     * @param timeout 超时时间(毫秒)
+     * @return 线圈状态数组(boolean[]:true=通/1,false=断/0)
+     */
+    public boolean[] readCoils(String ip, int port, int slaveId, int startAddr, int readLength, int timeout) throws Exception {
+        TCPMasterConnection connection = null;
+        try {
+            connection = getConnection(ip, port, timeout);
+
+            // 1. 创建线圈读取请求(功能码01)
+            ReadCoilsRequest request = new ReadCoilsRequest(startAddr, readLength);
+            request.setUnitID(slaveId); // 区分同一采集器下的不同PLC
+            request.setHeadless(false); // 启用MBAP头(必须)
+
+            // 2. 执行事务
+            ModbusTCPTransaction transaction = new ModbusTCPTransaction(connection);
+            transaction.setRequest(request);
+            transaction.execute();
+
+            // 3. 解析响应
+            ReadCoilsResponse response = (ReadCoilsResponse) transaction.getResponse();
+            boolean[] coilStatus = new boolean[readLength];
+            for (int i = 0; i < readLength; i++) {
+                coilStatus[i] = response.getCoilStatus(i); // 按位读取线圈状态
+            }
+            return coilStatus;
+        } catch (Exception e) {
+            log.error("读取线圈失败:IP={}, SlaveId={}, 起始地址={}", ip, slaveId, startAddr, e);
+            throw new Exception("读取线圈异常:" + e.getMessage());
+        } finally {
+            if (connection != null && connection.isConnected()) {
+                connection.close(); // 释放连接
+            }
+        }
+    }
+
+    // ==================== 寄存器读取(核心业务场景) ====================
+    /**
+     * 读取保持寄存器(功能码03)
+     * @param ip 采集器IP
+     * @param port 采集器端口
+     * @param slaveId PLC从站ID
+     * @param startAddr 寄存器起始地址(Modbus地址40001对应j2mod的0)
+     * @param readLength 读取寄存器数量(按字计数)
+     * @param timeout 超时时间(毫秒)
+     * @return 寄存器数值数组(int[])
+     */
+    public int[] readHoldingRegisters(String ip, int port, int slaveId, int startAddr, int readLength, int timeout) throws Exception {
+        TCPMasterConnection connection = null;
+        try {
+            connection = getConnection(ip, port, timeout);
+
+            // 1. 创建寄存器读取请求(功能码03)
+            ReadMultipleRegistersRequest  request = new ReadMultipleRegistersRequest (startAddr, readLength);
+            request.setUnitID(slaveId);
+            request.setHeadless(false);
+
+            // 2. 执行事务
+            ModbusTCPTransaction transaction = new ModbusTCPTransaction(connection);
+            transaction.setRequest(request);
+            transaction.execute();
+
+            // 3. 解析响应
+            ReadMultipleRegistersResponse  response = (ReadMultipleRegistersResponse ) transaction.getResponse();
+                   // 将BitVector转换为int数组
+            int[] registerValues = new int[readLength];
+            for (int i = 0; i < readLength; i++) {
+                if (i < response.getByteCount()/2) { // 每个寄存器占用2字节
+                    registerValues[i] = response.getRegisterValue(i);
+                }
+            } // 直接返回寄存器数值数组
+            return registerValues;
+        } catch (Exception e) {
+            log.error("读取寄存器失败:IP={}, SlaveId={}, 起始地址={}", ip, slaveId, startAddr, e);
+            throw new Exception("读取寄存器异常:" + e.getMessage());
+        } finally {
+            if (connection != null && connection.isConnected()) {
+                connection.close();
+            }
+        }
+    }
+
+    // ==================== 设备维护:写入线圈(控制设备) ====================
+    /**
+     * 写入单个线圈(功能码05)- 设备维护核心功能(如远程启停风机)
+     * @param ip 采集器IP
+     * @param port 采集器端口
+     * @param slaveId PLC从站ID
+     * @param addr 线圈地址
+     * @param status 线圈状态(true=置1/启动,false=置0/停止)
+     */
+    public void writeSingleCoil(String ip, int port, int slaveId, int addr, boolean status) throws Exception {
+        TCPMasterConnection connection = null;
+        try {
+            connection = getConnection(ip, port, 3000);
+
+            // 创建写线圈请求(功能码05)
+            WriteCoilRequest request = new WriteCoilRequest(addr, status);
+            request.setUnitID(slaveId);
+
+            ModbusTCPTransaction transaction = new ModbusTCPTransaction(connection);
+            transaction.setRequest(request);
+            transaction.execute();
+
+            log.info("写入线圈成功:IP={}, SlaveId={}, 地址={}, 状态={}", ip, slaveId, addr, status);
+        } catch (Exception e) {
+            log.error("写入线圈失败:IP={}, SlaveId={}, 地址={}", ip, slaveId, addr, e);
+            throw new Exception("写入线圈异常:" + e.getMessage());
+        } finally {
+            if (connection != null && connection.isConnected()) {
+                connection.close();
+            }
+        }
+    }
+
+}