电子秤设备
This commit is contained in:
@@ -67,6 +67,13 @@
|
|||||||
<artifactId>aws-java-sdk-s3</artifactId>
|
<artifactId>aws-java-sdk-s3</artifactId>
|
||||||
<version>1.12.780</version>
|
<version>1.12.780</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fazecast</groupId>
|
||||||
|
<artifactId>jSerialComm</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
package top.wms.admin.controller.weighManage.ah;
|
||||||
|
|
||||||
|
import com.fazecast.jSerialComm.SerialPort;
|
||||||
|
import com.fazecast.jSerialComm.SerialPortDataListener;
|
||||||
|
import com.fazecast.jSerialComm.SerialPortEvent;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安衡电子秤连接类
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class AHDZCConnect {
|
||||||
|
|
||||||
|
private static final String PORT_NAME = "COM5";
|
||||||
|
private static final int BAUD_RATE = 9600;
|
||||||
|
private static final int DATA_BITS = 8;
|
||||||
|
private static final int STOP_BITS = 1;
|
||||||
|
private static final int TIMEOUT = 2000;
|
||||||
|
private static final Charset SCALE_CHARSET = Charset.forName("GBK");
|
||||||
|
|
||||||
|
private static final long HEALTH_CHECK_INTERVAL = 5000;
|
||||||
|
private static final long DATA_TIMEOUT = 10000;
|
||||||
|
private static final int MAX_RECONNECT_DELAY = 30000;
|
||||||
|
private static final int INITIAL_RECONNECT_DELAY = 1000;
|
||||||
|
private static final int BUFFER_SIZE = 256;
|
||||||
|
|
||||||
|
private volatile SerialPort serialPort;
|
||||||
|
private final AtomicBoolean isConnecting = new AtomicBoolean(false);
|
||||||
|
private final AtomicBoolean isRunning = new AtomicBoolean(true);
|
||||||
|
private final AtomicLong lastDataTime = new AtomicLong(System.currentTimeMillis());
|
||||||
|
private final AtomicInteger reconnectAttempts = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger currentReconnectDelay = new AtomicInteger(INITIAL_RECONNECT_DELAY);
|
||||||
|
|
||||||
|
private final byte[] readBuffer = new byte[BUFFER_SIZE];
|
||||||
|
|
||||||
|
private ScheduledExecutorService executorService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// 项目启动时初始化并启动服务
|
||||||
|
ScaleService();
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void destroy() {
|
||||||
|
// 项目关闭时停止服务
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void ScaleService() {
|
||||||
|
executorService = Executors.newScheduledThreadPool(2, r -> {
|
||||||
|
Thread t = new Thread(r, "ScaleService-Worker");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "Shutdown-Hook"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
log.info("========================================");
|
||||||
|
log.info("启动电子秤连接线程,串口: " + PORT_NAME + " | 波特率: " + BAUD_RATE);
|
||||||
|
log.info("========================================");
|
||||||
|
|
||||||
|
startHealthCheck();
|
||||||
|
startConnectionMonitor();
|
||||||
|
if (connect()) {
|
||||||
|
log.info("电子秤连接成功!");
|
||||||
|
} else {
|
||||||
|
log.info("电子秤连接失败,将自动尝试重连...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean connect() {
|
||||||
|
if (!isRunning.get()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConnecting.compareAndSet(false, true)) {
|
||||||
|
log.error("[连接] 已有连接任务正在执行,跳过");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
closeSerialPort();
|
||||||
|
|
||||||
|
SerialPort newPort = SerialPort.getCommPort(PORT_NAME);
|
||||||
|
newPort.setBaudRate(BAUD_RATE);
|
||||||
|
newPort.setNumDataBits(DATA_BITS);
|
||||||
|
newPort.setNumStopBits(STOP_BITS);
|
||||||
|
newPort.setParity(SerialPort.NO_PARITY);
|
||||||
|
newPort.setComPortTimeouts(TIMEOUT, TIMEOUT, TIMEOUT);
|
||||||
|
|
||||||
|
if (!newPort.openPort()) {
|
||||||
|
int attempts = reconnectAttempts.incrementAndGet();
|
||||||
|
int delay = calculateReconnectDelay();
|
||||||
|
log.info("[连接失败] 无法打开串口 {} (第{}次尝试,下次重试间隔: {}ms)", PORT_NAME, attempts, delay);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialPort = newPort;
|
||||||
|
reconnectAttempts.set(0);
|
||||||
|
currentReconnectDelay.set(INITIAL_RECONNECT_DELAY);
|
||||||
|
lastDataTime.set(System.currentTimeMillis());
|
||||||
|
|
||||||
|
log.info("[连接成功] 串口 {} 已打开", PORT_NAME);
|
||||||
|
|
||||||
|
startDataListener();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[连接异常] {}", e.getMessage());
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
isConnecting.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateReconnectDelay() {
|
||||||
|
int delay = currentReconnectDelay.get();
|
||||||
|
int newDelay = Math.min(delay * 2, MAX_RECONNECT_DELAY);
|
||||||
|
currentReconnectDelay.set(newDelay);
|
||||||
|
return newDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeSerialPort() {
|
||||||
|
SerialPort oldPort = serialPort;
|
||||||
|
serialPort = null;
|
||||||
|
|
||||||
|
if (oldPort != null) {
|
||||||
|
try {
|
||||||
|
if (oldPort.isOpen()) {
|
||||||
|
oldPort.closePort();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[关闭串口] 异常: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startDataListener() {
|
||||||
|
SerialPort port = serialPort;
|
||||||
|
if (port == null || !port.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
port.addDataListener(new SerialPortDataListener() {
|
||||||
|
@Override
|
||||||
|
public int getListeningEvents() {
|
||||||
|
return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialEvent(SerialPortEvent event) {
|
||||||
|
if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SerialPort currentPort = serialPort;
|
||||||
|
if (currentPort == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesAvailable = currentPort.bytesAvailable();
|
||||||
|
if (bytesAvailable <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesToRead = Math.min(bytesAvailable, BUFFER_SIZE);
|
||||||
|
int numRead = currentPort.readBytes(readBuffer, bytesToRead);
|
||||||
|
|
||||||
|
if (numRead > 0) {
|
||||||
|
lastDataTime.set(System.currentTimeMillis());
|
||||||
|
String data = new String(readBuffer, 0, numRead, SCALE_CHARSET);
|
||||||
|
log.info("[数据] {}", data.trim());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[读取异常] {}", e.getMessage());
|
||||||
|
triggerReconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startHealthCheck() {
|
||||||
|
log.info("[健康检查] 启动,间隔 {} 秒", HEALTH_CHECK_INTERVAL / 1000);
|
||||||
|
|
||||||
|
executorService.scheduleAtFixedRate(() -> {
|
||||||
|
if (!isRunning.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SerialPort port = serialPort;
|
||||||
|
|
||||||
|
if (port == null) {
|
||||||
|
log.error("[健康检查] 串口对象为空");
|
||||||
|
if (reconnectAttempts.get() < 10) {
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isOpen = port.isOpen();
|
||||||
|
log.error("[健康检查] 串口状态: {}", isOpen ? "已打开" : "已关闭");
|
||||||
|
|
||||||
|
if (!isOpen) {
|
||||||
|
log.info("[健康检查] 检测到串口关闭,尝试重连...");
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[健康检查异常] {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}, HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startConnectionMonitor() {
|
||||||
|
log.info("[连接监控] 启动,间隔 3 秒");
|
||||||
|
|
||||||
|
executorService.scheduleAtFixedRate(() -> {
|
||||||
|
if (!isRunning.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long timeSinceLastData = currentTime - lastDataTime.get();
|
||||||
|
|
||||||
|
if (timeSinceLastData > DATA_TIMEOUT) {
|
||||||
|
SerialPort port = serialPort;
|
||||||
|
|
||||||
|
if (port != null && port.isOpen()) {
|
||||||
|
try {
|
||||||
|
int bytesAvailable = port.bytesAvailable();
|
||||||
|
|
||||||
|
if (bytesAvailable < 0) {
|
||||||
|
log.info("[连接监控] 检测到连接已断开,尝试重连...");
|
||||||
|
triggerReconnect();
|
||||||
|
} else {
|
||||||
|
log.error("[连接监控] 超时无数据,但连接正常,等待数据...");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[连接监控] 检测到连接异常: {}", e.getMessage());
|
||||||
|
triggerReconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[连接监控异常] {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}, 3000, 3000, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerReconnect() {
|
||||||
|
if (isConnecting.compareAndSet(false, true)) {
|
||||||
|
isConnecting.set(false);
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (!isRunning.compareAndSet(true, false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("========================================");
|
||||||
|
log.info("停止电子秤线程...");
|
||||||
|
log.info("========================================");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (executorService != null && !executorService.isShutdown()) {
|
||||||
|
executorService.shutdown();
|
||||||
|
if (!executorService.awaitTermination(3, TimeUnit.SECONDS)) {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (executorService != null) {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSerialPort();
|
||||||
|
|
||||||
|
log.info("线程已完全停止");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user