概要
如果你的领导坚持坚持坚持让你用java来进行串口交互, 不是项目有问题就是他有问题,为了解决你们的问题.
Nettyx提供了两种串口通信实现, 分别是Rxtx系和Jsc系, 个人强烈建议使用Jsc, 毕竟Rxtx不仅老,且使用时需要添加依赖包或平台执行文件至类路径, 麻烦啊
那正文开始了, 先引入Nettyx的依赖
请从maven中央仓获取{lastest.version},最新版本号
<dependency>
<groupId>io.github.fbbzl</groupId>
<artifactId>nettyx</artifactId>
<version>{lastest.version}</version>
</dependency>
一. Rxtx(极力不推荐, 底层库陈旧, 版本兼容极差,无人维护,使用麻烦,平台限制,不支持串口热插拔,截止2025/04/11, 甚至jdk8部分子版本已经不兼容,启动直接报错)
作为java曾经唯一能用的串口通信库, 现在已经全面被舍弃, rxtx的最后一次更新, 笔者还是未成年,你可敢信,使用前先确保Rxtx的依赖库动态链接dll放在正确的类路径下
1. abstract SingleRxtxChannelTemplate
虽然Rxtx已经过时,但Nettyx依然提供了SingleRxtxChannelTemplate来帮助初始化一个单串口通道客户端, 保存了单个rxtx-channel, 此客户端可以作为spring中的一个bean去使用, 完全可以当做是jdbcClient,redisClient, HttpClient这种工具组件去使用, 线程安全
以下展示了一个简单的 单通道 rxtxTemplate
@Component
public class TestSingleRxtxTemplate extends SingleRxtxChannelTemplate {
// 此示例需要重连, 所以有用来重连的定时器, 用于定时重连
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public TestSingleRxtxTemplate(String commAddress) {
super(commAddress);
}
@Override
protected ChannelInitializer<RxtxChannel> channelInitializer() {
// 实际使用替换成你的业务channelinitializer即可. 这里不展示ChannelInitializer用法
return new TestChannelInitializer<>();
}
// 重写doChannelConfig来进行参数设置,此方法入参channelConfig是一个空白的config对象,需要你set指定的业务通信参数
@Override
protected void doChannelConfig(RxtxChannelConfig channelConfig) {
channelConfig
.setBaudRate(115200)
.setDataBits(RxtxChannelConfig.DataBits.DATA_BITS_8)
.setStopBits(RxtxChannelConfig.StopBits.STOP_BITS_1)
.setParityBit(RxtxChannelConfig.ParityBit.NO)
.setDtr(false)
.setRts(false);
}
}
测试方法
public static void main(String[] args) {
// 这里直接使用main函数演示用法, 各位在项目中使用时,请将其实现作为spring bean来使用!!!
TestSingleRxtxTemplate testSingleRxtx = new TestSingleRxtxTemplate("COM3");
ChannelFutureListener listener = new ActionChannelFutureListener()
// 当成功时执行指定函数, 通过lambda来指定
.whenSuccess((l, cf) -> {
executor.scheduleAtFixedRate(() -> {
byte[] msg = new byte[300];
Arrays.fill(msg, (byte) 1);
testSingleRxtx.writeAndFlush(Unpooled.wrappedBuffer(msg));
}, 2, 30, TimeUnit.MILLISECONDS);
System.err.println(cf.channel().localAddress() + ": ok");
})
// 取消时执行指定函数, 通过lambda指定
.whenCancel((l, cf) -> System.err.println("cancel"))
// 失败时执行指定函数, 其中redo()为ActionChannelFutureListener内部静态方法, 方便进行重试
.whenFailure(redo(testSingleRxtx::connect, 2, SECONDS))
// 当任务结束时执行指定函数
.whenDone((l, cf) -> System.err.println("done"));
testSingleRxtx.connect().addListener(listener);
}
}
2. abstract MultiRxtxChannelTemplate
部分业务可能需要你操作一组相同功能的channel. 如果按照之前的方式去创建,一个channel一个工具bean的话,将会难以管理, 所以Nettyx提供了MultiRxtxChannelTemplate, 让多个channel可以保存在一个组件中, 然后通过key去检索channel.所以MultiRxtxChannelTemplate中大部分api都需要提供key
类上通过泛型来指定key的类型.
以下展示了一个简单的多通道rxtxclient
@Component
public class TestMultiRxtxTemplate extends MultiRxtxChannelTemplate<String> {
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
protected TestMultiRxtxTemplate(Map<String, RxtxDeviceAddress> stringRxtxDeviceAddressMap) {
super(stringRxtxDeviceAddressMap);
}
@Override
protected ChannelInitializer<RxtxChannel> channelInitializer() {
// 实际使用替换成你的业务channelinitializer即可. 这里不展示ChannelInitializer用法
return new TestChannelInitializer<>();
}
@Override
protected void doChannelConfig(String targetChannelKey, RxtxChannelConfig channelConfig) {
// if(targetChannelKey=="MES") {br=19200}
channelConfig
.setBaudRate(115200)
.setDataBits(RxtxChannelConfig.DataBits.DATA_BITS_8)
.setStopBits(RxtxChannelConfig.StopBits.STOP_BITS_1)
.setParityBit(RxtxChannelConfig.ParityBit.NO)
.setDtr(false)
.setRts(false);
}
}
测试方法
// 这里直接使用main函数演示用法, 各位在项目中使用时,请将其作为spring bean来使用!!!
public static void main(String[] args) {
Map<String, RxtxDeviceAddress> map = new HashMap<>(2);
// key是用来检索channel的
// 这里是和SingleRxtxChannelTemplate 不一样的地方, 构造参数需要提供串口地址和key的映射关系
map.put("i am COM5", new RxtxDeviceAddress("COM5"));
map.put("i am COM6", new RxtxDeviceAddress("COM6"));
TestMultiRxtxTemplate testMultiTcp = new TestMultiRxtxTemplate(map);
ChannelFutureListener listener = new ActionChannelFutureListener()
.whenSuccess((l, cf) -> {
executor.scheduleAtFixedRate(() -> {
byte[] msg = new byte[300];
Arrays.fill(msg, (byte) 1);
cf.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
}, 2, 30, TimeUnit.MILLISECONDS);
System.err.println(cf.channel().localAddress() + ": ok");
})
.whenCancel((l, cf) -> System.err.println("cancel"))
.whenFailure(redo(cf -> testMultiTcp.connect(channelKey(cf)), 2, SECONDS))
.whenDone((l, cf) -> System.err.println("done"));
testMultiTcp.connectAll().values().forEach(c -> c.addListener(listener));
}
}
二. Java Serial Comm 简称JSC(推荐, 社区热情高涨,多平台支持,使用简单)
1. abstract SingleJscChannelTemplate
以下展示了一个简单的单通道jsc client, 继承SingleJscChannelTemplate即可,用起来几乎和rxtx一样
@Slf4j
@Component
public class TestSingleJscTemplate extends SingleJscChannelTemplate {
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public TestSingleJscTemplate(String commAddress) {
super(commAddress);
}
@Override
protected ChannelInitializer<JscChannel> channelInitializer() {
// 实际使用替换成你的业务channelinitializer即可. 这里不展示ChannelInitializer用法
return new TestChannelInitializer<>();
}
@Override
protected void doChannelConfig(JscChannelConfig channelConfig) {
channelConfig
.setBaudRate(115200)
.setDataBits(JscChannelConfig.DataBits.DATA_BITS_8)
.setStopBits(JscChannelConfig.StopBits.STOP_BITS_1)
.setParityBit(JscChannelConfig.ParityBit.NO)
.setDtr(false)
.setRts(false);
}
}
测试方法
// 这里直接使用main函数演示用法, 各位在项目中使用时,请将其作为spring bean来使用!!!
public static void main(String[] args) {
TestSingleJscTemplate testSingleJsc = new TestSingleJscTemplate("COM2");
ChannelFutureListener listener = new ActionChannelFutureListener()
.whenSuccess((l, cf) -> {
executor.scheduleAtFixedRate(() -> {
byte[] msg = new byte[300];
Arrays.fill(msg, (byte) 1);
testSingleJsc.writeAndFlush(Unpooled.wrappedBuffer(msg));
}, 2, 30, TimeUnit.MILLISECONDS);
Console.log(cf.channel().localAddress() + ": ok");
})
.whenCancel((l, cf) -> System.err.println("cancel"))
.whenFailure(redo(testSingleJsc::connect, 2, SECONDS))
.whenDone((l, cf) -> System.err.println("done"));
testSingleJsc.connect().addListener(listener);
// send msg
testSingleJsc.write("this is msg from write");
testSingleJsc.writeAndFlush("this is msg writeAndFlush");
}
}
2.abstract MultiJscChannelTemplate
以下展示了一个简单的多通道jscclient, 继承MultiJscChannelTemplate, 实际调用的时候, 大部分方法都需要指定key
import client.TestChannelInitializer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.fz.nettyx.action.ChannelFutureAction;
import org.fz.nettyx.endpoint.client.jsc.MultiJscChannelClient;
import org.fz.nettyx.endpoint.client.jsc.support.JscDeviceAddress;
/**
* @author fengbinbin
* @version 1.0
* @since 2024/3/1 22:58
*/
@Component
public class TestMultiJscTemplate extends MultiJscChannelTemplate<String> {
static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public TestMultiJscTemplate(Map<String, JscDeviceAddress> stringJscDeviceAddressMap) {
super(stringJscDeviceAddressMap);
}
@Override
protected ChannelInitializer<NioSocketChannel> channelInitializer() {
// 实际使用替换成你的业务channelinitializer即可. 这里不展示ChannelInitializer用法
return new TestChannelInitializer<>();
}
@Override
protected void doChannelConfig(String targetChannelKey, JscChannelConfig channelConfig) {
// if(targetChannelKey=="MES") {br=19200}
channelConfig
.setBaudRate(115200)
.setDataBits(JscChannelConfig.DataBits.DATA_BITS_8)
.setStopBits(JscChannelConfig.StopBits.STOP_BITS_1)
.setParityBit(JscChannelConfig.ParityBit.NO)
.setDtr(false)
.setRts(false);
}
}
测试方法
// 这里直接使用main函数演示用法, 各位在项目中使用时,请将其作为spring bean来使用!!!
public static void main(String[] args) {
Map<String, JscDeviceAddress> map = new HashMap<>(2);
map.put("5", new JscDeviceAddress("COM5"));
map.put("6", new JscDeviceAddress("COM6"));
TestMultiJscTemplate testMultiJsc = new TestMultiJscTemplate(map);
ChannelFutureListener listener = new ActionChannelFutureListener()
.whenSuccess((l, cf) -> {
executor.scheduleAtFixedRate(() -> {
byte[] msg = new byte[300];
Arrays.fill(msg, (byte) 1);
cf.channel().writeAndFlush(Unpooled.wrappedBuffer(msg));
}, 2, 30, TimeUnit.MILLISECONDS);
System.err.println(cf.channel().localAddress() + ": ok");
})
.whenCancel((l, cf) -> System.err.println("cancel"))
.whenFailure(redo(cf -> testMultiJsc.connect(channelKey(cf)), 2, SECONDS))
.whenDone((l, cf) -> System.err.println("done"));
testMultiJsc.connectAll().values().forEach(c -> c.addListener(listener));
// send msg
testMultiJsc.write("5", "this is msg from 5 write");
testMultiJsc.writeAndFlush("6", "this is msg from 6 writeAndFlush");
}
==========================================================================
至此完成了对Nettyx中串口通信
文末附上demo项目地址, 作为参考: https://gitee.com/fbbzl/nettyx-test