前言
本次实现modbus rtu数据读写,在做java的modbus rtu通信时查了好多资料,大多数都是用modbus4j+rxtx实现,不过这种实现方式有一个弊端,就是需要调用dll,而且跨平台不兼容,这对于一个强迫症来说是致命的,本次记录另一种跨平台实现方案。
实现方式
使用modbus4j实现modbus通信,同时使用jssc来实现串口数据读写。
Maven依赖
<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>
<dependency>
<groupId>io.github.java-native</groupId>
<artifactId>jssc</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.4</version>
</dependency>
</dependencies>
其中jssc是用来实现串口通信的库,可以根据操作系统自动识别。
代码实现
串口的输入流实现类 SerialInputStream.java
import jssc.SerialPort;
import java.io.IOException;
import java.io.InputStream;
public class SerialInputStream extends InputStream {
private SerialPort serialPort;
private int defaultTimeout = 50;
public SerialInputStream(SerialPort sp) {
serialPort = sp;
}
public void setTimeout(int time) {
defaultTimeout = time;
}
@Override
public int read() throws IOException {
return read(defaultTimeout);
}
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);
}
}
@Override
public int read(byte[] buf) throws IOException {
return read(buf, 0, buf.length);
}
@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, readBuf.length);
return readBuf.length;
} catch (Exception e) {
throw new IOException(e);
}
}
public int blockingRead(byte[] buf) throws IOException {
return blockingRead(buf, 0, buf.length, defaultTimeout);
}
public int blockingRead(byte[] buf, int timeout) throws IOException {
return blockingRead(buf, 0, buf.length, timeout);
}
public int blockingRead(byte[] buf, int offset, int length) throws IOException {
return blockingRead(buf, offset, length, defaultTimeout);
}
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;
}
} catch (Exception e) {
//TODO 异常处理
}
return 0;
}
}
串口输出流实现类 SerialOutputStream.java
import jssc.SerialPort;
import jssc.SerialPortException;
import java.io.IOException;
import java.io.OutputStream;
public class SerialOutputStream extends OutputStream {
SerialPort serialPort;
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);
}
}
}
modbus4j的串口实现类
import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;
import jssc.SerialPortException;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.io.OutputStream;
@Slf4j
public class SerialPortWrapperImpl implements SerialPortWrapper {
private final SerialPort port;
private final int baudRate;
private final int dataBits;
private final int stopBits;
private final int parity;
private final int flowControlIn;
private final int flowControlOut;
public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits, int parity, int flowControlIn,
int flowControlOut) {
this.baudRate = baudRate;
this.dataBits = dataBits;
this.stopBits = stopBits;
this.parity = parity;
this.flowControlIn = flowControlIn;
this.flowControlOut = flowControlOut;
port = new SerialPort(commPortId);
}
@Override
public void close() throws Exception {
port.closePort();
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());
log.debug("Serial port {} opened", port.getPortName());
} catch (SerialPortException e) {
log.error("Error opening port : {} for {} ", port.getPortName(), e.getMessage());
}
}
@Override
public InputStream getInputStream() {
return new SerialInputStream(port);
}
@Override
public OutputStream getOutputStream() {
return new SerialOutputStream(port);
}
@Override
public int getBaudRate() {
return baudRate;
}
@Override
public int getFlowControlIn() {
return flowControlIn;
}
@Override
public int getFlowControlOut() {
return flowControlOut;
}
@Override
public int getDataBits() {
return dataBits;
}
@Override
public int getStopBits() {
return stopBits;
}
@Override
public int getParity() {
return parity;
}
}
上面这个是比较关键的串口实现,到这里已经可以和网上的modbus4j资料完成采集,具体实现下节实现