package chushihua;
|
|
import chuankou.Sendmsg;
|
import home.MachineConfig;
|
import publicway.QueryData;
|
import xitongshezhi.Fkj;
|
import xitongshezhi.SystemDebugDialog;
|
|
import java.util.Map;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
/**
|
* 轮询查询类 - 优化版本
|
* 用于定时向所有卡槽发送查询指令
|
* 支持暂停和恢复功能,检查串口连接状态
|
*/
|
public class lunxun {
|
private static volatile boolean isRunning = false;
|
private static volatile boolean isPaused = false;
|
private static final AtomicBoolean shouldStop = new AtomicBoolean(false);
|
private static Thread pollingThread;
|
private static int pollingInterval = 100; // 默认轮询间隔
|
|
// 卡槽相关常量
|
private static final int MIN_SLOT = 1;
|
private static final int MAX_SLOT = 60;
|
|
// 串口连接检查相关
|
private static final int SERIAL_CHECK_INTERVAL = 5000; // 5秒检查一次串口连接
|
private static long lastSerialCheckTime = 0;
|
private static boolean serialConnected = false;
|
|
// 性能优化:查询指令缓存
|
private static final Map<Integer, String> queryCommandCache = new ConcurrentHashMap<>();
|
|
// 调试模式控制
|
public static boolean DEBUG_ENABLED = false;
|
|
/**
|
* 检查串口连接状态 - 优化版本,添加重试机制
|
* @return true-串口已连接, false-串口未连接
|
*/
|
public static boolean checkSerialConnection() {
|
// 避免频繁检查,每5秒检查一次
|
long currentTime = System.currentTimeMillis();
|
if (currentTime - lastSerialCheckTime < SERIAL_CHECK_INTERVAL) {
|
return serialConnected;
|
}
|
|
lastSerialCheckTime = currentTime;
|
|
try {
|
// 简化检查:直接检查Sendmsg的串口状态,而不是发送测试指令
|
boolean result = Sendmsg.isPortOpen();
|
|
if (result) {
|
if (DEBUG_ENABLED) {
|
//System.out.println("串口连接正常");
|
}
|
serialConnected = true;
|
} else {
|
System.err.println("串口连接失败 - 串口未打开");
|
serialConnected = false;
|
}
|
} catch (Exception e) {
|
System.err.println("串口连接检查异常: " + e.getMessage());
|
serialConnected = false;
|
}
|
|
return serialConnected;
|
}
|
|
/**
|
* 带重试的串口连接检查
|
*/
|
private static boolean checkSerialConnectionWithRetry() {
|
for (int i = 0; i < 3; i++) {
|
if (checkSerialConnection()) {
|
return true;
|
}
|
try {
|
Thread.sleep(1000);
|
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt();
|
return false;
|
}
|
}
|
return false;
|
}
|
|
/**
|
* 启动轮询查询 - 优化版本
|
* @return true-启动成功, false-启动失败
|
*/
|
public static boolean startPolling() {
|
if (isRunning) {
|
//System.out.println("轮询查询已经在运行中");
|
return true;
|
}
|
|
// 启动前严格检查串口连接
|
if (!checkSerialConnectionWithRetry()) {
|
System.err.println("串口未连接,无法启动轮询查询");
|
return false;
|
}
|
|
// 从配置中获取轮询间隔
|
loadPollingIntervalFromConfig();
|
|
isRunning = true;
|
isPaused = false;
|
shouldStop.set(false);
|
|
try {
|
pollingThread = new Thread(new PollingTask(), "CardSlot-Polling-Thread");
|
pollingThread.setDaemon(true);
|
pollingThread.start();
|
return true;
|
} catch (Exception e) {
|
System.err.println("启动轮询查询线程时发生异常: " + e.getMessage());
|
isRunning = false;
|
shouldStop.set(true);
|
return false;
|
}
|
}
|
|
/**
|
* 停止轮询查询 - 修复版本
|
* @return true-停止成功, false-停止失败
|
*/
|
public static boolean stopPolling() {
|
if (!isRunning) {
|
//System.out.println("轮询查询未在运行");
|
return false;
|
}
|
|
shouldStop.set(true);
|
isRunning = false;
|
isPaused = false;
|
|
if (pollingThread != null) {
|
pollingThread.interrupt();
|
try {
|
pollingThread.join(3000); // 等待3秒
|
// 检查线程是否还在运行
|
if (pollingThread.isAlive()) {
|
System.err.println("轮询线程未在3秒内停止,标记为守护线程并忽略");
|
// 不强制停止,而是确保它是守护线程
|
pollingThread.setDaemon(true);
|
}
|
} catch (InterruptedException e) {
|
System.err.println("停止轮询查询时被中断: " + e.getMessage());
|
Thread.currentThread().interrupt();
|
} catch (Exception e) {
|
System.err.println("停止轮询线程时发生异常: " + e.getMessage());
|
} finally {
|
pollingThread = null;
|
}
|
}
|
|
shouldStop.set(false);
|
//System.out.println("轮询查询已停止");
|
return true;
|
}
|
|
/**
|
* 暂停轮询查询
|
* @return true-暂停成功, false-暂停失败
|
*/
|
public static boolean pausePolling() {
|
if (!isRunning) {
|
if (DEBUG_ENABLED) {
|
//System.out.println("轮询查询未在运行,无法暂停");
|
}
|
return false;
|
}
|
|
if (isPaused) {
|
if (DEBUG_ENABLED) {
|
//System.out.println("轮询查询已经处于暂停状态");
|
}
|
return false;
|
}
|
|
isPaused = true;
|
//System.out.println("轮询查询已暂停");
|
return true;
|
}
|
|
/**
|
* 恢复轮询查询
|
* @return true-恢复成功, false-恢复失败
|
*/
|
public static boolean resumePolling() {
|
if (!isRunning) {
|
//System.out.println("轮询查询未在运行,无法恢复");
|
return false;
|
}
|
|
if (!isPaused) {
|
//System.out.println("轮询查询未处于暂停状态");
|
return false;
|
}
|
|
// 恢复前检查串口连接
|
if (!checkSerialConnectionWithRetry()) {
|
System.err.println("串口未连接,无法恢复轮询查询");
|
return false;
|
}
|
|
isPaused = false;
|
synchronized (lunxun.class) {
|
lunxun.class.notifyAll(); // 唤醒等待的线程
|
}
|
//System.out.println("轮询查询已恢复");
|
return true;
|
}
|
|
/**
|
* 检查轮询状态
|
* @return true-正在运行, false-已停止
|
*/
|
public static boolean isPolling() {
|
return isRunning;
|
}
|
|
/**
|
* 检查是否暂停
|
* @return true-已暂停, false-未暂停
|
*/
|
public static boolean isPaused() {
|
return isPaused;
|
}
|
|
/**
|
* 检查串口连接状态
|
* @return true-串口已连接, false-串口未连接
|
*/
|
public static boolean isSerialConnected() {
|
return serialConnected;
|
}
|
|
/**
|
* 设置轮询间隔
|
* @param interval 轮询间隔(毫秒)
|
*/
|
public static void setPollingInterval(int interval) {
|
if (interval < 10) {
|
System.err.println("轮询间隔不能小于10ms");
|
return;
|
}
|
|
pollingInterval = interval;
|
//System.out.println("轮询间隔已设置为: " + interval + "ms");
|
|
// 如果正在运行,重新启动以应用新的间隔
|
if (isRunning) {
|
restartPolling();
|
}
|
}
|
|
/**
|
* 获取当前轮询间隔
|
* @return 轮询间隔(毫秒)
|
*/
|
public static int getPollingInterval() {
|
return pollingInterval;
|
}
|
|
/**
|
* 重新启动轮询查询
|
* @return true-重启成功, false-重启失败
|
*/
|
public static boolean restartPolling() {
|
stopPolling();
|
|
// 等待一小段时间确保线程完全停止
|
try {
|
Thread.sleep(200);
|
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt();
|
}
|
|
return startPolling();
|
}
|
|
/**
|
* 从配置中加载轮询间隔
|
*/
|
private static void loadPollingIntervalFromConfig() {
|
try {
|
Chushihua configSystem = Chushihua.getInstance();
|
if (configSystem.isInitialized()) {
|
MachineConfig machineConfig = configSystem.getMachineConfig();
|
pollingInterval = machineConfig.getPollingInterval();
|
//System.out.println("从配置加载轮询间隔: " + pollingInterval + "ms");
|
} else {
|
//System.out.println("配置系统未初始化,使用默认轮询间隔: " + pollingInterval + "ms");
|
}
|
} catch (Exception e) {
|
System.err.println("加载轮询间隔配置失败: " + e.getMessage());
|
//System.out.println("使用默认轮询间隔: " + pollingInterval + "ms");
|
}
|
}
|
|
/**
|
* 获取缓存的查询指令
|
*/
|
private static String getCachedQueryCommand(int slotNumber) {
|
return queryCommandCache.computeIfAbsent(slotNumber, QueryData::queryData);
|
}
|
|
/**
|
* 清空查询指令缓存(当查询逻辑变化时调用)
|
*/
|
public static void clearQueryCache() {
|
queryCommandCache.clear();
|
//System.out.println("查询指令缓存已清空");
|
}
|
|
/**
|
* 轮询任务内部类 - 优化版本
|
* 优化内存使用:避免在循环中创建新对象
|
* 添加批量处理和性能监控
|
*/
|
private static class PollingTask implements Runnable {
|
private int currentIndex = 0; // 当前索引,用于遍历slotArray
|
private int consecutiveFailures = 0; // 连续失败次数
|
private static final int MAX_CONSECUTIVE_FAILURES = 5; // 最大连续失败次数
|
|
@Override
|
public void run() {
|
//System.out.println("轮询查询线程开始运行");
|
|
while (isRunning && !Thread.currentThread().isInterrupted() && !shouldStop.get()) {
|
try {
|
// 检查是否暂停
|
if (isPaused) {
|
synchronized (lunxun.class) {
|
while (isPaused && isRunning && !shouldStop.get()) {
|
lunxun.class.wait(1000); // 等待1秒或直到被唤醒
|
}
|
}
|
continue;
|
}
|
|
// 定期检查串口连接状态(每10次循环检查一次)
|
if (currentIndex % 10 == 0 && !checkSerialConnectionWithRetry()) {
|
System.err.println("串口连接断开,暂停轮询");
|
pausePolling();
|
continue;
|
}
|
|
// 获取卡槽数组
|
Fkj[] slotArray = SlotManager.getSlotArray();
|
if (slotArray == null || slotArray.length == 0) {
|
System.err.println("卡槽数组未初始化");
|
Thread.sleep(pollingInterval);
|
continue;
|
}
|
|
// 遍历所有卡槽,只给 hasCard != 1 的卡槽发送查询指令
|
boolean sentQuery = false;
|
int checkedSlots = 0;
|
int maxSlotsPerCycle = Math.min(10, slotArray.length); // 每周期最多检查10个卡槽
|
|
for (int i = 0; i < maxSlotsPerCycle && checkedSlots < slotArray.length; i++) {
|
Fkj slot = slotArray[currentIndex];
|
if (slot != null) {
|
String hasCard = slot.getHasCard();
|
if (!"1".equals(hasCard)) {
|
int slotNumber = currentIndex + 1;
|
if (sendQueryToSlot(slotNumber)) {
|
sentQuery = true;
|
consecutiveFailures = 0;
|
|
// 关键修复:在break前先更新索引
|
currentIndex = (currentIndex + 1) % slotArray.length;
|
checkedSlots++;
|
|
Thread.sleep(pollingInterval);
|
break;
|
} else {
|
consecutiveFailures++;
|
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
System.err.println("lunxun连续失败次数过多,暂停轮询");
|
pausePolling();
|
break;
|
}
|
}
|
}
|
}
|
|
// 对于不需要发送查询的卡槽,正常更新索引
|
currentIndex = (currentIndex + 1) % slotArray.length;
|
checkedSlots++;
|
}
|
|
// 如果没有找到需要查询的卡槽,等待一段时间再继续
|
if (!sentQuery) {
|
Thread.sleep(pollingInterval * 2); // 没有查询时等待时间加倍
|
}
|
|
} catch (InterruptedException e) {
|
//System.out.println("轮询查询线程被中断");
|
Thread.currentThread().interrupt();
|
break;
|
} catch (Exception e) {
|
System.err.println("轮询查询过程中发生异常: " + e.getMessage());
|
consecutiveFailures++;
|
|
// 发生异常时等待一段时间再继续
|
try {
|
Thread.sleep(1000);
|
} catch (InterruptedException ie) {
|
Thread.currentThread().interrupt();
|
break;
|
}
|
}
|
}
|
|
//System.out.println("轮询查询线程结束运行");
|
}
|
|
/**
|
* 向指定卡槽发送查询指令 - 优化版本
|
* 使用缓存指令,优化调试输出
|
*/
|
private boolean sendQueryToSlot(int slotNumber) {
|
try {
|
// 使用缓存的查询指令
|
String queryCommand = getCachedQueryCommand(slotNumber);
|
|
if (queryCommand != null && !queryCommand.trim().isEmpty()) {
|
// 发送到串口
|
boolean sendResult = Sendmsg.sendMessage(queryCommand);
|
|
if (sendResult) {
|
// 只在调试时输出,避免频繁打印
|
if (DEBUG_ENABLED) {
|
SystemDebugDialog.appendAsciiData(String.format("Slot %d Send query (hasCard !=1)\n", slotNumber));
|
}
|
return true;
|
} else {
|
if (DEBUG_ENABLED) {
|
SystemDebugDialog.appendAsciiData("Send query command to card slot err");
|
}
|
// 发送失败可能是串口断开,更新连接状态
|
serialConnected = false;
|
return false;
|
}
|
} else {
|
System.err.println("生成的查询指令为空,卡槽: " + slotNumber);
|
return false;
|
}
|
|
} catch (Exception e) {
|
System.err.println("发送查询指令到卡槽 " + slotNumber + " 时发生异常: " + e.getMessage());
|
// 发生异常时更新串口连接状态
|
serialConnected = false;
|
return false;
|
}
|
}
|
}
|
|
/**
|
* 立即向指定卡槽发送查询指令(不等待轮询)
|
* @param slotNumber 卡槽编号 (1-60)
|
* @return true-发送成功, false-发送失败
|
*/
|
public static boolean sendImmediateQuery(int slotNumber) {
|
if (slotNumber < MIN_SLOT || slotNumber > MAX_SLOT) {
|
System.err.println("卡槽编号必须在" + MIN_SLOT + "-" + MAX_SLOT + "之间");
|
return false;
|
}
|
|
// 检查串口连接
|
if (!checkSerialConnectionWithRetry()) {
|
System.err.println("串口未连接,无法发送查询指令");
|
return false;
|
}
|
|
try {
|
// 使用缓存的查询指令
|
String queryCommand = getCachedQueryCommand(slotNumber);
|
|
if (queryCommand != null && !queryCommand.trim().isEmpty()) {
|
// 发送到串口
|
boolean sendResult = Sendmsg.sendMessage(queryCommand);
|
|
if (sendResult) {
|
if (DEBUG_ENABLED) {
|
//System.out.println("立即查询成功 - 卡槽 " + slotNumber);
|
}
|
return true;
|
} else {
|
System.err.println("立即查询失败 - 发送指令到卡槽 " + slotNumber + " 失败");
|
return false;
|
}
|
} else {
|
System.err.println("立即查询失败 - 生成的查询指令为空,卡槽: " + slotNumber);
|
return false;
|
}
|
|
} catch (Exception e) {
|
System.err.println("立即查询卡槽 " + slotNumber + " 时发生异常: " + e.getMessage());
|
return false;
|
}
|
}
|
|
/**
|
* 立即向所有卡槽发送查询指令(批量)- 优化版本
|
* @return 成功发送的指令数量
|
*/
|
public static int sendImmediateQueryToAll() {
|
// 检查串口连接
|
if (!checkSerialConnectionWithRetry()) {
|
System.err.println("串口未连接,无法发送批量查询指令");
|
return 0;
|
}
|
|
int successCount = 0;
|
int batchSize = 5; // 每批次发送5个查询
|
int totalSlots = MAX_SLOT - MIN_SLOT + 1;
|
|
//System.out.println("开始批量查询所有卡槽...");
|
|
for (int batchStart = MIN_SLOT; batchStart <= MAX_SLOT; batchStart += batchSize) {
|
if (shouldStop.get()) {
|
break;
|
}
|
|
int batchEnd = Math.min(batchStart + batchSize - 1, MAX_SLOT);
|
|
// 批次内查询
|
for (int slot = batchStart; slot <= batchEnd; slot++) {
|
if (sendImmediateQuery(slot)) {
|
successCount++;
|
}
|
}
|
|
// 批次间间隔,避免串口拥堵
|
if (batchEnd < MAX_SLOT) {
|
try {
|
Thread.sleep(50);
|
} catch (InterruptedException e) {
|
Thread.currentThread().interrupt();
|
break;
|
}
|
}
|
}
|
|
//System.out.println("批量查询完成,成功发送: " + successCount + "/" + totalSlots);
|
return successCount;
|
}
|
|
/**
|
* 手动设置串口连接状态(用于外部检测到串口状态变化时调用)
|
* @param connected 串口连接状态
|
*/
|
public static void setSerialConnected(boolean connected) {
|
serialConnected = connected;
|
lastSerialCheckTime = System.currentTimeMillis();
|
|
if (connected) {
|
// //System.out.println("串口连接状态已设置为: 已连接");
|
} else {
|
System.err.println("串口连接状态已设置为: 未连接");
|
// 如果串口断开且轮询正在运行,自动暂停轮询
|
if (isRunning && !isPaused) {
|
pausePolling();
|
}
|
}
|
}
|
|
/**
|
* 获取轮询状态信息
|
* @return 状态信息字符串
|
*/
|
public static String getPollingStatus() {
|
String status;
|
if (!isRunning) {
|
status = "已停止";
|
} else if (isPaused) {
|
status = "已暂停";
|
} else {
|
status = "运行中";
|
}
|
|
String serialStatus = serialConnected ? "已连接" : "未连接";
|
int cacheSize = queryCommandCache.size();
|
|
return String.format("轮询状态: %s, 串口: %s, 间隔: %dms, 指令缓存: %d, 卡槽范围: %d-%d",
|
status, serialStatus, pollingInterval, cacheSize, MIN_SLOT, MAX_SLOT);
|
}
|
|
/**
|
* 直接设置轮询暂停状态(供其他类调用)
|
* @param paused true-暂停轮询, false-恢复轮询
|
* @return true-设置成功, false-设置失败(如轮询未运行)
|
*/
|
public static boolean setPollingPaused(boolean paused) {
|
if (!isRunning) {
|
//System.out.println("轮询查询未在运行,无法设置暂停状态");
|
return false;
|
}
|
|
if (paused) {
|
// 请求暂停
|
if (!isPaused) {
|
isPaused = true;
|
//System.out.println("轮询查询已通过外部调用暂停");
|
return true;
|
} else {
|
//System.out.println("轮询查询已经处于暂停状态");
|
return false;
|
}
|
} else {
|
// 请求恢复
|
if (isPaused) {
|
// 恢复前检查串口连接
|
if (!checkSerialConnectionWithRetry()) {
|
System.err.println("串口未连接,无法恢复轮询查询");
|
return false;
|
}
|
|
isPaused = false;
|
synchronized (lunxun.class) {
|
lunxun.class.notifyAll(); // 唤醒等待的线程
|
}
|
//System.out.println("轮询查询已通过外部调用恢复");
|
return true;
|
} else {
|
//System.out.println("轮询查询未处于暂停状态");
|
return false;
|
}
|
}
|
}
|
|
/**
|
* 获取性能统计信息
|
*/
|
public static String getPerformanceStats() {
|
return String.format("查询指令缓存大小: %d, 轮询间隔: %dms",
|
queryCommandCache.size(), pollingInterval);
|
}
|
|
public static boolean isDEBUG_ENABLED() {
|
return DEBUG_ENABLED;
|
}
|
|
public static void setDEBUG_ENABLED(boolean dEBUG_ENABLED) {
|
DEBUG_ENABLED = dEBUG_ENABLED;
|
}
|
}
|