|
@@ -2,11 +2,14 @@ package com.mes.laser;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
+import com.mes.print.PrintHelper;
|
|
|
import com.mes.ui.DataUtil;
|
|
import com.mes.ui.DataUtil;
|
|
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.BufferedReader;
|
|
|
import java.io.InputStream;
|
|
import java.io.InputStream;
|
|
|
import java.io.InputStreamReader;
|
|
import java.io.InputStreamReader;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
import java.util.Properties;
|
|
import java.util.Properties;
|
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
@@ -16,82 +19,87 @@ import java.util.concurrent.ScheduledExecutorService;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 镭雕流程管理器 —— 新方案:客户端不再联动镭雕机,改为定时轮询 mescloud
|
|
|
|
|
- * 拉取本产线待打印列表 → 建档 → 打印流转卡 → 上报 ack。
|
|
|
|
|
|
|
+ * 拉铆流转卡打印代理 —— 定时轮询服务端 pendingPrint,
|
|
|
|
|
+ * 拿到一条就走 PrintHelper 打印,成功后 ackPrint OK,失败 ackPrint NG。
|
|
|
*
|
|
*
|
|
|
- * 类名和公共 API 保持与旧版一致,MesClient.java 里的调用点不用改。
|
|
|
|
|
- * 旧的"获取镭雕码 / 完成回调 / 模拟"等语义在新流程下已经没有意义,
|
|
|
|
|
- * 相关方法保留但退化为空操作或转发到轮询开关。
|
|
|
|
|
|
|
+ * 数据流:
|
|
|
|
|
+ * 定时器 → GET /mesLaser/pendingPrint → 内存 Set 去重 → 单线程消费
|
|
|
|
|
+ * → PrintHelper.printFlowCard → POST /mesLaser/printAck
|
|
|
*
|
|
*
|
|
|
- * 数据流:定时器 → GET /mesLaser/pendingPrint → 内存 Set 去重 → 单线程消费
|
|
|
|
|
- * → onSubmitResult 建档(走 sendCreate 报文)→ LaserCardPrintUtil.printCard
|
|
|
|
|
- * → POST /mesLaser/printAck
|
|
|
|
|
- * @author hzd
|
|
|
|
|
|
|
+ * 状态语义(服务端表 mes_product_laser.rivet_print_status):
|
|
|
|
|
+ * 0 = 无需打印
|
|
|
|
|
+ * 1 = 待打印 ← 代理拉取
|
|
|
|
|
+ * 2 = 已打印 ← ack OK
|
|
|
|
|
+ * 3 = 打印失败 ← ack NG
|
|
|
*/
|
|
*/
|
|
|
public class LaserFlowManager {
|
|
public class LaserFlowManager {
|
|
|
|
|
|
|
|
public interface UiCallback {
|
|
public interface UiCallback {
|
|
|
/** 状态栏提示,error=true 走错误样式 */
|
|
/** 状态栏提示,error=true 走错误样式 */
|
|
|
void onStatus(String msg, boolean error);
|
|
void onStatus(String msg, boolean error);
|
|
|
- /** 打印开始/结束时的按钮锁定信号,UI 用来防止操作员打印进行中重复点击 */
|
|
|
|
|
|
|
+ /** 打印进行中/结束的按钮锁定信号,UI 用来防止重复点击 */
|
|
|
void onLockButton(boolean lock);
|
|
void onLockButton(boolean lock);
|
|
|
- /** 客户码回显到扫码框 */
|
|
|
|
|
- void onCodeReceived(String code);
|
|
|
|
|
- /** 客户端建档回调(走 op040 sendCreate 报文),true=建档成功 */
|
|
|
|
|
- boolean onSubmitResult(String customerSn, String qret);
|
|
|
|
|
- /** 待打印数量变化通知,UI 用来更新 pending label */
|
|
|
|
|
|
|
+ /** 待打印数量变化通知 */
|
|
|
void onPendingCount(int count);
|
|
void onPendingCount(int count);
|
|
|
/**
|
|
/**
|
|
|
- * 队列快照更新:3 行队列
|
|
|
|
|
- * rows[0] = 最近打完的一条(done=true,UI 显示绿框),首次启动没打过时为 null
|
|
|
|
|
- * rows[1..N] = 服务端 pending 最早的几条(done=false,UI 显示灰框)
|
|
|
|
|
|
|
+ * 队列快照:5 行
|
|
|
|
|
+ * rows[0] = 最近打完的一条(done=true 绿框),首次启动为 null
|
|
|
|
|
+ * rows[1..N] = 服务端 pending 最早几条(done=false 灰框)
|
|
|
*/
|
|
*/
|
|
|
- void onQueueUpdated(java.util.List<QueueRow> rows);
|
|
|
|
|
|
|
+ void onQueueUpdated(List<QueueRow> rows);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 队列行数据:钢印码 + 客户码 + 是否已打印 */
|
|
|
|
|
|
|
+ /** 队列行数据 */
|
|
|
public static class QueueRow {
|
|
public static class QueueRow {
|
|
|
|
|
+ /** 状态: 1=待打印, 2=已打印, 3=打印失败 */
|
|
|
|
|
+ public final int status;
|
|
|
public final String steelSn;
|
|
public final String steelSn;
|
|
|
public final String customerSn;
|
|
public final String customerSn;
|
|
|
- public final boolean done;
|
|
|
|
|
- public QueueRow(String steelSn, String customerSn, boolean done) {
|
|
|
|
|
|
|
+ public final String errMsg;
|
|
|
|
|
+
|
|
|
|
|
+ public QueueRow(int status, String steelSn, String customerSn, String errMsg) {
|
|
|
|
|
+ this.status = status;
|
|
|
this.steelSn = steelSn;
|
|
this.steelSn = steelSn;
|
|
|
this.customerSn = customerSn;
|
|
this.customerSn = customerSn;
|
|
|
- this.done = done;
|
|
|
|
|
|
|
+ this.errMsg = errMsg;
|
|
|
}
|
|
}
|
|
|
|
|
+ public boolean isDone() { return status == 2; }
|
|
|
|
|
+ public boolean isPending() { return status == 1; }
|
|
|
|
|
+ public boolean isFailed() { return status == 3; }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private final UiCallback uiCallback;
|
|
private final UiCallback uiCallback;
|
|
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
|
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
|
|
- Thread t = new Thread(r, "LaserPoll-Scheduler");
|
|
|
|
|
|
|
+ Thread t = new Thread(r, "RivetPoll-Scheduler");
|
|
|
t.setDaemon(true);
|
|
t.setDaemon(true);
|
|
|
return t;
|
|
return t;
|
|
|
});
|
|
});
|
|
|
- // 单线程打印,避免并发抢打印机队列,也保证扫码框回显顺序
|
|
|
|
|
|
|
+ // 单线程打印,避免并发抢打印机队列
|
|
|
private final ExecutorService printerExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
private final ExecutorService printerExecutor = Executors.newSingleThreadExecutor(r -> {
|
|
|
- Thread t = new Thread(r, "LaserPoll-Printer");
|
|
|
|
|
|
|
+ Thread t = new Thread(r, "RivetPoll-Printer");
|
|
|
t.setDaemon(true);
|
|
t.setDaemon(true);
|
|
|
return t;
|
|
return t;
|
|
|
});
|
|
});
|
|
|
- // 内存去重:已经在处理(打印中/待 ack)的客户码,防止 ack 之前被同一次拉取里的下一批重复消费
|
|
|
|
|
|
|
+ // 内存去重:正在处理(打印中/待 ack)的客户码,防止同一次拉取的下一批重复消费
|
|
|
private final Set<String> processing = ConcurrentHashMap.newKeySet();
|
|
private final Set<String> processing = ConcurrentHashMap.newKeySet();
|
|
|
|
|
|
|
|
- // 队列快照:最近打完的一条 + 上次轮询拿到的 pending 列表
|
|
|
|
|
|
|
+ // 队列快照
|
|
|
private volatile QueueRow lastPrintedRow = null;
|
|
private volatile QueueRow lastPrintedRow = null;
|
|
|
- private volatile java.util.List<QueueRow> lastPending = new java.util.ArrayList<>();
|
|
|
|
|
|
|
+ private volatile List<QueueRow> lastPending = new ArrayList<>();
|
|
|
|
|
|
|
|
- // paused = true 等价于手动模式(轮询定时器空转)
|
|
|
|
|
|
|
+ // paused=true 等价于手动模式(轮询定时器空转,只更新队列 UI 不打印)
|
|
|
private volatile boolean paused = false;
|
|
private volatile boolean paused = false;
|
|
|
private volatile boolean running = false;
|
|
private volatile boolean running = false;
|
|
|
|
|
|
|
|
|
|
+ // 累计计数
|
|
|
|
|
+ private int totalPrinted = 0;
|
|
|
|
|
+ private int totalFailed = 0;
|
|
|
|
|
+
|
|
|
// ==== 配置项 ====
|
|
// ==== 配置项 ====
|
|
|
- private String lineSn = "T9";
|
|
|
|
|
- private String prodCode = "T09";
|
|
|
|
|
private long pollIntervalMs = 3000;
|
|
private long pollIntervalMs = 3000;
|
|
|
private int batchSize = 10;
|
|
private int batchSize = 10;
|
|
|
- private String printerName = "";
|
|
|
|
|
- // 启动时默认模式:auto=自动轮询打印,manual=只在点击后打印一次
|
|
|
|
|
- private String defaultMode = "manual";
|
|
|
|
|
|
|
+ /** 启动默认模式:auto=自动轮询打印,manual=只更新 UI 不自动打 */
|
|
|
|
|
+ private String defaultMode = "auto";
|
|
|
|
|
|
|
|
public LaserFlowManager(UiCallback uiCallback) {
|
|
public LaserFlowManager(UiCallback uiCallback) {
|
|
|
this.uiCallback = uiCallback;
|
|
this.uiCallback = uiCallback;
|
|
@@ -105,111 +113,63 @@ public class LaserFlowManager {
|
|
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
|
|
|
pro.load(br);
|
|
pro.load(br);
|
|
|
}
|
|
}
|
|
|
- lineSn = pro.getProperty("mes.line_sn", "T9").trim();
|
|
|
|
|
- prodCode = pro.getProperty("mes.prod_code", "T09").trim();
|
|
|
|
|
- pollIntervalMs = Long.parseLong(pro.getProperty("mes.laser.poll_interval", "3000").trim());
|
|
|
|
|
- batchSize = Integer.parseInt(pro.getProperty("mes.laser.poll_batch_size", "10").trim());
|
|
|
|
|
- printerName = pro.getProperty("mes.laser.printer_name", "").trim();
|
|
|
|
|
- defaultMode = pro.getProperty("mes.laser.print_mode", "manual").trim().toLowerCase();
|
|
|
|
|
|
|
+ pollIntervalMs = Long.parseLong(pro.getProperty("mes.rivet.poll_interval", "3000").trim());
|
|
|
|
|
+ batchSize = Integer.parseInt(pro.getProperty("mes.rivet.poll_batch_size", "10").trim());
|
|
|
|
|
+ defaultMode = pro.getProperty("mes.rivet.print_mode", "auto").trim().toLowerCase();
|
|
|
paused = "manual".equals(defaultMode);
|
|
paused = "manual".equals(defaultMode);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
e.printStackTrace();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // ==== 对外 API(兼容 MesClient.java 原调用点)====
|
|
|
|
|
|
|
+ // ==== 对外 API ====
|
|
|
|
|
|
|
|
- /** 应用启动时调一次。旧名字沿用,实际启动的是轮询定时器 */
|
|
|
|
|
- public void startCompleteServer() {
|
|
|
|
|
|
|
+ public void start() {
|
|
|
if (running) return;
|
|
if (running) return;
|
|
|
running = true;
|
|
running = true;
|
|
|
scheduler.scheduleWithFixedDelay(this::pollOnce, 500, pollIntervalMs, TimeUnit.MILLISECONDS);
|
|
scheduler.scheduleWithFixedDelay(this::pollOnce, 500, pollIntervalMs, TimeUnit.MILLISECONDS);
|
|
|
- String mode = paused ? "手动模式(点按钮打一次)" : "自动模式(间隔 " + pollIntervalMs + " ms)";
|
|
|
|
|
- uiCallback.onStatus("镭雕轮询已启动 - " + mode, false);
|
|
|
|
|
|
|
+ uiCallback.onStatus("轮询已启动 - " + (paused ? "手动模式" : "自动模式(间隔" + pollIntervalMs + "ms)"), false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 应用关闭时调一次 */
|
|
|
|
|
- public void stopCompleteServer() {
|
|
|
|
|
|
|
+ public void stop() {
|
|
|
running = false;
|
|
running = false;
|
|
|
scheduler.shutdownNow();
|
|
scheduler.shutdownNow();
|
|
|
printerExecutor.shutdown();
|
|
printerExecutor.shutdown();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 旧 UI 用来判断"镭雕进行中"以禁用按钮。新方案下轮询是常态、单件打印是异步,
|
|
|
|
|
- * 直接返回是否有正在处理的记录即可。为了不误锁 UI,保守返回 false。
|
|
|
|
|
- */
|
|
|
|
|
- public boolean isWaitingComplete() {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /** 设置自动打印开关:true=开启定时轮询自动打印,false=只更新 UI 不自动打(靠队列行按钮打) */
|
|
|
|
|
|
|
+ /** 自动打印开关 */
|
|
|
public void setAutoMode(boolean auto) {
|
|
public void setAutoMode(boolean auto) {
|
|
|
paused = !auto;
|
|
paused = !auto;
|
|
|
- uiCallback.onStatus(auto ? "自动打印:开" : "自动打印:关", false);
|
|
|
|
|
|
|
+ uiCallback.onStatus(auto ? "自动打印: 开" : "自动打印: 关(仅刷新队列)", false);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public boolean isAutoMode() {
|
|
public boolean isAutoMode() {
|
|
|
return !paused;
|
|
return !paused;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 提交钢印码并打印,独立于轮询队列。操作员手工输入 steelSn 后调用 */
|
|
|
|
|
- public void submitAndPrint(String steelSn) {
|
|
|
|
|
- if (steelSn == null || steelSn.trim().isEmpty()) {
|
|
|
|
|
- uiCallback.onStatus("请输入钢印码", true);
|
|
|
|
|
|
|
+ /** 手动重打指定一条 */
|
|
|
|
|
+ public void reprint(String customerSn, String steelSn) {
|
|
|
|
|
+ if (customerSn == null || customerSn.isEmpty() || steelSn == null || steelSn.isEmpty()) {
|
|
|
|
|
+ uiCallback.onStatus("参数缺失,无法打印", true);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- final String ss = steelSn.trim();
|
|
|
|
|
- printerExecutor.submit(() -> {
|
|
|
|
|
- uiCallback.onLockButton(true);
|
|
|
|
|
- try {
|
|
|
|
|
- uiCallback.onStatus("正在提交钢印码到服务端:" + ss, false);
|
|
|
|
|
- JSONObject resp = DataUtil.submitSteelSn(prodCode, ss);
|
|
|
|
|
- if (resp == null || resp.get("result") == null
|
|
|
|
|
- || !"true".equalsIgnoreCase(resp.get("result").toString())) {
|
|
|
|
|
- String msg = resp != null && resp.get("message") != null
|
|
|
|
|
- ? resp.get("message").toString() : "服务端无响应";
|
|
|
|
|
- uiCallback.onStatus("提交失败:" + msg, true);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- JSONObject data = resp.getJSONObject("data");
|
|
|
|
|
- String customerSn = data == null ? null : data.getString("customerSn");
|
|
|
|
|
- if (customerSn == null || customerSn.isEmpty()) {
|
|
|
|
|
- uiCallback.onStatus("服务端未返回客户码,无法打印", true);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- // 回显客户码到扫码框
|
|
|
|
|
- uiCallback.onCodeReceived(customerSn);
|
|
|
|
|
- uiCallback.onStatus("已入库,正在建档并打印:" + customerSn, false);
|
|
|
|
|
-
|
|
|
|
|
- // 直接打印(此时已在 printerExecutor 单线程里,直接 printOne 串行执行)
|
|
|
|
|
- if (!processing.add(customerSn)) return;
|
|
|
|
|
- try {
|
|
|
|
|
- printOne(customerSn, ss);
|
|
|
|
|
- } finally {
|
|
|
|
|
- processing.remove(customerSn);
|
|
|
|
|
- }
|
|
|
|
|
- } finally {
|
|
|
|
|
- uiCallback.onLockButton(false);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ submitPrintJob(customerSn, steelSn);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public int getTotalPrinted() { return totalPrinted; }
|
|
|
|
|
+ public int getTotalFailed() { return totalFailed; }
|
|
|
|
|
|
|
|
|
|
+ /** UI 查询某条 SN 是否正在打印中,用于按钮置灰 */
|
|
|
|
|
+ public boolean isProcessing(String customerSn) {
|
|
|
|
|
+ return customerSn != null && processing.contains(customerSn);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// ==== 内部实现 ====
|
|
// ==== 内部实现 ====
|
|
|
|
|
|
|
|
- /** 定时器每 pollIntervalMs 触发一次。无论开关状态都拉数据更新 UI,只有开关 ON 时才自动打印 */
|
|
|
|
|
|
|
+ /** 定时器每 pollIntervalMs 触发一次:拉取列表并更新 UI,自动模式下逐条打印 */
|
|
|
private void pollOnce() {
|
|
private void pollOnce() {
|
|
|
- pollBatch(batchSize, !paused);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 拉待打印列表并更新 UI 队列;printAll=true 时把拉到的每一条都送去打印(仅自动模式用)
|
|
|
|
|
- */
|
|
|
|
|
- private void pollBatch(int limit, boolean printAll) {
|
|
|
|
|
try {
|
|
try {
|
|
|
- JSONObject resp = DataUtil.listPendingPrint(lineSn, limit);
|
|
|
|
|
|
|
+ JSONObject resp = DataUtil.listPendingPrint(batchSize);
|
|
|
if (resp == null || resp.get("result") == null
|
|
if (resp == null || resp.get("result") == null
|
|
|
|| !"true".equalsIgnoreCase(resp.get("result").toString())) {
|
|
|| !"true".equalsIgnoreCase(resp.get("result").toString())) {
|
|
|
return;
|
|
return;
|
|
@@ -217,98 +177,94 @@ public class LaserFlowManager {
|
|
|
Object dataObj = resp.get("data");
|
|
Object dataObj = resp.get("data");
|
|
|
if (!(dataObj instanceof JSONArray)) return;
|
|
if (!(dataObj instanceof JSONArray)) return;
|
|
|
JSONArray list = (JSONArray) dataObj;
|
|
JSONArray list = (JSONArray) dataObj;
|
|
|
- uiCallback.onPendingCount(list.size());
|
|
|
|
|
- // 更新 pending 快照供 UI 展示:取前 5 条足够填 5 行 UI(即使 Row0 让给了 lastPrinted)
|
|
|
|
|
- java.util.List<QueueRow> newPending = new java.util.ArrayList<>();
|
|
|
|
|
- for (int i = 0; i < Math.min(5, list.size()); i++) {
|
|
|
|
|
|
|
+ // 拉到的记录包含 status=1(待打印) 和 status=3(失败)
|
|
|
|
|
+ int pendingCnt = 0;
|
|
|
|
|
+ List<QueueRow> newPending = new ArrayList<>();
|
|
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
JSONObject r = list.getJSONObject(i);
|
|
JSONObject r = list.getJSONObject(i);
|
|
|
- newPending.add(new QueueRow(r.getString("steel_sn"), r.getString("customer_sn"), false));
|
|
|
|
|
|
|
+ Integer st = r.getInteger("rivet_print_status");
|
|
|
|
|
+ int status = st == null ? 1 : st;
|
|
|
|
|
+ if (status == 1) pendingCnt++;
|
|
|
|
|
+ if (newPending.size() < 5) {
|
|
|
|
|
+ newPending.add(new QueueRow(
|
|
|
|
|
+ status,
|
|
|
|
|
+ r.getString("steel_sn"),
|
|
|
|
|
+ r.getString("customer_sn"),
|
|
|
|
|
+ r.getString("print_msg")));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ uiCallback.onPendingCount(pendingCnt);
|
|
|
lastPending = newPending;
|
|
lastPending = newPending;
|
|
|
pushQueueSnapshot();
|
|
pushQueueSnapshot();
|
|
|
|
|
|
|
|
- if (!printAll || list.isEmpty()) return;
|
|
|
|
|
|
|
+ if (paused || list.isEmpty()) return;
|
|
|
|
|
|
|
|
- // 自动模式:把拉到的每一条都送去打印(内存 processing 去重防止重复)
|
|
|
|
|
|
|
+ // 自动模式:仅打 status=1;status=3(失败) 只在 UI 展示,等人工点重打
|
|
|
for (int i = 0; i < list.size(); i++) {
|
|
for (int i = 0; i < list.size(); i++) {
|
|
|
JSONObject row = list.getJSONObject(i);
|
|
JSONObject row = list.getJSONObject(i);
|
|
|
- submitPrintJob(row.getString("customer_sn"), row.getString("steel_sn"));
|
|
|
|
|
|
|
+ Integer st = row.getInteger("rivet_print_status");
|
|
|
|
|
+ if (st != null && st == 1) {
|
|
|
|
|
+ submitPrintJob(row.getString("customer_sn"), row.getString("steel_sn"));
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
e.printStackTrace();
|
|
e.printStackTrace();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /** 打印指定的一条:队列 UI 行点「打印」按钮走这里 */
|
|
|
|
|
- public void printSpecific(String customerSn, String steelSn) {
|
|
|
|
|
- if (customerSn == null || customerSn.isEmpty() || steelSn == null || steelSn.isEmpty()) {
|
|
|
|
|
- uiCallback.onStatus("参数缺失,无法打印", true);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- submitPrintJob(customerSn, steelSn);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /** 把一次打印任务塞到单线程 executor,内存去重防止同一 sn 并发打两次 */
|
|
|
|
|
|
|
+ /** 把一次打印任务塞到单线程 executor */
|
|
|
private void submitPrintJob(String customerSn, String steelSn) {
|
|
private void submitPrintJob(String customerSn, String steelSn) {
|
|
|
if (customerSn == null || steelSn == null) return;
|
|
if (customerSn == null || steelSn == null) return;
|
|
|
- if (!processing.add(customerSn)) return;
|
|
|
|
|
|
|
+ if (!processing.add(customerSn)) return; // 已在处理,跳过
|
|
|
|
|
+ // 立即通知 UI 刷新(这条会显示"打印中")
|
|
|
|
|
+ pushQueueSnapshot();
|
|
|
printerExecutor.submit(() -> {
|
|
printerExecutor.submit(() -> {
|
|
|
try {
|
|
try {
|
|
|
uiCallback.onLockButton(true);
|
|
uiCallback.onLockButton(true);
|
|
|
- uiCallback.onCodeReceived(customerSn);
|
|
|
|
|
- uiCallback.onStatus("正在建档并打印:" + customerSn, false);
|
|
|
|
|
|
|
+ uiCallback.onStatus("正在打印: " + customerSn, false);
|
|
|
printOne(customerSn, steelSn);
|
|
printOne(customerSn, steelSn);
|
|
|
} finally {
|
|
} finally {
|
|
|
processing.remove(customerSn);
|
|
processing.remove(customerSn);
|
|
|
uiCallback.onLockButton(false);
|
|
uiCallback.onLockButton(false);
|
|
|
|
|
+ // 任务收尾再刷新一次,让按钮从"打印中"恢复
|
|
|
|
|
+ pushQueueSnapshot();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 单条打印:调 PrintHelper → ack → 更新计数和 UI */
|
|
|
private void printOne(String customerSn, String steelSn) {
|
|
private void printOne(String customerSn, String steelSn) {
|
|
|
- // 1. 建档(走 op040 现有 sendCreate 报文)
|
|
|
|
|
- boolean buildOk = uiCallback.onSubmitResult(customerSn, "OK");
|
|
|
|
|
- if (!buildOk) {
|
|
|
|
|
- uiCallback.onStatus("建档失败:" + customerSn + ",下次继续重试", true);
|
|
|
|
|
- DataUtil.ackPrint(customerSn, "NG", "客户端建档失败");
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- // 2. 打印流转卡(PrinterJob.print 返回 = 任务提交系统队列成功 = 视为完成)
|
|
|
|
|
- boolean printOk = LaserCardPrintUtil.printCard(printerName, steelSn, customerSn);
|
|
|
|
|
- // 3. ack:让服务端把 print_status 从 0 更新为 1
|
|
|
|
|
|
|
+ boolean printOk = PrintHelper.printFlowCard(customerSn, steelSn);
|
|
|
if (printOk) {
|
|
if (printOk) {
|
|
|
boolean ackOk = DataUtil.ackPrint(customerSn, "OK", null);
|
|
boolean ackOk = DataUtil.ackPrint(customerSn, "OK", null);
|
|
|
if (ackOk) {
|
|
if (ackOk) {
|
|
|
- uiCallback.onStatus("打印完成并已入库:" + customerSn, false);
|
|
|
|
|
|
|
+ totalPrinted++;
|
|
|
|
|
+ uiCallback.onStatus("打印完成: " + customerSn, false);
|
|
|
} else {
|
|
} else {
|
|
|
- uiCallback.onStatus("打印完成但 ack 失败(服务端会保留待打印状态):" + customerSn, true);
|
|
|
|
|
|
|
+ uiCallback.onStatus("打印完成但 ack 失败(下次会重打): " + customerSn, true);
|
|
|
}
|
|
}
|
|
|
- // 顶部绿框显示刚打完的这条
|
|
|
|
|
- lastPrintedRow = new QueueRow(steelSn, customerSn, true);
|
|
|
|
|
- // 先清空 lastPending 里跟刚打完这条重复的记录,避免 UI 出现 "已打印+待打印" 两行内容一样
|
|
|
|
|
- java.util.List<QueueRow> filtered = new java.util.ArrayList<>();
|
|
|
|
|
|
|
+ lastPrintedRow = new QueueRow(2, steelSn, customerSn, null);
|
|
|
|
|
+ // 清掉 pending 里跟刚打完这条重复的
|
|
|
|
|
+ List<QueueRow> filtered = new ArrayList<>();
|
|
|
for (QueueRow r : lastPending) {
|
|
for (QueueRow r : lastPending) {
|
|
|
if (r != null && !customerSn.equals(r.customerSn)) filtered.add(r);
|
|
if (r != null && !customerSn.equals(r.customerSn)) filtered.add(r);
|
|
|
}
|
|
}
|
|
|
lastPending = filtered;
|
|
lastPending = filtered;
|
|
|
pushQueueSnapshot();
|
|
pushQueueSnapshot();
|
|
|
- // 立即触发一次刷新(printAll=false 只更新 UI 不打印,避免重复打)
|
|
|
|
|
- scheduler.execute(() -> pollBatch(batchSize, false));
|
|
|
|
|
} else {
|
|
} else {
|
|
|
DataUtil.ackPrint(customerSn, "NG", "客户端打印失败");
|
|
DataUtil.ackPrint(customerSn, "NG", "客户端打印失败");
|
|
|
- uiCallback.onStatus("打印失败:" + customerSn, true);
|
|
|
|
|
|
|
+ totalFailed++;
|
|
|
|
|
+ uiCallback.onStatus("打印失败: " + customerSn, true);
|
|
|
|
|
+ // 立即触发一次拉取,UI 能看到该条被服务端标记为 status=3
|
|
|
|
|
+ scheduler.execute(this::pollOnce);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 合成 5 行队列推给 UI。策略:
|
|
|
|
|
- * 有 lastPrinted:Row 0 = lastPrinted(绿框),Row 1-4 = pending[0..3]
|
|
|
|
|
- * 没有:Row 0-4 全部是 pending[0..4]("只有 1 条就在第一行")
|
|
|
|
|
- */
|
|
|
|
|
|
|
+ /** 合成 5 行队列推给 UI */
|
|
|
private void pushQueueSnapshot() {
|
|
private void pushQueueSnapshot() {
|
|
|
final int total = 5;
|
|
final int total = 5;
|
|
|
- java.util.List<QueueRow> rows = new java.util.ArrayList<>(total);
|
|
|
|
|
- java.util.List<QueueRow> p = lastPending;
|
|
|
|
|
|
|
+ List<QueueRow> rows = new ArrayList<>(total);
|
|
|
|
|
+ List<QueueRow> p = lastPending;
|
|
|
if (lastPrintedRow != null) {
|
|
if (lastPrintedRow != null) {
|
|
|
rows.add(lastPrintedRow);
|
|
rows.add(lastPrintedRow);
|
|
|
for (int i = 0; i < total - 1; i++) {
|
|
for (int i = 0; i < total - 1; i++) {
|