基于 Netty + RXTX 的无协议 COM 通讯案例实现

news2024/11/20 18:24:10

参考

Netty集成串口RXTX编程,为什么过时了?

Java版本

java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

RXTX版本

# 官网 http://rxtx.qbang.org/wiki/index.php/Download
# 版本 rxtx-2.2pre1-bins

POM依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>
    </dependencies>

Com 配置类封装

package com.yushanma.config;

import io.netty.channel.rxtx.RxtxChannelConfig;
import lombok.Data;

/**
 * desc
 *
 * @author Yushanma
 * @since 2023/6/4 17:20
 */
@Data
public final class ComConfig {
    /**
     * 串口名称,以COM开头(COM0、COM1、COM2等等)
     */
    private String serialPortName;
    /**
     * 波特率, 默认:9600
     */
    private int baudRate = 9600;
    /**
     * 数据位 默认8位
     * 可以设置的值:SerialPort.DATABITS_5、SerialPort.DATABITS_6、SerialPort.DATABITS_7、SerialPort.DATABITS_8
     */
    private RxtxChannelConfig.Databits dataBits = RxtxChannelConfig.Databits.DATABITS_8;
    /**
     * 停止位
     * 可以设置的值:SerialPort.STOPBITS_1、SerialPort.STOPBITS_2、SerialPort.STOPBITS_1_5
     */
    private RxtxChannelConfig.Stopbits stopBits = RxtxChannelConfig.Stopbits.STOPBITS_1;
    /**
     * 校验位
     * 可以设置的值:SerialPort.PARITY_NONE、SerialPort.PARITY_ODD、SerialPort.PARITY_EVEN、SerialPort.PARITY_MARK、SerialPort.PARITY_SPACE
     */
    private RxtxChannelConfig.Paritybit parity = RxtxChannelConfig.Paritybit.NONE;
}

字节编解码工具

package com.yushanma.utils;

import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

/**
 * 解码器
 *
 * @author Yushanma
 * @since 2023/6/4 17:17
 */
public class ByteArrayDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 标记一下当前的 readIndex 的位置
        in.markReaderIndex();
        int dataLength = in.readableBytes();
        byte[] array = new byte[dataLength];
        in.readBytes(array, 0, dataLength);
        if(array.length > 0){
            out.add(array);
        }
    }
}
package com.yushanma.utils;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
 * 编码器
 *
 * @author Yushanma
 * @since 2023/6/4 17:18
 */
public class ByteArrayEncoder extends MessageToByteEncoder<byte[]> {

    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
        // 消息体,包含我们要发送的数据
        out.writeBytes(msg);
    }
}

Channel 事件处理器

package com.yushanma.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;

import java.time.LocalDateTime;

/**
 * Channel 事件处理器
 * 需要处理什么事件就重写 SimpleChannelInboundHandler 中对应的方法
 * @author Yushanma
 * @since 2023/6/4 17:19
 */
@ChannelHandler.Sharable
public class RxtxHandler extends SimpleChannelInboundHandler<byte[]> {

    /**
     * 当 COM 口接收到数据时
     * 根据不同的进制对数据包进行解码
     * 然后执行对应业务逻辑
     * @param ctx 上下文
     * @param msg 接收到的数据包
     * @throws Exception 异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {

        // 十六进制发送编解码
        int dataLength = msg.length;
        ByteBuf buf = Unpooled.buffer(dataLength);
        buf.writeBytes(msg);
        System.out.println(LocalDateTime.now() + "接收到:");
        while(buf.isReadable()){
            System.out.print(buf.readByte() + " ");
        }
        System.out.println();
        // 释放资源
        ReferenceCountUtil.release(msg);
    }
}

启动类封装

package com.yushanma.server;

import com.yushanma.config.ComConfig;
import com.yushanma.handler.RxtxHandler;
import com.yushanma.utils.ByteArrayDecoder;
import com.yushanma.utils.ByteArrayEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.rxtx.RxtxChannel;
import io.netty.channel.rxtx.RxtxDeviceAddress;
import io.netty.util.concurrent.GenericFutureListener;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

/**
 * desc
 *
 * @author Yushanma
 * @since 2023/6/4 17:23
 */
public class RxtxServer {

    private RxtxChannel channel;

    private ComConfig config;

    public RxtxServer(ComConfig config) {
        this.config = config;
    }

