Java 串口通信 Netty 最佳实践

概要

  如果你的领导坚持坚持坚持让你用java来进行串口交互, 不是项目有问题就是他有问题,为了解决你们的问题,Nettyx提供了两种串口通信实现, 分别是Rxtx系和Jsc系, 个人强烈建议使用Jsc, 毕竟Rxtx不仅老,而且使用的时候需要添加依赖包或平台执行文件至类路径, 麻烦啊
那正文开始了, 先引入Nettyx的依赖

确保使用最新版本nettyx,此处为了演示使用了如下版本
<dependency>
    <groupId>io.github.fbbzl</groupId>
    <artifactId>nettyx</artifactId>
    <version>2.3.3-RELEASE</version>
</dependency>

1. Rxtx(极力不推荐, 底层库陈旧, 无人维护, 使用麻烦, 平台限制)

  作为java曾经唯一能用的串口通信库, 现在已经全面被舍弃, rxtx的最后一次更新, 笔者还是未成年,你可敢信

abstract SingleRxtxChannelTemplate

  Nettyx依然提供了SingleRxtxChannelTemplate来初始化一个单串口通道客户端, 保存了单个rxtx-channel, 此客户端可以作为spring中的一个bean去使用, 完全可以当做是jdbcClient,redisClient, HttpClient这种工具组件去使用, 线程安全

以下展示了一个简单的单通道rxtxclient

/**
 *  继承SingleRxtxChannelTemplate即可创建一个串口通信客户端,配置一些参数即可使用, 很简单
/
@Component
public class TestSingleRxtx extends SingleRxtxChannelTemplate {

   // 用来重连的定时器, 用于定时重连
    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
 
    public TestSingleRxtx(String commAddress) {
        super(commAddress);
    }

    @Override
    protected ChannelInitializer<RxtxChannel> channelInitializer() {
        // 实际使用替换成你的业务channelinitializer即可. 这里不展示ChannelInitializer用法
        return new TestChannelInitializer<>();
    }
    @Override
    protected void doChannelConfig(RxtxChannelConfig channelConfig) {
        channelConfig
                .setBaudRate(115200)
                .setDataBits(RxtxChannelConfig.DataBits.DATABITS_8)
                .setStopBits(RxtxChannelConfig.StopBits.STOPBITS_1)
                .setParityBit(RxtxChannelConfig.ParityBit.NONE)
                .setDtr(false)
                .setRts(false);
    }
}

测试方法

    public static void main(String[] args) {
        TestSingleRxtx testSingleRxtx = new TestSingleRxtx("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);
    }
}

abstract MultiRxtxChannelTemplate

  部分业务可能需要你操作一组相同功能的channel. 如果按照之前的方式去创建,一个channel一个工具bean的话,将会难以管理, 所以Nettyx提供了MultiRxtxChannelTemplate, 让多个channel可以保存在一个组件中, 然后通过key去检索channel.大部分api都需要提供key,
类上通过泛型来指定key的类型.

以下展示了一个简单的多通道rxtxclient

@Component
 public class TestMultiRxtx extends MultiRxtxChannelTemplate<String> {

    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    protected TestMultiRxtx(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.DATABITS_8)
                .setStopBits(RxtxChannelConfig.StopBits.STOPBITS_1)
                .setParityBit(RxtxChannelConfig.ParityBit.NONE)
                .setDtr(false)
                .setRts(false);
    }
}

测试方法

    public static void main(String[] args) {
        Map<String, RxtxDeviceAddress> map = new HashMap<>(2);
   		//  key是用来检索channel的
   		// 这里是和SingleRxtxChannelTemplate 不一样的地方, 构造参数需要提供串口地址和key的映射关系
        map.put("5", new RxtxDeviceAddress("COM5"));
        map.put("zhangsan", new RxtxDeviceAddress("COM6"));

        TestMultiRxtx testMultiTcp = new TestMultiRxtx(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));
    }
}

2. JSC(推荐, 社区热情高涨,多平台支持,使用简单)

  JSC的使用几乎和rxtx完全一样

abstract SingleJscChannelTemplate

  以下展示了一个简单的单通道jsc client, 继承SingleJscChannelTemplate即可

 @Slf4j
 @Component
public class TestSingleJsc extends SingleJscChannelTemplate {

    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public TestSingleJsc(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.DATABITS_8)
                .setStopBits(JscChannelConfig.StopBits.ONE_STOP_BIT)
                .setParityBit(JscChannelConfig.ParityBit.NO_PARITY)
                .setDtr(false)
                .setRts(false);
    }
}

测试方法

    public static void main(String[] args) {
        TestSingleJsc testSingleJsc = new TestSingleJsc("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");
    }
}

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 TestMultiJsc extends MultiJscChannelTemplate<String> {

    static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

    public TestMultiJsc(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.DATABITS_8)
               .setStopBits(JscChannelConfig.StopBits.ONE_STOP_BIT)
               .setParityBit(JscChannelConfig.ParityBit.NO_PARITY)
               .setDtr(false)
               .setRts(false);
    }
}

测试方法

    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"));

        TestMultiJsc testMultiJsc = new TestMultiJsc(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中串口通信

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值