和豆包,deepSeek友好的聊了一天,也是终于实现了这一个功能。。。
首先下载依赖
<dependency> <groupId>com.infiniteautomation</groupId> <artifactId>modbus4j</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>com.fazecast</groupId> <artifactId>jserialcomm</artifactId> <version>2.10.4</version> </dependency>完整的POM.xml文件如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>projectDemo</artifactId> <version>1.0-SNAPSHOT</version> <repositories> <repository> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> <id>ias-snapshots</id> <name>Infinite Automation Snapshot Repository</name> <url>https://maven.mangoautomation.net/repository/ias-snapshot/</url> </repository> <repository> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> <id>ias-releases</id> <name>Infinite Automation Release Repository</name> <url>https://maven.mangoautomation.net/repository/ias-release/</url> </repository> </repositories> <dependencies> <!--串口 jSerialComm 正常可用--> <dependency> <groupId>com.fazecast</groupId> <artifactId>jSerialComm</artifactId> <version>2.10.4</version> </dependency> <!-- 稳定版modbus4j --> <dependency> <groupId>com.infiniteautomation</groupId> <artifactId>modbus4j</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>com.fazecast</groupId> <artifactId>jserialcomm</artifactId> <version>2.10.4</version> </dependency> </dependencies> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project>然后,创建两个工具类
package org.modbus.util; import com.serotonin.modbus4j.BatchRead; import com.serotonin.modbus4j.BatchResults; import com.serotonin.modbus4j.ModbusFactory; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.code.DataType; import com.serotonin.modbus4j.exception.ErrorResponseException; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.ip.IpParameters; import com.serotonin.modbus4j.locator.BaseLocator; import com.serotonin.modbus4j.serial.SerialPortWrapper; import java.util.ArrayList; import java.util.List; public class Modbus4jUtils { /** * 工厂。 */ static ModbusFactory modbusFactory; static { if (modbusFactory == null) { modbusFactory = new ModbusFactory(); } } /** * 获取master * * @return * @throws ModbusInitException */ public static ModbusMaster getMaster(SerialPortWrapper wrapper, int type) { if (type == 1) { //RTU return modbusFactory.createRtuMaster(wrapper); } else if (type == 2) { //ASC return modbusFactory.createAsciiMaster(wrapper); } else { throw new RuntimeException("参数有误"); } } /** * 获取master * * @return * @throws ModbusInitException */ public static ModbusMaster getMaster(IpParameters params, Boolean keepAlive) { if (keepAlive != null) { //TCP return modbusFactory.createTcpMaster(params, keepAlive); } else return modbusFactory.createUdpMaster(params); //UDP } /** * 读取[01 Coil Status 0x]类型 开关数据 * * @param slaveId slaveId * @param startAddress 起始寄存器位置 * @param numberOfPoints 读取寄存器数量 * @throws ModbusTransportException 异常 * @throws ErrorResponseException 异常 * @throws ModbusInitException 异常 */ public static BatchResults<Integer> readCoilStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints) throws ModbusTransportException, ErrorResponseException { BatchRead<Integer> batch = new BatchRead<Integer>(); for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){ batch.addLocator(j, BaseLocator.coilStatus(slaveId, i)); } batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); return results; } /** * 读取[02 Input Status 1x]类型 开关数据 * * @param slaveId * @param startAddress 起始寄存器位置 * @param numberOfPoints 读取寄存器数量 * @return * @throws ModbusTransportException * @throws ErrorResponseException * @throws ModbusInitException */ public static BatchResults<Integer> readInputStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints) throws ModbusTransportException, ErrorResponseException{ BatchRead<Integer> batch = new BatchRead<Integer>(); for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){ batch.addLocator(j, BaseLocator.inputStatus(slaveId, i)); } batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); return results; } /** * 读取[03 Holding Register类型 2x]模拟量数据 * * @param slaveId slave Id * @param startAddress 起始寄存器位置 * @param numberOfPoints 读取寄存器数量 * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType * @return * @throws ModbusTransportException 异常 * @throws ErrorResponseException 异常 */ public static BatchResults<Integer> readHoldingRegister(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints, int dataType) throws ModbusTransportException, ErrorResponseException { BatchRead<Integer> batch = new BatchRead<Integer>(); for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){ batch.addLocator(j, BaseLocator.holdingRegister(slaveId, i, dataType)); } batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); return results; } /** * 读取[04 Input Registers 3x]类型 模拟量数据 * * @param slaveId slaveId * @param startAddress 起始寄存器位置 * @param numberOfPoints 读取寄存器数量 * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType * @return 返回结果 * @throws ModbusTransportException 异常 * @throws ErrorResponseException 异常 */ public static BatchResults<Integer> readInputRegisters(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints, int dataType) throws ModbusTransportException, ErrorResponseException { BatchRead<Integer> batch = new BatchRead<Integer>(); for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){ batch.addLocator(j, BaseLocator.inputRegister(slaveId, i, dataType)); } batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); return results; } /** * 批量读取使用方法 * * @throws ModbusTransportException * @throws ErrorResponseException * @throws ModbusInitException */ public static void batchRead(ModbusMaster master) throws ModbusTransportException, ErrorResponseException, ModbusInitException { BatchRead<Integer> batch = new BatchRead<Integer>(); batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT)); batch.addLocator(1, BaseLocator.inputStatus(1, 0)); batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); System.out.println(results.getValue(0)); System.out.println(results.getValue(1)); } }这个工具类封装了创建工厂,读取数据的所有方法
package org.modbus.util; import com.serotonin.modbus4j.serial.SerialPortWrapper; import com.fazecast.jSerialComm.SerialPort; import java.io.InputStream; import java.io.OutputStream; public class SerialPortWrapperJSerialImpl implements SerialPortWrapper { private SerialPort serialPort; private final String portName; private final int baudRate; private final int dataBits; private final int stopBits; private final int parity; public SerialPortWrapperJSerialImpl(String portName, int baudRate, int dataBits, int stopBits, int parity) { this.portName = portName; this.baudRate = baudRate; this.dataBits = dataBits; this.stopBits = stopBits; this.parity = parity; } @Override public int getBaudRate() { return baudRate; } @Override public int getDataBits() { return dataBits; } @Override public int getStopBits() { return stopBits; } @Override public int getParity() { return parity; } @Override public int getFlowControlIn() { return 0; } @Override public int getFlowControlOut() { return 0; } @Override public void open() throws Exception { if (serialPort != null && serialPort.isOpen()) { return; } serialPort = SerialPort.getCommPort(portName); serialPort.setBaudRate(baudRate); serialPort.setNumDataBits(dataBits); serialPort.setNumStopBits(stopBits); // 转换modbus4j parity常量到jSerialComm switch (parity) { case 0: serialPort.setParity(SerialPort.NO_PARITY); break; case 1: serialPort.setParity(SerialPort.ODD_PARITY); break; case 2: serialPort.setParity(SerialPort.EVEN_PARITY); break; default: serialPort.setParity(SerialPort.NO_PARITY); } // 关闭硬件流控,485不需要 serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED); // 超时防止阻塞 serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0); serialPort.openPort(); } @Override public void close() throws Exception { if (serialPort != null && serialPort.isOpen()) { serialPort.closePort(); } serialPort = null; } @Override public InputStream getInputStream() { return serialPort.getInputStream(); } @Override public OutputStream getOutputStream() { return serialPort.getOutputStream(); } }这个工具用来初始化串口
好了,一切准备就绪,开始测试
package org.modbus; import com.fazecast.jSerialComm.SerialPort; import com.serotonin.modbus4j.BatchResults; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.code.DataType; import com.serotonin.modbus4j.exception.ErrorResponseException; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.serial.SerialPortWrapper; import org.modbus.util.Modbus4jUtils; import org.modbus.util.SerialPortWrapperJSerialImpl; public class Demo02 { public static void main(String[] args) throws ModbusTransportException, ErrorResponseException, ModbusInitException { redTest(); } private static void redTest() throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 1. 实例化JSSC串口包装类,8N1无校验 SerialPortWrapper wrapper = new SerialPortWrapperJSerialImpl("COM1",9600,8,1,0); ModbusMaster master = Modbus4jUtils.getMaster(wrapper, 1); master.init(); //读取从站1的保持型寄存器0~9 BatchResults<Integer> res = Modbus4jUtils.readHoldingRegister(master, 1, 0, 9, DataType.TWO_BYTE_INT_UNSIGNED); System.out.println(res); //读取从站1的线圈状态0~7 BatchResults<Integer> coils = Modbus4jUtils.readCoilStatus(master,1,0,8); System.out.println(coils); //读取从站1的输入线圈0~7 BatchResults<Integer> inputs = Modbus4jUtils.readInputStatus(master,1,0,8); System.out.println(inputs); //读取从站5的输入寄存器0~4 BatchResults<Integer> inputRegisters = Modbus4jUtils.readInputRegisters(master,5,0,5,DataType.TWO_BYTE_INT_UNSIGNED); System.out.println(inputRegisters); } }配置主站(我这里使用的是虚拟的)
然后我们run一下,看一下效果
家人们,牛逼坏了我,搞了一天啊!!!