|
|
@@ -0,0 +1,344 @@
|
|
|
+package com.jeesite.modules.scheduling;
|
|
|
+
|
|
|
+import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
|
|
|
+import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
|
|
|
+import com.jeesite.common.config.Global;
|
|
|
+import com.jeesite.common.lang.ObjectUtils;
|
|
|
+import com.jeesite.common.lang.StringUtils;
|
|
|
+import com.jeesite.modules.mes.entity.MesLine;
|
|
|
+import com.jeesite.modules.mes.entity.MesProduct;
|
|
|
+import com.jeesite.modules.mes.entity.MesProductQmzc;
|
|
|
+import com.jeesite.modules.mes.resp.BindMaterialResp;
|
|
|
+import com.jeesite.modules.mes.resp.ParamsResp;
|
|
|
+import com.jeesite.modules.mes.service.MesLineService;
|
|
|
+import com.jeesite.modules.mes.service.MesProductQmzcService;
|
|
|
+import com.jeesite.modules.mes.service.MesProductRecordService;
|
|
|
+import com.jeesite.modules.mes.service.MesProductService;
|
|
|
+import com.jeesite.modules.mes.web.MesProductRecordController;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.context.annotation.Configuration;
|
|
|
+import org.springframework.scheduling.annotation.EnableScheduling;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 框架气密 S7 采集与质量握手(OP360)。
|
|
|
+ * <pre>
|
|
|
+ * DB200 点位:
|
|
|
+ * 0.0 质量校验通过反馈(MES 写 true)
|
|
|
+ * 2.0 框架码 String[30]
|
|
|
+ * 34.0 扫码反馈信号
|
|
|
+ * 36.0 液冷板充气时间 Real
|
|
|
+ * 40.0 液冷板测试时间 Real
|
|
|
+ * 44.0 液冷板充气压力 Real
|
|
|
+ * 48.0 液冷板稳定压力 Real
|
|
|
+ * 52.0 液冷板泄漏量 Real
|
|
|
+ * 56.0 框架充气时间 Real
|
|
|
+ * 60.0 框架测试时间 Real
|
|
|
+ * 64.0 框架充气压力 Real
|
|
|
+ * 68.0 框架稳定压力 Real
|
|
|
+ * 72.0 框架泄露量 Real
|
|
|
+ * 76.0 过程数据采集触发
|
|
|
+ * 78.0 液冷板保压时间 Real
|
|
|
+ * 82.0 框架保压时间 Real
|
|
|
+ * 86.0 液冷板判定结果 Bool
|
|
|
+ * 86.1 框架判定结果 Bool
|
|
|
+ *
|
|
|
+ * 配置(application.yml):
|
|
|
+ * mes.framework.qm.enabled 是否启用(默认 false)
|
|
|
+ * mes.framework.qm.plc.ip PLC IP,默认 192.168.1.100
|
|
|
+ * mes.framework.qm.oprno 工位号,默认 OP360
|
|
|
+ * mes.framework.qm.lineSn 产线编号,默认 XT
|
|
|
+ * </pre>
|
|
|
+ */
|
|
|
+@Configuration
|
|
|
+@EnableScheduling
|
|
|
+public class FrameworkQmScheduling {
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(FrameworkQmScheduling.class);
|
|
|
+
|
|
|
+ private static final String ADDR_QUALITY_PASS = "DB200.0.0";
|
|
|
+ private static final String ADDR_SN = "DB200.2.0";
|
|
|
+ private static final int SN_LENGTH = 30;
|
|
|
+ private static final String ADDR_SCAN_FEEDBACK = "DB200.34.0";
|
|
|
+
|
|
|
+ private static final String ADDR_YLB_INFLATION_TIME = "DB200.36.0";
|
|
|
+ private static final String ADDR_YLB_TEST_TIME = "DB200.40.0";
|
|
|
+ private static final String ADDR_YLB_INFLATION_PRESSURE = "DB200.44.0";
|
|
|
+ private static final String ADDR_YLB_STABLE_PRESSURE = "DB200.48.0";
|
|
|
+ private static final String ADDR_YLB_LEAK = "DB200.52.0";
|
|
|
+ private static final String ADDR_YLB_HOLD_TIME = "DB200.78.0";
|
|
|
+
|
|
|
+ private static final String ADDR_INFLATION_TIME = "DB200.56.0";
|
|
|
+ private static final String ADDR_TEST_TIME = "DB200.60.0";
|
|
|
+ private static final String ADDR_INFLATION_PRESSURE = "DB200.64.0";
|
|
|
+ private static final String ADDR_STABLE_PRESSURE = "DB200.68.0";
|
|
|
+ private static final String ADDR_LEAK_AMOUNT = "DB200.72.0";
|
|
|
+ private static final String ADDR_COLLECT_FLAG = "DB200.76.0";
|
|
|
+ private static final String ADDR_HOLD_TIME = "DB200.82.0";
|
|
|
+ private static final String ADDR_YLB_JUDGE_RESULT = "DB200.86.0";
|
|
|
+ private static final String ADDR_JUDGE_RESULT = "DB200.86.1";
|
|
|
+
|
|
|
+ private static final String DEFAULT_PLC_IP = "192.168.1.100";
|
|
|
+ private static final String DEFAULT_OPRNO = "OP360";
|
|
|
+ private static final String DEFAULT_LINE_SN = "XT";
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private MesProductRecordController mesProductRecordController;
|
|
|
+ @Autowired
|
|
|
+ private MesProductService mesProductService;
|
|
|
+ @Autowired
|
|
|
+ private MesLineService mesLineService;
|
|
|
+ @Autowired
|
|
|
+ private MesProductQmzcService mesProductQmzcService;
|
|
|
+ @Autowired
|
|
|
+ private MesProductRecordService mesProductRecordService;
|
|
|
+
|
|
|
+ private final AtomicBoolean running = new AtomicBoolean(false);
|
|
|
+ private volatile String lastCheckedSn;
|
|
|
+ private volatile String lastKnownSn;
|
|
|
+
|
|
|
+ @Scheduled(fixedRate = 1000)
|
|
|
+ public void collect() {
|
|
|
+ String enabled = Global.getConfig("mes.framework.qm.enabled");
|
|
|
+ if (StringUtils.isBlank(enabled) || !"true".equalsIgnoreCase(enabled.trim())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ runOnce();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 直接执行一轮(供测试调用)
|
|
|
+ */
|
|
|
+ public void runOnce() {
|
|
|
+ if (!running.compareAndSet(false, true)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String ip = getPlcIp();
|
|
|
+ S7PLC s7PLC = null;
|
|
|
+ try {
|
|
|
+ s7PLC = new S7PLC(EPlcType.S1200, ip);
|
|
|
+ handleScanFeedback(s7PLC);
|
|
|
+ handleCollectTrigger(s7PLC);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("框架气密S7任务失败[{}]:{}", ip, e.getMessage());
|
|
|
+ } finally {
|
|
|
+ if (s7PLC != null) {
|
|
|
+ try {
|
|
|
+ s7PLC.close();
|
|
|
+ } catch (Exception ignore) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+ running.set(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 扫码反馈为 true:读框架码 → 质量校验 → 通过则写 DB200.0.0=true
|
|
|
+ */
|
|
|
+ private void handleScanFeedback(S7PLC s7PLC) {
|
|
|
+ if (!s7PLC.readBoolean(ADDR_SCAN_FEEDBACK)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String sn = readWorkpieceCode(s7PLC);
|
|
|
+ if (StringUtils.isBlank(sn)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lastKnownSn = sn;
|
|
|
+ if (sn.equals(lastCheckedSn)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ boolean pass = doQualityCheck(sn);
|
|
|
+ if (pass) {
|
|
|
+ s7PLC.writeBoolean(ADDR_QUALITY_PASS, true);
|
|
|
+ logger.info("框架气密质量校验通过 sn={},已写 {}=true", sn, ADDR_QUALITY_PASS);
|
|
|
+ } else {
|
|
|
+ logger.warn("框架气密质量校验未通过 sn={}", sn);
|
|
|
+ }
|
|
|
+ lastCheckedSn = sn;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 采集触发为 true:读过程参数 → 落库 mes_product_qmzc → 提交 OP360 工位结果 → 清触发位
|
|
|
+ */
|
|
|
+ private void handleCollectTrigger(S7PLC s7PLC) {
|
|
|
+ if (!s7PLC.readBoolean(ADDR_COLLECT_FLAG)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String sn = readWorkpieceCode(s7PLC);
|
|
|
+ if (StringUtils.isBlank(sn)) {
|
|
|
+ sn = lastKnownSn;
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(sn)) {
|
|
|
+ logger.error("框架气密采集失败:无工件码,保持 {}=true 待重试", ADDR_COLLECT_FLAG);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lastKnownSn = sn;
|
|
|
+
|
|
|
+ float ylbInflationTime = s7PLC.readFloat32(ADDR_YLB_INFLATION_TIME);
|
|
|
+ float ylbTestTime = s7PLC.readFloat32(ADDR_YLB_TEST_TIME);
|
|
|
+ float ylbInflationPressure = s7PLC.readFloat32(ADDR_YLB_INFLATION_PRESSURE);
|
|
|
+ float ylbStablePressure = s7PLC.readFloat32(ADDR_YLB_STABLE_PRESSURE);
|
|
|
+ float ylbLeak = s7PLC.readFloat32(ADDR_YLB_LEAK);
|
|
|
+ float ylbHoldTime = s7PLC.readFloat32(ADDR_YLB_HOLD_TIME);
|
|
|
+
|
|
|
+ float inflationTime = s7PLC.readFloat32(ADDR_INFLATION_TIME);
|
|
|
+ float testTime = s7PLC.readFloat32(ADDR_TEST_TIME);
|
|
|
+ float inflationPressure = s7PLC.readFloat32(ADDR_INFLATION_PRESSURE);
|
|
|
+ float stablePressure = s7PLC.readFloat32(ADDR_STABLE_PRESSURE);
|
|
|
+ float leakAmount = s7PLC.readFloat32(ADDR_LEAK_AMOUNT);
|
|
|
+ float holdTime = s7PLC.readFloat32(ADDR_HOLD_TIME);
|
|
|
+ boolean ylbJudgePass = s7PLC.readBoolean(ADDR_YLB_JUDGE_RESULT);
|
|
|
+ boolean judgePass = s7PLC.readBoolean(ADDR_JUDGE_RESULT);
|
|
|
+ String ylbStationResult = ylbJudgePass ? "OK" : "NG";
|
|
|
+ String stationResult = judgePass ? "OK" : "NG";
|
|
|
+
|
|
|
+ logger.info("框架气密采集 sn={} 液冷板[充气时间={} 测试时间={} 充气压力={} 稳定压力={} 泄漏量={} 保压时间={} 判定={}] "
|
|
|
+ + "框架[充气时间={} 测试时间={} 充气压力={} 稳定压力={} 泄露量={} 保压时间={} 判定={}]",
|
|
|
+ sn, ylbInflationTime, ylbTestTime, ylbInflationPressure, ylbStablePressure, ylbLeak, ylbHoldTime, ylbStationResult,
|
|
|
+ inflationTime, testTime, inflationPressure, stablePressure, leakAmount, holdTime, stationResult);
|
|
|
+
|
|
|
+ if (!saveQmzcData(ylbInflationTime, ylbTestTime, ylbInflationPressure, ylbStablePressure, ylbLeak, ylbHoldTime,
|
|
|
+ inflationTime, testTime, inflationPressure, stablePressure, leakAmount, holdTime,
|
|
|
+ ylbStationResult, stationResult)) {
|
|
|
+ logger.error("框架气密数据保存失败,保持 {}=true 待重试 sn={}", ADDR_COLLECT_FLAG, sn);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!submitStationResult(sn, stationResult)) {
|
|
|
+ logger.error("框架气密工位结果提交失败,保持 {}=true 待重试 sn={}", ADDR_COLLECT_FLAG, sn);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ s7PLC.writeBoolean(ADDR_COLLECT_FLAG, false);
|
|
|
+ logger.info("框架气密采集完成 sn={} result={},已将 {} 置 false", sn, stationResult, ADDR_COLLECT_FLAG);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean saveQmzcData(float ylbInflationTime, float ylbTestTime, float ylbInflationPressure,
|
|
|
+ float ylbStablePressure, float ylbLeak, float ylbHoldTime,
|
|
|
+ float inflationTime, float testTime, float inflationPressure, float stablePressure,
|
|
|
+ float leakAmount, float holdTime, String ylbStationResult, String stationResult) {
|
|
|
+ try {
|
|
|
+ MesProductQmzc entity = new MesProductQmzc();
|
|
|
+ entity.setYlbCqsj(fmt(ylbInflationTime));
|
|
|
+ entity.setYlbCssj(fmt(ylbTestTime));
|
|
|
+ entity.setYlbCqyl(fmt(ylbInflationPressure));
|
|
|
+ entity.setYlbWdyl(fmt(ylbStablePressure));
|
|
|
+ entity.setYlbLeak(fmt(ylbLeak));
|
|
|
+ entity.setYlbBysj(fmt(ylbHoldTime));
|
|
|
+ entity.setYlbResult(ylbStationResult);
|
|
|
+ entity.setCqsj(fmt(inflationTime));
|
|
|
+ entity.setCssj(fmt(testTime));
|
|
|
+ entity.setCqya(fmt(inflationPressure));
|
|
|
+ entity.setWdyl(fmt(stablePressure));
|
|
|
+ entity.setLeak(fmt(leakAmount));
|
|
|
+ entity.setBysj(fmt(holdTime));
|
|
|
+ entity.setResult(stationResult);
|
|
|
+ mesProductQmzcService.save(entity);
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("框架气密数据保存异常:{}", e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean submitStationResult(String sn, String result) {
|
|
|
+ try {
|
|
|
+ String oprno = getOprno();
|
|
|
+ if (oprno.length() == 5) {
|
|
|
+ oprno = oprno + "A";
|
|
|
+ }
|
|
|
+ String lineSn = getLineSn(sn);
|
|
|
+ if (StringUtils.isBlank(lineSn)) {
|
|
|
+ logger.warn("框架气密工位结果提交失败:无法解析产线编号 sn={}", sn);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ List<ParamsResp> params = new ArrayList<>();
|
|
|
+ List<BindMaterialResp> bmlists = new ArrayList<>();
|
|
|
+ mesProductRecordService.updateRecordResult(sn, oprno, "100000", "", result,
|
|
|
+ "", params, lineSn, true, bmlists, null);
|
|
|
+ logger.info("框架气密工位结果已提交 sn={} oprno={} result={}", sn, oprno, result);
|
|
|
+ return true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("框架气密工位结果提交异常 sn={}:{}", sn, e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean doQualityCheck(String sn) {
|
|
|
+ String oprno = getOprno();
|
|
|
+ String oldOprno = oprno.length() == 5 ? oprno + "A" : oprno;
|
|
|
+ String lineSn = getLineSn(sn);
|
|
|
+ if (StringUtils.isBlank(lineSn)) {
|
|
|
+ logger.warn("框架气密质量校验失败:无法解析产线编号 sn={}", sn);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ String ret = mesProductRecordController.checkQualityCommon(oldOprno, sn, lineSn, "");
|
|
|
+ if (StringUtils.isBlank(ret) || ret.length() < 5) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return "UD".equals(ret.substring(3, 5));
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("框架气密质量校验异常 sn={}:{}", sn, e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getLineSn(String sn) {
|
|
|
+ String lineSn = Global.getConfig("mes.framework.qm.lineSn");
|
|
|
+ if (StringUtils.isNotBlank(lineSn)) {
|
|
|
+ return lineSn.trim();
|
|
|
+ }
|
|
|
+ MesProduct product = mesProductService.findBySn(sn);
|
|
|
+ if (ObjectUtils.isEmpty(product) || StringUtils.isBlank(product.getCate())) {
|
|
|
+ return DEFAULT_LINE_SN;
|
|
|
+ }
|
|
|
+ MesLine query = new MesLine();
|
|
|
+ query.setProductCate(product.getCate());
|
|
|
+ query.setStatus("0");
|
|
|
+ MesLine line = mesLineService.findInfo(query);
|
|
|
+ if (ObjectUtils.isEmpty(line) || StringUtils.isBlank(line.getSn())) {
|
|
|
+ return DEFAULT_LINE_SN;
|
|
|
+ }
|
|
|
+ return line.getSn();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String readWorkpieceCode(S7PLC s7PLC) {
|
|
|
+ String raw = s7PLC.readString(ADDR_SN, SN_LENGTH);
|
|
|
+ if (StringUtils.isBlank(raw)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ StringBuilder sb = new StringBuilder(raw.length());
|
|
|
+ for (int i = 0; i < raw.length(); i++) {
|
|
|
+ char c = raw.charAt(i);
|
|
|
+ if (c == 0 || c == ' ') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (c >= 32 && c < 127) {
|
|
|
+ sb.append(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sb.toString().trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getPlcIp() {
|
|
|
+ String ip = Global.getConfig("mes.framework.qm.plc.ip");
|
|
|
+ return StringUtils.isBlank(ip) ? DEFAULT_PLC_IP : ip.trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getOprno() {
|
|
|
+ String oprno = Global.getConfig("mes.framework.qm.oprno");
|
|
|
+ return StringUtils.isBlank(oprno) ? DEFAULT_OPRNO : oprno.trim();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String fmt(float val) {
|
|
|
+ return String.format("%.3f", val);
|
|
|
+ }
|
|
|
+}
|