    public void createRxtx() throws Exception {
        // 串口使用阻塞io
        EventLoopGroup group = new OioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channelFactory(() -> {
                        RxtxChannel rxtxChannel = new RxtxChannel();
                        rxtxChannel.config()
                                .setBaudrate(config.getBaudRate()) // 波特率
                                .setDatabits(config.getDataBits()) // 数据位
                                .setParitybit(config.getParity())    // 校验位
                                .setStopbits(config.getStopBits()); // 停止位
                        return rxtxChannel;
                    })
                    .handler(new ChannelInitializer<RxtxChannel>() {
                        @Override
                        protected void initChannel(RxtxChannel rxtxChannel) {
                            rxtxChannel.pipeline().addLast(
                                    // 十六进制形式发送编解码
                                    new ByteArrayDecoder(),
                                    new ByteArrayEncoder(),
                                    new RxtxHandler()
                            );
                        }
                    });

            ChannelFuture f = bootstrap.connect(new RxtxDeviceAddress(config.getSerialPortName())).sync();
            f.addListener(connectedListener);

            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }


    // 连接监听
    GenericFutureListener<ChannelFuture> connectedListener = (ChannelFuture f) -> {
        final EventLoop eventLoop = f.channel().eventLoop();
        if (!f.isSuccess()) {
            System.out.println("连接失败");
        } else {
            channel = (RxtxChannel) f.channel();
            System.out.println("连接成功");
            sendData();
        }
    };

    /**
     * 发送数据
     */
    public void sendData() {
        // 十六机制形式发送
        ByteBuf buf = Unpooled.buffer(2);
        buf.writeByte(3);
        buf.writeByte(2);
        channel.writeAndFlush(buf.array());

        // 文本形式发送
        //channel.writeAndFlush("2");
    }

