|
|
@@ -16,7 +16,12 @@ import java.util.TimerTask;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
/**
|
|
|
- * 框架气密 PLC 定时轮询:扫码进站 + 检测完成上传。
|
|
|
+ * 框架气密 PLC 轮询(每秒一次)。
|
|
|
+ * <pre>
|
|
|
+ * 扫码反馈=1 → 读框架码校验 → 写允许进站=1、扫码反馈=0
|
|
|
+ * 检测完成:记录上一秒;上一秒=0 且本秒=1 → 上传 OK/NG
|
|
|
+ * 上传成功后:MES 复位允许进站=0;检测完成由 PLC 厂家清
|
|
|
+ * </pre>
|
|
|
*/
|
|
|
public class QmPlcPoller {
|
|
|
|
|
|
@@ -28,13 +33,12 @@ public class QmPlcPoller {
|
|
|
private static QmPlcConfig config;
|
|
|
private static QmS7Client s7Client;
|
|
|
|
|
|
+ /** 已允许进站,等待完成位 0→1 */
|
|
|
+ private static boolean armed;
|
|
|
+ private static boolean uploadDone;
|
|
|
+ /** 上一秒读到的检测完成 */
|
|
|
private static boolean lastComplete;
|
|
|
- private static boolean handshakeBusy;
|
|
|
- private static boolean uploadBusy;
|
|
|
- private static String currentSn = "";
|
|
|
- private static boolean entered;
|
|
|
- private static String lastCheckedSn = "";
|
|
|
- private static String lastUploadedSn = "";
|
|
|
+ private static boolean busy;
|
|
|
|
|
|
public static void start() {
|
|
|
config = QmPlcConfig.load();
|
|
|
@@ -58,10 +62,9 @@ public class QmPlcPoller {
|
|
|
}
|
|
|
}, 1000, 1000);
|
|
|
log.info("框架气密PLC轮询已启动 host={} DB{}", config.host, config.db);
|
|
|
- SwingUtilities.invokeLater(() -> MesClient.setMenuStatus("等待PLC扫码", 0));
|
|
|
+ SwingUtilities.invokeLater(() -> MesClient.setMenuStatus("等待PLC扫码反馈(框架码从DB读取)", 0));
|
|
|
}
|
|
|
|
|
|
- /** 调试页改 IP 后刷新轮询连接 */
|
|
|
public static void reloadHost(String host) {
|
|
|
if (config != null && s7Client != null) {
|
|
|
s7Client.updateHost(host);
|
|
|
@@ -80,13 +83,10 @@ public class QmPlcPoller {
|
|
|
}
|
|
|
|
|
|
public static void resetCycle() {
|
|
|
- currentSn = "";
|
|
|
- entered = false;
|
|
|
+ armed = false;
|
|
|
+ uploadDone = false;
|
|
|
lastComplete = false;
|
|
|
- handshakeBusy = false;
|
|
|
- uploadBusy = false;
|
|
|
- lastCheckedSn = "";
|
|
|
- // lastUploadedSn 保留,防止清完成位失败后重复上传;新进站时覆盖
|
|
|
+ busy = false;
|
|
|
}
|
|
|
|
|
|
private static void pollOnce() {
|
|
|
@@ -95,9 +95,10 @@ public class QmPlcPoller {
|
|
|
}
|
|
|
try {
|
|
|
handleScanFeedback();
|
|
|
- handleComplete();
|
|
|
+ handleCompleteRisingEdge();
|
|
|
} catch (Exception e) {
|
|
|
log.error("框架气密PLC轮询异常: {}", e.getMessage());
|
|
|
+ updateStatus("PLC异常: " + e.getMessage(), false);
|
|
|
if (s7Client != null) {
|
|
|
s7Client.close();
|
|
|
}
|
|
|
@@ -106,8 +107,12 @@ public class QmPlcPoller {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 扫码反馈=1 → 校验 → 写允许进站=1 并清扫码反馈=0。
|
|
|
+ * 反馈保持为1时每秒可重试(不因 entered 卡住)。
|
|
|
+ */
|
|
|
private static void handleScanFeedback() throws Exception {
|
|
|
- if (handshakeBusy || entered || MesClient.work_status == 1) {
|
|
|
+ if (busy) {
|
|
|
return;
|
|
|
}
|
|
|
boolean feedback = s7Client.readBit(config.scanFeedbackByte, config.scanFeedbackBit);
|
|
|
@@ -115,92 +120,82 @@ public class QmPlcPoller {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- handshakeBusy = true;
|
|
|
+ busy = true;
|
|
|
try {
|
|
|
String sn = s7Client.readFrameCode();
|
|
|
if (sn == null || sn.isEmpty()) {
|
|
|
updateStatus("PLC框架码为空,保持扫码反馈待重试", false);
|
|
|
return;
|
|
|
}
|
|
|
- if (sn.equals(lastCheckedSn)) {
|
|
|
- return;
|
|
|
- }
|
|
|
+
|
|
|
+ log.info("PLC扫码反馈=1,读取框架码并校验 sn={}", sn);
|
|
|
+ updateStatus("读取框架码校验中: " + sn, true);
|
|
|
|
|
|
JSONObject resp = DataUtil.checkQualityHttp(sn);
|
|
|
if (resp == null) {
|
|
|
- lastCheckedSn = sn;
|
|
|
s7Client.clearScanFeedback();
|
|
|
updateStatus("质量校验请求失败: " + sn, false);
|
|
|
return;
|
|
|
}
|
|
|
boolean pass = "true".equalsIgnoreCase(String.valueOf(resp.get("result")));
|
|
|
- lastCheckedSn = sn;
|
|
|
- if (pass) {
|
|
|
- s7Client.allowEntryAndClearScanFeedback();
|
|
|
- currentSn = sn;
|
|
|
- entered = true;
|
|
|
- lastComplete = s7Client.readBit(config.completeByte, config.completeBit);
|
|
|
- SwingUtilities.invokeLater(() -> {
|
|
|
- MesClient.product_sn.setText(sn);
|
|
|
- MesClient.product_sn.setEditable(false);
|
|
|
- MesClient.check_quality_result = true;
|
|
|
- MesClient.work_status = 1;
|
|
|
- MesClient.tjFlag = 1;
|
|
|
- if (MesClient.result != null) {
|
|
|
- MesClient.result.setText("等待结果");
|
|
|
- MesClient.result.setForeground(Color.GRAY);
|
|
|
- }
|
|
|
- });
|
|
|
- updateStatus("允许进站: " + sn + ",等待检测完成", true);
|
|
|
- } else {
|
|
|
+ if (!pass) {
|
|
|
s7Client.clearScanFeedback();
|
|
|
String msg = resp.getString("message");
|
|
|
if (msg == null || msg.isEmpty()) {
|
|
|
msg = "不可加工";
|
|
|
}
|
|
|
updateStatus(msg + " [" + sn + "]", false);
|
|
|
+ log.warn("校验未通过 sn={} msg={}", sn, msg);
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ s7Client.allowEntryAndClearScanFeedback();
|
|
|
+
|
|
|
+ // 同步当前完成位,避免进站瞬间把已是1的完成位当成本次0→1
|
|
|
+ lastComplete = s7Client.readBit(config.completeByte, config.completeBit);
|
|
|
+ armed = true;
|
|
|
+ uploadDone = false;
|
|
|
+
|
|
|
+ SwingUtilities.invokeLater(() -> {
|
|
|
+ if (MesClient.product_sn != null) {
|
|
|
+ MesClient.product_sn.setText(sn);
|
|
|
+ MesClient.product_sn.setEditable(false);
|
|
|
+ MesClient.product_sn.setFocusable(false);
|
|
|
+ }
|
|
|
+ MesClient.check_quality_result = true;
|
|
|
+ MesClient.work_status = 1;
|
|
|
+ if (MesClient.result != null) {
|
|
|
+ MesClient.result.setText("等待结果");
|
|
|
+ MesClient.result.setForeground(Color.GRAY);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ updateStatus("已允许进站: " + sn + ",等待检测完成0→1", true);
|
|
|
+ log.info("已写允许进站并清扫码反馈 sn={} lastComplete={}", sn, lastComplete);
|
|
|
} finally {
|
|
|
- handshakeBusy = false;
|
|
|
+ busy = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static void handleComplete() throws Exception {
|
|
|
- if (!entered || currentSn == null || currentSn.isEmpty()) {
|
|
|
- lastComplete = false;
|
|
|
- return;
|
|
|
- }
|
|
|
- if (uploadBusy) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 每秒读检测完成:上一秒=0 且本秒=1 → 上传。
|
|
|
+ * 不写检测完成;上传成功后复位允许进站=0。
|
|
|
+ */
|
|
|
+ private static void handleCompleteRisingEdge() throws Exception {
|
|
|
boolean complete = s7Client.readBit(config.completeByte, config.completeBit);
|
|
|
- // 保持完成位为 true 时持续尝试上传(失败可重试);首次从 false→true 或已完成待上传均可
|
|
|
- if (!complete) {
|
|
|
- lastComplete = false;
|
|
|
+ boolean rising = complete && !lastComplete;
|
|
|
+ lastComplete = complete;
|
|
|
+
|
|
|
+ if (busy || !armed || uploadDone || !rising) {
|
|
|
return;
|
|
|
}
|
|
|
- lastComplete = true;
|
|
|
|
|
|
- uploadBusy = true;
|
|
|
+ busy = true;
|
|
|
try {
|
|
|
String sn = s7Client.readFrameCode();
|
|
|
if (sn == null || sn.isEmpty()) {
|
|
|
- sn = currentSn;
|
|
|
- }
|
|
|
- if (sn == null || sn.isEmpty()) {
|
|
|
- log.error("框架气密采集失败:无框架码,保持检测完成待重试");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 已上传成功但清完成位失败时,只重试清位,避免重复提交
|
|
|
- if (sn.equals(lastUploadedSn)) {
|
|
|
- s7Client.clearComplete();
|
|
|
- SwingUtilities.invokeLater(() -> {
|
|
|
- MesClient.resetScanA();
|
|
|
- MesClient.setMenuStatus("测试结果上传成功,请扫下一件", 0);
|
|
|
- });
|
|
|
- resetCycle();
|
|
|
+ log.error("完成上升沿但框架码为空");
|
|
|
+ updateStatus("检测完成但框架码为空,无法上传", false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -213,35 +208,33 @@ public class QmPlcPoller {
|
|
|
boolean judgePass = s7Client.readBit(config.judgeResultByte, config.judgeResultBit);
|
|
|
String stationResult = judgePass ? "OK" : "NG";
|
|
|
|
|
|
- log.info("框架气密采集 sn={} 充气时间={} 保压时间={} 测试时间={} 充气压力={} 稳定压力={} 泄漏量={} 判定={}",
|
|
|
- sn, inflationTime, holdTime, testTime, inflationPressure, stablePressure, leak, stationResult);
|
|
|
-
|
|
|
- final String snF = sn;
|
|
|
- final String resultF = stationResult;
|
|
|
- final float stableF = stablePressure;
|
|
|
- final float leakF = leak;
|
|
|
- final float cqF = inflationTime;
|
|
|
- final float byF = holdTime;
|
|
|
- final float csF = testTime;
|
|
|
- final float inflationPressureF = inflationPressure;
|
|
|
+ log.info("完成0→1上传 sn={} 判定={}", sn, stationResult);
|
|
|
+ updateStatus("正在上传 " + sn + " " + stationResult + "...", true);
|
|
|
|
|
|
- TestParam testParam = buildTestParam(snF, resultF, stableF, leakF, cqF, byF, csF, inflationPressureF);
|
|
|
+ TestParam testParam = buildTestParam(sn, stationResult, stablePressure, leak,
|
|
|
+ inflationTime, holdTime, testTime, inflationPressure);
|
|
|
JSONObject resp = DataUtil.qmResultData(testParam);
|
|
|
- boolean ok = resp != null && "true".equalsIgnoreCase(String.valueOf(resp.get("result")));
|
|
|
- if (!ok) {
|
|
|
- log.error("框架气密结果上传失败,保持检测完成待重试 sn={}", snF);
|
|
|
- updateStatus("测试结果上传失败,请重试 [" + snF + "]", false);
|
|
|
+ boolean uploadOk = resp != null && "true".equalsIgnoreCase(String.valueOf(resp.get("result")));
|
|
|
+ if (!uploadOk) {
|
|
|
+ String msg = resp != null ? resp.getString("message") : "无响应";
|
|
|
+ log.error("上传失败 sn={} msg={}", sn, msg);
|
|
|
+ updateStatus("上传失败: " + msg + ",等完成位再次0→1", false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- lastUploadedSn = snF;
|
|
|
- s7Client.clearComplete();
|
|
|
+ boolean localOk = false;
|
|
|
try {
|
|
|
- JdbcUtils.insertTestRecord(testParam);
|
|
|
+ localOk = JdbcUtils.insertTestRecord(testParam);
|
|
|
} catch (Exception e) {
|
|
|
- log.warn("本地测试记录保存失败: {}", e.getMessage());
|
|
|
+ log.error("本地记录保存异常: {}", e.getMessage());
|
|
|
}
|
|
|
|
|
|
+ final String resultF = stationResult;
|
|
|
+ final float stableF = stablePressure;
|
|
|
+ final float leakF = leak;
|
|
|
+ final float byF = holdTime;
|
|
|
+ final float csF = testTime;
|
|
|
+ final boolean localSaved = localOk;
|
|
|
SwingUtilities.invokeLater(() -> {
|
|
|
if (MesClient.param1 != null) {
|
|
|
MesClient.param1.setText(fmt(stableF));
|
|
|
@@ -259,13 +252,35 @@ public class QmPlcPoller {
|
|
|
MesClient.result.setText(resultF);
|
|
|
MesClient.result.setForeground("OK".equals(resultF) ? Color.GREEN : Color.RED);
|
|
|
}
|
|
|
- MesClient.resetScanA();
|
|
|
- MesClient.setMenuStatus("测试结果上传成功,请扫下一件", 0);
|
|
|
+ if (MesClient.product_sn != null) {
|
|
|
+ MesClient.product_sn.setText(sn);
|
|
|
+ }
|
|
|
+ MesClient.work_status = 0;
|
|
|
+ MesClient.check_quality_result = false;
|
|
|
+ try {
|
|
|
+ JdbcUtils.getTestData();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("刷新记录查询失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
});
|
|
|
- resetCycle();
|
|
|
- log.info("框架气密采集完成 sn={} result={}", snF, resultF);
|
|
|
+
|
|
|
+ uploadDone = true;
|
|
|
+ armed = false;
|
|
|
+ try {
|
|
|
+ s7Client.clearAllowEntry();
|
|
|
+ log.info("上传成功后已复位允许进站=0 sn={}", sn);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("复位允许进站失败 sn={}: {}", sn, e.getMessage());
|
|
|
+ updateStatus("上传成功但复位允许进站失败: " + e.getMessage(), false);
|
|
|
+ }
|
|
|
+ if (localSaved) {
|
|
|
+ updateStatus("上传成功(" + stationResult + "),已复位进站,等待PLC扫码反馈", true);
|
|
|
+ } else {
|
|
|
+ updateStatus("上传成功(" + stationResult + "),本地记录失败,等待PLC扫码反馈", false);
|
|
|
+ }
|
|
|
+ log.info("上传成功 sn={} result={} localOk={}", sn, stationResult, localSaved);
|
|
|
} finally {
|
|
|
- uploadBusy = false;
|
|
|
+ busy = false;
|
|
|
}
|
|
|
}
|
|
|
|