Spring Boot 中实现 COM 口数据监听并解析十六进制数据,结合多线程处理

在 Spring Boot 中实现 COM 口数据监听并解析十六进制数据,结合多线程处理,可通过以下步骤实现:


一、依赖配置

在 pom.xml 中添加串口通信库 ​​RXTXcomm​​:

xml 复制代码
<dependency>
    <groupId>org.rxtx</groupId>
    <artifactId>rxtx</artifactId>
    <version>2.2pre2</version>
</dependency>

二、串口配置类

创建串口参数配置类,定义波特率、数据位等参数:

arduino 复制代码
public class SerialConfig {
    private String portName = "COM3";  // 串口号
    private int baudRate = 9600;       // 波特率
    private int dataBits = 8;          // 数据位
    private int stopBits = 1;          // 停止位
    private int parity = SerialPort.PARITY_NONE; // 校验位

    // Getters and Setters
}

三、多线程串口监听实现

1. 串口服务类(使用 @Async 异步处理)

java 复制代码
@Service
public class SerialPortListener {

    @Autowired
    private SerialConfig config;

    private SerialPort serialPort;
    private ExecutorService executor = Executors.newFixedThreadPool(5); // 线程池

    @PostConstruct
    public void init() throws Exception {
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(config.getPortName());
        if (portIdentifier.isCurrentlyOwned()) {
            throw new RuntimeException("串口被占用");
        }
        CommPort commPort = portIdentifier.open(this.getClass().getName(), 2000);
        if (commPort instanceof SerialPort) {
            serialPort = (SerialPort) commPort;
            serialPort.setSerialPortParams(
                config.getBaudRate(),
                config.getDataBits(),
                config.getStopBits(),
                config.getParity()
            );
            serialPort.addEventListener(new SerialReader());
            serialPort.notifyOnDataAvailable(true);
        }
    }

    // 异步处理接收数据
    private class SerialReader implements SerialPortEventListener {
        @Override
        @Async("serialTaskExecutor") // 指定自定义线程池
        public void serialEvent(SerialPortEvent event) {
            if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                try (InputStream in = serialPort.getInputStream()) {
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = in.read(buffer)) > 0) {
                        byte[] data = Arrays.copyOf(buffer, len);
                        executor.submit(() -> processData(data)); // 多线程处理
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 十六进制解析方法
    private void processData(byte[] data) {
        String hexString = bytesToHex(data);
        System.out.println("接收数据(Hex): " + hexString);
        // 在此添加业务逻辑(如校验、协议解析等)
    }

    // 字节数组转十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }

    @PreDestroy
    public void close() {
        if (serialPort != null) {
            serialPort.close();
        }
    }
}

2. 自定义线程池配置

less 复制代码
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "serialTaskExecutor")
    public Executor serialTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);    // 核心线程数
        executor.setMaxPoolSize(10);     // 最大线程数
        executor.setQueueCapacity(100);  // 队列容量
        executor.setThreadNamePrefix("Serial-Thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

四、关键实现原理

  1. ​​事件驱动模型​ ​

    通过 SerialPortEventListener 监听 DATA_AVAILABLE 事件,避免轮询带来的性能损耗。

  2. ​​多线程处理​​

    • 使用 @Async 注解将数据处理逻辑异步化
    • 自定义线程池 serialTaskExecutor 控制并发量,防止资源耗尽
  3. ​​十六进制解析​ ​

    通过 String.format("%02X ", b) 将字节转为标准十六进制字符串,保留空格分隔符便于阅读。


五、使用示例

typescript 复制代码
@RestController
public class SerialController {

    @Autowired
    private SerialPortListener listener;

    @GetMapping("/start")
    public String startListening() {
        return "COM监听已启动";
    }

    @GetMapping("/hex-data")
    public String getLatestHexData() {
        // 此处可添加数据缓存逻辑
        return "最新数据需通过异步回调获取";
    }
}

六、注意事项

  1. ​​依赖冲突​ ​

    确保 rxtxSerial.dll(Windows)或 librxtxSerial.so(Linux)存在于 JVM 的 bin 目录下。

  2. ​​线程安全​ ​

    若需共享数据,使用 ConcurrentLinkedQueue 或 BlockingQueue 保证线程安全。

  3. ​​异常处理​ ​

    添加 try-catch 块捕获 PortInUseException、UnsupportedCommOperationException 等异常。

  4. ​​性能优化​​

    • 调整串口缓冲区大小:serialPort.setInputBufferSize(4096)
    • 使用 DMA 模式减少 CPU 占用(需硬件支持)

七、扩展功能

  1. ​​协议解析​ ​

    添加 MODBUS、自定义二进制协议等解析逻辑:

    arduino 复制代码
    private void parseModbus(byte[] data) {
        // 解析从站地址、功能码、数据区等
    }
  2. ​​数据持久化​ ​

    结合数据库或消息队列(如 Kafka)存储解析后的数据:

    typescript 复制代码
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    private void saveToKafka(String hexData) {
        kafkaTemplate.send("serial-data-topic", hexData);
    }
  3. ​​心跳检测​ ​

    定时发送检测命令验证设备在线状态:

    ini 复制代码
    @Scheduled(fixedRate = 5000)
    public void sendHeartbeat() {
        byte[] cmd = hexStringToBytes("01 03 00 00 00 01 84 0A");
        serialPort.getOutputStream().write(cmd);
    }

通过上述方案,可实现高效稳定的 COM 口数据监听与处理,适用于工业控制、物联网设备通信等场景。

相关推荐
BingoGo1 分钟前
PHP 紧急修复两项严重 RCE 漏洞,8.2 至 8.5 用户应立即升级
后端·php
紧跟先前的步伐15 分钟前
Go AI Agent 实战 05|Temperature 与生成参数:让模型输出可控
java·人工智能·golang
金銀銅鐵20 分钟前
[Java] 展示添加元素时 ArrayList 的内部变化
java·后端·ai编程
斌糖雪梨39 分钟前
spring auth 核心流程详解
java·后端·spring
高频因子挖掘机2 小时前
本地已有一年历史数据,第二天更新别只拉“昨天一天”:量化 K 线增量同步实践
后端·github·api
文丑颜不良啊2 小时前
LeetCode201~225题目解析
java·leetcode
高频因子挖掘机2 小时前
只补缺失日期,不重拉全部历史:增量更新 K 线应该怎么设计?
后端·github·api
Gopher_HBo3 小时前
Go reflect 包动态调用与方法反射 —Call、MakeFunc 与性能
后端
梦想出海-Phoebe3 小时前
AI Agent能实现自主复制吗?Anthropic研究揭示模型部署与IoT设备安全问题
人工智能·物联网·安全
码事漫谈4 小时前
人设带处境,AI 更入戏
后端