    public void start() {
        CompletableFuture.runAsync(() -> {
            try {
                // 阻塞的函数
                createRxtx();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, Executors.newSingleThreadExecutor());//不传默认使用ForkJoinPool,都是守护线程
    }

    public static void main(String[] args) {
        // 串口连接服务类
        ComConfig comConfig = new ComConfig();
        comConfig.setSerialPortName("COM1");
        comConfig.setBaudRate(9600);
        RxtxServer rxtxServer = new RxtxServer(comConfig);
        rxtxServer.start();
        try {
            // 连接串口需要一点时间,这里稍微等待一下
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 发送数据
        rxtxServer.sendData();
    }
}

测试效果

这里注意,因为解码是按照十六进制解码,所以需要以“十六进制发送”,“十六进制显示”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1058983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第 4 章 串(图书关键字索引表实现)

1. 背景说明 需要从书目文件中获取其关键字及对应的书号索引 bookInfo.txt 005 Computer Data Structures 010 Introduction to Data Structures 023 Fundamentals of Data Structures 034 The Design and Analysis of Computer Algorithms 050 Introduction to Numerical Anal…

STM32复习笔记(六):STM32远程升级BootLoader相关

目录 Preface&#xff1a; &#xff08;一&#xff09;STM32上电启动流程 &#xff08;二&#xff09;BootLoader相关 &#xff08;三&#xff09;Clion配置 Preface&#xff1a; 有关STM32的BootLoader主要还是参考了许多大佬的文章&#xff0c;这里只是简单地列举一下&am…

postgresql新特性之Merge

postgresql新特性之Merge 创建测试表测试案例 创建测试表 create table cps.public.test(id integer primary key,balance numeric,status varchar(1));测试案例 官网介绍 merge into test t using ( select 1 id,0 balance,Y status) s on(t.id s.id) -- 当匹配上了,statu…

STM32复习笔记(二):GPIO

目录 &#xff08;一&#xff09;Demo流程 &#xff08;二&#xff09;工程配置 &#xff08;三&#xff09;代码部分 &#xff08;四&#xff09;外部中断&#xff08;EXTI&#xff09; &#xff08;一&#xff09;Demo流程 首先&#xff0c;板子上有4个按键&#xff0c;…

网络技术在学校是学习网络协议吗

大家好&#xff0c;我是网络工程师成长日记实验室的郑老师&#xff0c;您现在正在查看的是网络工程师成长日记专栏&#xff0c;记录网络工程师日常生活的点点滴滴 一个同学说他现在也在学这个计算机专业&#xff0c;他以后他现在目前规划是把软考和华为的HIP1套。我自己本身也做…

AI时代给我们公司模式带来的改变

最近和几个朋友聊天&#xff0c;都说公司卷的更厉害了&#xff0c;老板趁着经济形势不好压榨的更厉害了&#xff01;但确实没办法&#xff0c;我们部分老板还是喜欢以人为本&#xff08;成本&#xff01;&#xff09;&#xff0c;更喜欢雇更多的人&#xff0c;看着他们低效而痛…

【Java】权限修饰符

目录 权限修饰符 权限修饰符-示例代码 权限修饰符 Java有四种访问权限&#xff0c;其中三种有访问权限修饰符&#xff0c;分别为 private&#xff0c;public 和 protected&#xff0c;还有一种不带任何修饰符&#xff1a; private&#xff1a;Java语言中对访问权限限制的最窄…

【C语言经典100例题-68】有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

方法一 将原数组拆成两部分&#xff0c;前面n-m个数和后面m个数。首先将前面n-m个数逆序&#xff0c;然后将后面的m个数逆序。最后将整个数组逆序即可。 #include <stdio.h>void reverse(int arr[], int start, int end) {for (int i start, j end; i < (start en…

快排三种递归及其优化,非递归和三路划分

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 快排简介&#xff1a; 快排的三种递归实现&#xff1a; Hoare&#xff1a; 挖坑&#xff1a; 双指针&#xff1a; 小区间优化&#xff1a; 三数取中优化&#xff1a; 快排非递归实现&#xff1a; 快排的三路划…

嵌入式Linux应用开发-驱动大全-第一章同步与互斥④

嵌入式Linux应用开发-驱动大全-第一章同步与互斥④ 第一章 同步与互斥④1.5 自旋锁spinlock的实现1.5.1 自旋锁的内核结构体1.5.2 spinlock在UP系统中的实现1.5.3 spinlock在SMP系统中的实现 1.6 信号量semaphore的实现1.6.1 semaphore的内核结构体1.6.2 down函数的实现1.6.3 u…

Android etc1tool之png图片转换pkm 和 zipalign简介

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、etc1tool2.1、用法 三、zipalign3.1 使用 四…

day49 ARM

.text .globl _start _start:mov r1,#1mov r2,#0mov r3,#100 fun2:cmp r2,r3bcc fun1 stop:b stop fun1: ADD r2,r2,r1add r4,r4,r2b fun2 .end

华为云云耀云服务器L实例评测|部署个人在线电子书库 calibre

华为云云耀云服务器L实例评测&#xff5c;部署个人在线电子书库 calibre 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 calibre3.1 calibre 介绍3.2 Docker 环境搭建3.3 c…

javaScript-事件循环-微任务-宏任务

为什么引入事件循环?如何理解&#xff1f; js是单线程的语言&#xff0c;需要把异步任务交给宿主浏览器执行&#xff0c;仿制js引擎堵塞 以下面的代码为例 异步的代码交给浏览器之后 进入队列中等待被调用&#xff1a; <!DOCTYPE html> <html lang"en"…

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占…

CharacterEncodingFilter的用法

CharacterEncoding是SpringMVC提供的一个一个过滤器,用于设置请求和响应的字符编码,解决乱码问题,他本身是一个过滤器 那么在SpringBoot中,CharacterEncoding就有一个很好的秒用 setEncoding("UTF-8")设置编码 setForceEncoding(true) 设置请求和响应编码 还需要在配…

树的存储结构以及树,二叉树,森林之间的转换

目录 1.双亲表示法 2.孩子链表 3.孩子兄弟表示法 4.树与二叉树的转换 &#xff08;1&#xff09;树转换为二叉树 &#xff08;2&#xff09;二叉树转换成树 5.二叉树与森林的转化 &#xff08;1&#xff09;森林转换为二叉树 以下树为例 1.双亲表示法 双亲表示法定义了…

javaee之通用mapper

通用mapper可以帮我们写sql语句 我们需要引入依赖是 通用mapper的核心依赖 它本身就依赖一个jpa的依赖&#xff0c;通用mapper的整体依赖就包含了通用mapper的核心依赖 下面说一下通用mapper里面的常见注解 KeySql的用法 tk.mybatis.mapper.common.Mapper 这个是通用Mapper的一…

c#设计模式-行为型模式 之 模板方法模式

&#x1f680;简介 模板方法模式定义了一个操作中的算法的骨架&#xff0c;而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下&#xff0c;重新定义算法中的某些步骤。通常用于应对在开发中设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定…

用 Pycharm 远程连接 Linux 服务器——超详细

用 Pycharm 远程连接 Linux 服务器——超详细 一、介绍二、要求三、服务器配置四、Pycharm远程连接Linux服务器 实战 一、介绍 本人是做NLP的&#xff0c;pycharm写的项目&#xff0c;数据集很大&#xff0c;在自己电脑上运行很慢&#xff0c;但是放到服务器上跑就很快。下面详…