Modbus4j获取Rs232串口数据
总的记录一下被折磨一周的痛苦,为以后遇到类似问题留个记录
首先引入依赖
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>io.github.java-native</groupId>
<artifactId>jssc</artifactId>
<version>2.9.2</version>
</dependency>
导入不了依赖的话maven仓库下载再用指令引入
mvn install:install-file -Dfile=文件路径具体到文件名 -DgroupId=对应的groupId
-DartifactId=对应的artifactId -Dversion=对应的version -Dpackaging=jar
适用于绝大多数依赖到不进来的情况
直接上代码
public class Main {
public static void main(String[] args) {
// 创建 ModbusMaster 实例
ModbusMaster master = createModbusMaster();
// 定义从站地址、起始地址和读取的寄存器数量
int slaveId = 1;
int startOffset = 4;
int quantity = 1;
// 读取输入寄存器的测试方法
try {
readInputRegistersTest(master, slaveId, startOffset, quantity);
} catch (Exception e) {
// 处理异常,可以打印异常信息或采取其他处理措施
e.printStackTrace();
} finally {
closeModbusMaster(master);
}
}
/**
* 创建 ModbusMaster 实例
*/
public static ModbusMaster createModbusMaster() {
// 实际创建 ModbusMaster 的过程,需要根据你的通信方式进行配置
// 这里使用 createRtuMaster 方法作为示例
ModbusFactory modbusFactory = new ModbusFactory();
SerialPortWrapperImpl com6 = new SerialPortWrapperImpl("COM6", 9600, 8, 1, 2);
return modbusFactory.createRtuMaster(com6);
}
/**
* 关闭 ModbusMaster 连接
*/
private static void closeModbusMaster(ModbusMaster master) {
if (master != null) {
master.destroy();
}
}
/**
* 读保持寄存器上的内容
*
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param len 待读寄存器的个数
*/
private static void readHoldingRegistersTest(ModbusMaster master, int slaveId, int start, int len) {
try {
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
if (response.isException()) {
System.out.println("Exception response: message=" + response.getExceptionMessage());
} else {
System.out.println(Arrays.toString(response.getShortData()));
short[] list = response.getShortData();
for (int i = 0; i < list.length; i++) {
System.out.print(list[i] + " ");
}
}
} catch (ModbusTransportException e) {
e.printStackTrace();
}
}
/**
* 读取输入寄存器上的内容
*
* @param master ModbusMaster 实例
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param len 待读寄存器的个数
*/
private static void readInputRegistersTest(ModbusMaster master, int slaveId, int start, int len) {
try {
ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, start, len);
ModbusResponse response = master.send(request);
if (response instanceof ReadInputRegistersResponse) {
// 处理响应
ReadInputRegistersResponse inputRegistersResponse = (ReadInputRegistersResponse) response;
if (inputRegistersResponse.isException()) {
System.out.println("Exception response: message=" + inputRegistersResponse.getExceptionMessage());
} else {
System.out.println(Arrays.toString(inputRegistersResponse.getShortData()));
short[] list = inputRegistersResponse.getShortData();
for (int i = 0; i < list.length; i++) {
System.out.print(list[i] + " ");
}
}
} else {
System.out.println("Unexpected response type.");
}
} catch (ModbusTransportException e) {
// 处理超时异常
System.out.println("Modbus transport exception: " + e.getMessage());
e.printStackTrace();
}
}
}
其中SerialPortWrapper要我们重新实现一下
public class SerialPortWrapperImpl implements SerialPortWrapper {
private static final Logger LOG = LoggerFactory.getLogger(SerialPortWrapperImpl.class);
private SerialPort port;
private String commPortId;
private int baudRate;
private int dataBits;
private int stopBits;
private int parity;
// private int flowControlIn;
// private int flowControlOut;
public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits, int parity) {
this.commPortId = commPortId;
this.baudRate = baudRate;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
// this.flowControlIn = flowControlIn;
// this.flowControlOut = flowControlOut;
port = new SerialPort(this.commPortId);
}
@Override
public void close() throws Exception {
port.closePort();
//listeners.forEach(PortConnectionListener::closed);
LOG.debug("Serial port {} closed", port.getPortName());
}
@Override
public void open() {
try {
port.openPort();
port.setParams(this.getBaudRate(), this.getDataBits(), this.getStopBits(), this.getParity());
// port.setFlowControlMode(this.getFlowControlIn() | this.getFlowControlOut());
//listeners.forEach(PortConnectionListener::opened);
LOG.debug("Serial port {} opened", port.getPortName());
} catch (SerialPortException ex) {
LOG.error("Error opening port : {} for {} ", port.getPortName(), ex);
}
}
@Override
public InputStream getInputStream() {
return new SerialInputStream(port);
}
@Override
public OutputStream getOutputStream() {
return new SerialOutputStream(port);
}
@Override
public int getBaudRate() {
return baudRate;
//return SerialPort.BAUDRATE_9600;
}
// @Override
// public int getFlowControlIn() {
// return flowControlIn;
// //return SerialPort.FLOWCONTROL_NONE;
// }
//
// @Override
// public int getFlowControlOut() {
// return flowControlOut;
// //return SerialPort.FLOWCONTROL_NONE;
// }
@Override
public int getDataBits() {
return dataBits;
//return SerialPort.DATABITS_8;
}
@Override
public int getStopBits() {
return stopBits;
//return SerialPort.STOPBITS_1;
}
@Override
public int getParity() {
return parity;
//return SerialPort.PARITY_NONE;
}
}
以及额外的两个SerialInputStream和SerialOutputStream帮忙序列化输入输出流
package org.example;
/**
*
* Copyright (c) 2009-2020 Freedomotic Team http://www.freedomotic-iot.com
*
* This file is part of Freedomotic
*
* This Program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2, or (at your option) any later version.
*
* This Program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* Freedomotic; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
import jssc.SerialPort;
import jssc.SerialPortException;
import java.io.IOException;
import java.io.OutputStream;
/**
* Class that wraps a {@link SerialPort} to provide {@link OutputStream}
* functionality.
* <br>
* It is instantiated by passing the constructor a {@link SerialPort} instance.
* Do not create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @author Charles Hache <chalz@member.fsf.org>
*
* Attribution: https://github.com/therealchalz/java-simple-serial-connector
*
*/
public class SerialOutputStream extends OutputStream {
SerialPort serialPort;
/**
* Instantiates a SerialOutputStream for the given {@link SerialPort} Do not
* create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @param sp The serial port to stream.
*/
public SerialOutputStream(SerialPort sp) {
serialPort = sp;
}
@Override
public void write(int b) throws IOException {
try {
serialPort.writeInt(b);
} catch (SerialPortException e) {
throw new IOException(e);
}
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
byte[] buffer = new byte[len];
System.arraycopy(b, off, buffer, 0, len);
try {
serialPort.writeBytes(buffer);
} catch (SerialPortException e) {
throw new IOException(e);
}
}
}
SerialInputStream
package org.example;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import jssc.SerialPort;
import java.io.IOException;
import java.io.InputStream;
/**
* Class that wraps a {@link SerialPort} to provide {@link InputStream}
* functionality. This stream also provides support for performing blocking
* reads with timeouts.
* <br>
* It is instantiated by passing the constructor a {@link SerialPort} instance.
* Do not create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @author Charles Hache <chalz@member.fsf.org>
*
* Attribution: https://github.com/therealchalz/java-simple-serial-connector
*
*/
public class SerialInputStream extends InputStream {
private SerialPort serialPort;
private int defaultTimeout = 0;
/**
* Instantiates a SerialInputStream for the given {@link SerialPort} Do not
* create multiple streams for the same serial port unless you implement
* your own synchronization.
*
* @param sp The serial port to stream.
*/
public SerialInputStream(SerialPort sp) {
serialPort = sp;
}
/**
* Set the default timeout (ms) of this SerialInputStream. This affects
* subsequent calls to {@link #read()}, {@link #blockingRead(int[])}, and
* {@link #blockingRead(int[], int, int)} The default timeout can be 'unset'
* by setting it to 0.
*
* @param time The timeout in milliseconds.
*/
public void setTimeout(int time) {
defaultTimeout = time;
}
/**
* Reads the next byte from the port. If the timeout of this stream has been
* set, then this method blocks until data is available or until the timeout
* has been hit. If the timeout is not set or has been set to 0, then this
* method blocks indefinitely.
*/
@Override
public int read() throws IOException {
return read(defaultTimeout);
}
/**
* The same contract as {@link #read()}, except overrides this stream's
* default timeout with the given timeout in milliseconds.
*
* @param timeout The timeout in milliseconds.
* @return The read byte.
* @throws IOException On serial port error or timeout
*/
public int read(int timeout) throws IOException {
byte[] buf = new byte[1];
try {
if (timeout > 0) {
buf = serialPort.readBytes(1, timeout);
} else {
buf = serialPort.readBytes(1);
}
return buf[0];
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Non-blocking read of up to buf.length bytes from the stream. This call
* behaves as read(buf, 0, buf.length) would.
*
* @param buf The buffer to fill.
* @return The number of bytes read, which can be 0.
* @throws IOException on error.
*/
@Override
public int read(byte[] buf) throws IOException {
return read(buf, 0, buf.length);
}
/**
* Non-blocking read of up to length bytes from the stream. This method
* returns what is immediately available in the input buffer.
*
* @param buf The buffer to fill.
* @param offset The offset into the buffer to start copying data.
* @param length The maximum number of bytes to read.
* @return The actual number of bytes read, which can be 0.
* @throws IOException on error.
*/
@Override
public int read(byte[] buf, int offset, int length) throws IOException {
if (buf.length < offset + length) {
length = buf.length - offset;
}
int available = this.available();
if (available > length) {
available = length;
}
try {
byte[] readBuf = serialPort.readBytes(available);
// System.arraycopy(readBuf, 0, buf, offset, length);
System.arraycopy(readBuf, 0, buf, offset, readBuf.length);
return readBuf.length;
} catch (Exception e) {
throw new IOException(e);
}
}
/**
* Blocks until buf.length bytes are read, an error occurs, or the default
* timeout is hit (if specified). This behaves as blockingRead(buf, 0,
* buf.length) would.
*
* @param buf The buffer to fill with data.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf) throws IOException {
return blockingRead(buf, 0, buf.length, defaultTimeout);
}
/**
* The same contract as {@link #blockingRead(byte[])} except overrides this
* stream's default timeout with the given one.
*
* @param buf The buffer to fill.
* @param timeout The timeout in milliseconds.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf, int timeout) throws IOException {
return blockingRead(buf, 0, buf.length, timeout);
}
/**
* Blocks until length bytes are read, an error occurs, or the default
* timeout is hit (if specified). Saves the data into the given buffer at
* the specified offset. If the stream's timeout is not set, behaves as
* {@link #read(byte[], int, int)} would.
*
* @param buf The buffer to fill.
* @param offset The offset in buffer to save the data.
* @param length The number of bytes to read.
* @return the number of bytes read.
* @throws IOException on error or timeout.
*/
public int blockingRead(byte[] buf, int offset, int length) throws IOException {
return blockingRead(buf, offset, length, defaultTimeout);
}
/**
* The same contract as {@link #blockingRead(byte[], int, int)} except
* overrides this stream's default timeout with the given one.
*
* @param buf The buffer to fill.
* @param offset Offset in the buffer to start saving data.
* @param length The number of bytes to read.
* @param timeout The timeout in milliseconds.
* @return The number of bytes read.
* @throws IOException On error or timeout.
*/
public int blockingRead(byte[] buf, int offset, int length, int timeout) throws IOException {
if (buf.length < offset + length) {
throw new IOException("Not enough buffer space for serial data");
}
if (timeout < 1) {
return read(buf, offset, length);
}
try {
byte[] readBuf = serialPort.readBytes(length, timeout);
System.arraycopy(readBuf, 0, buf, offset, length);
return readBuf.length;
} catch (Exception e) {
throw new IOException(e);
}
}
@Override
public int available() throws IOException {
int ret;
try {
ret = serialPort.getInputBufferBytesCount();
if (ret >= 0) {
return ret;
}
throw new IOException("Error checking available bytes from the serial port.");
} catch (Exception e) {
throw new IOException("Error checking available bytes from the serial port.");
}
}
}