zsh_root
2025-12-10 8d662de2fd262b3a485f16e197cb4d0ca2a61cdf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// 声明包名 dell_system
package home;
 
// 导入串口通信库
import com.fazecast.jSerialComm.SerialPort;
// 导入 Java 函数式接口 Consumer,用于数据接收回调
import javax.swing.*;
import java.util.function.Consumer;
// 导入字节数组输出流
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
 
/**
 * 串口服务类
 * 负责串口的打开、关闭、数据收发
 */
public class SerialPortService {
 
    // 串口对象,用于与硬件通信
    private SerialPort port;
   
    // 标记是否正在捕获数据(线程运行中)
    private volatile boolean capturing = false;
    // 标记是否暂停接收数据
    private volatile boolean paused    = false;
 
    // 数据读取线程
    private Thread readerThread;
    private Consumer<byte[]> responseConsumer;
    
    
    // 优化:重用缓冲区,减少内存分配
    private byte[] readBuffer = new byte[200];
    private ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
    private Consumer<byte[]> dataReceivedCallback;
 
  
    public InputStream getInputStream() {
        if (port != null && port.isOpen()) {
            return port.getInputStream();
        }
        return null;
    }
 
    public OutputStream getOutputStream() {
        if (port != null && port.isOpen()) {
            return port.getOutputStream();
        }
        return null;
    }
    public void setComPortTimeouts(int timeoutMode, int readTimeout, int writeTimeout) {
        if (port != null && port.isOpen()) {
            port.setComPortTimeouts(timeoutMode, readTimeout, writeTimeout);
        }
    }
   
    
    public void startCapture() {
        if (dataReceivedCallback != null) {
            startCapture(dataReceivedCallback);
        } else {
            System.err.println("No data received callback set. Please call startCapture(Consumer<byte[]> onReceived) first.");
        }
   }
 
    /**
     * 打开串口
     * @param portName 串口名称(如 COM3)
     * @param baud 波特率(如 9600)
     * @return 是否成功打开
     */
    public boolean open(String portName, int baud) {
        // 如果串口已打开,直接返回成功
        if (port != null && port.isOpen()) {
            return true;
        }
 
        // 根据名称获取串口实例
        port = SerialPort.getCommPort(portName);
        // 设置串口参数:波特率、数据位8、停止位1、无校验位
        port.setComPortParameters(baud, 8, 1, SerialPort.NO_PARITY);
        // 修改为半阻塞模式,读超时1ms
        port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1, 0);
 
        // 打开串口并返回结果
        return port.openPort();
    }
    
    public void setResponseConsumer(Consumer<byte[]> consumer) {
        this.responseConsumer = consumer;
    }
 
    /**
     * 关闭串口
     */
    public void close() {
        // 先停止数据接收线程
        stopCapture();
        // 如果串口已打开,关闭它
        if (port != null && port.isOpen()) {
            port.closePort();
        }
        // 释放串口引用
        port = null;
    }
 
    /**
     * 启动数据接收线程
     * @param onReceived 数据接收回调函数,收到数据时调用
     */
    public void startCapture(Consumer<byte[]> onReceived) {
        this.dataReceivedCallback = onReceived;
        if (capturing || port == null || !port.isOpen()) return;
        capturing = true;
        paused = false;
 
        readerThread = new Thread(() -> {
            buffer.reset();
            long lastReceivedTime = 0;
            
            while (capturing && port.isOpen()) {
                if (paused) {
                    buffer.reset();
                    int len;
                    do {
                        len = port.readBytes(readBuffer, readBuffer.length);
                    } while (len > 0);
                    try { Thread.sleep(100); } catch (InterruptedException ignore) {}
                    continue;
                }
 
                long currentTime = System.currentTimeMillis();
                if (buffer.size() > 0 && (currentTime - lastReceivedTime) >= 20) {
                    byte[] data = buffer.toByteArray();
                    onReceived.accept(data);
                    if (responseConsumer != null) {
                        responseConsumer.accept(data);
                    }
                    buffer.reset();
                }
 
                int len = port.readBytes(readBuffer, readBuffer.length);
                currentTime = System.currentTimeMillis();
 
                if (len > 0) {
                    buffer.write(readBuffer, 0, len);
                    lastReceivedTime = currentTime;
                }
                
                if (len <= 0 && buffer.size() == 0) {
                    try { Thread.sleep(1); } catch (InterruptedException ignore) {}
                }
            }
            
            if (buffer.size() > 0) {
                byte[] data = buffer.toByteArray();
                onReceived.accept(data);
                if (responseConsumer != null) {
                    responseConsumer.accept(data);
                }
            }
        });
        readerThread.setDaemon(true);
        readerThread.start();
    }
    /**
     * 停止数据接收线程
     */
    public void stopCapture() {
        // 设置捕获标志为 false,通知线程退出
        capturing = false;
        // 如果线程存在,等待最多500ms确保线程结束
        if (readerThread != null) {
            try { readerThread.join(500); } catch (InterruptedException ignore) {}
            // 清空线程引用
            readerThread = null;
        }
    }
 
    // 设置暂停状态
    public void setPaused(boolean paused) {
        this.paused = paused;
    }
 
    // 获取当前是否暂停
    public boolean isPaused() {
        return paused;
    }
 
    // 判断串口是否已打开
    public boolean isOpen() {
        return port != null && port.isOpen();
    }
 
    /**
     * 发送数据
     * @param data 要发送的字节数组
     * @return 是否成功发送
     */
    public boolean send(byte[] data) {
        if (!isOpen()) {
            return false;
        }
        // 如果串口已初始化且已打开,发送数据并返回结果
        return port != null && port.isOpen() && port.writeBytes(data, data.length) > 0;
    }
}