| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- 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.MesProductRecord;
- import com.jeesite.modules.mes.entity.MesProductTj;
- 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.MesProductRecordService;
- import com.jeesite.modules.mes.service.MesProductService;
- import com.jeesite.modules.mes.service.MesProductTjService;
- import com.jeesite.modules.mes.util.CommonUitl;
- 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 采集与质量握手(OP220,固定工位号)。
- * <pre>
- * DB1300 点位:
- * 0.0 框架码 String[30]
- * 64.0 扫码反馈信号
- * 64.2 质量校验通过反馈(校验通过 MES 写 true,工位完成后 MES 写 false)
- * 64.4 涂胶数据采集触发
- * 64.5 整体完成信号(true 时提交工位 OK 并回查确认落库,成功后将 64.2 置 false)
- * 66.0 涂胶序号 Word
- * 68.0 涂胶速度 Real
- * 72.0 涂胶长度 Word
- *
- * 配置(application.yml):
- * mes.tj.enabled 是否启用(默认 false)
- * mes.tj.plc.ip PLC IP,默认 10.0.40.100
- * mes.tj.lineSn 产线编号,默认 XT
- * </pre>
- */
- @Configuration
- @EnableScheduling
- public class ProductTjScheduling {
- private static final Logger logger = LoggerFactory.getLogger(ProductTjScheduling.class);
- private static final String ADDR_SN = "DB1300.0.0";
- private static final int SN_LENGTH = 30;
- private static final String ADDR_SCAN_FEEDBACK = "DB1300.64.0";
- private static final String ADDR_QUALITY_PASS = "DB1300.64.2";
- private static final String ADDR_GLUE_COLLECT = "DB1300.64.4";
- private static final String ADDR_COMPLETE = "DB1300.64.5";
- private static final String ADDR_GLUE_SEQ = "DB1300.66.0";
- private static final String ADDR_GLUE_SPEED = "DB1300.68.0";
- private static final String ADDR_GLUE_LENGTH = "DB1300.72.0";
- private static final String DEFAULT_PLC_IP = "10.0.40.100";
- private static final String OPRNO = "OP220";
- private static final String DEFAULT_LINE_SN = "XT";
- @Autowired
- private MesProductRecordController mesProductRecordController;
- @Autowired
- private MesProductService mesProductService;
- @Autowired
- private MesLineService mesLineService;
- @Autowired
- private MesProductTjService mesProductTjService;
- @Autowired
- private MesProductRecordService mesProductRecordService;
- private final AtomicBoolean running = new AtomicBoolean(false);
- private volatile String lastKnownSn;
- private volatile String lastCompletedSn;
- @Scheduled(fixedRate = 100)
- public void collect() {
- String enabled = Global.getConfig("mes.tj.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);
- handleGlueCollect(s7PLC);
- handleComplete(s7PLC);
- } catch (Exception e) {
- logger.error("涂胶S7任务失败[{}]:{}", ip, e.getMessage());
- } finally {
- if (s7PLC != null) {
- try {
- s7PLC.close();
- } catch (Exception ignore) {
- }
- }
- running.set(false);
- }
- }
- /**
- * 扫码反馈为 true:读框架码 → 质量校验 → 通过则写 DB1300.64.2=true
- */
- private void handleScanFeedback(S7PLC s7PLC) {
- if (!s7PLC.readBoolean(ADDR_SCAN_FEEDBACK)) {
- lastCompletedSn = null;
- return;
- }
- String sn = readWorkpieceCode(s7PLC);
- if (StringUtils.isBlank(sn)) {
- return;
- }
- lastKnownSn = sn;
- 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);
- }
- }
- /**
- * 涂胶采集触发为 true:读涂胶参数 → 落库 mes_product_tj → 清触发位
- */
- private void handleGlueCollect(S7PLC s7PLC) {
- if (!s7PLC.readBoolean(ADDR_GLUE_COLLECT)) {
- return;
- }
- String sn = readWorkpieceCode(s7PLC);
- if (StringUtils.isBlank(sn)) {
- sn = lastKnownSn;
- }
- if (StringUtils.isBlank(sn)) {
- logger.error("涂胶数据采集失败:无工件码,保持 {}=true 待重试", ADDR_GLUE_COLLECT);
- return;
- }
- lastKnownSn = sn;
- int glueSeq = s7PLC.readUInt16(ADDR_GLUE_SEQ);
- float glueSpeed = s7PLC.readFloat32(ADDR_GLUE_SPEED);
- int glueLength = s7PLC.readUInt16(ADDR_GLUE_LENGTH);
- logger.info("涂胶数据采集 sn={} 序号={} 速度={} 长度={}", sn, glueSeq, glueSpeed, glueLength);
- if (!saveTjData(sn, glueSeq, glueSpeed, glueLength)) {
- logger.error("涂胶数据保存失败,保持 {}=true 待重试 sn={}", ADDR_GLUE_COLLECT, sn);
- return;
- }
- s7PLC.writeBoolean(ADDR_GLUE_COLLECT, false);
- logger.info("涂胶数据采集完成 sn={} 序号={},已将 {} 置 false", sn, glueSeq, ADDR_GLUE_COLLECT);
- }
- /**
- * DB1300.64.5 整体完成信号为 true:提交工位 OK → 成功后将 DB1300.64.2 置 false
- */
- private void handleComplete(S7PLC s7PLC) {
- if (!s7PLC.readBoolean(ADDR_COMPLETE)) {
- return;
- }
- String sn = readWorkpieceCode(s7PLC);
- if (StringUtils.isBlank(sn)) {
- sn = lastKnownSn;
- }
- if (StringUtils.isBlank(sn)) {
- logger.error("涂胶工位完成提交失败:无工件码,保持 {}=true 待重试", ADDR_COMPLETE);
- return;
- }
- if (sn.equals(lastCompletedSn)) {
- return;
- }
- if (!submitStationResult(sn, "OK")) {
- logger.error("涂胶工位结果提交失败,保持信号待重试 sn={}", sn);
- return;
- }
- s7PLC.writeBoolean(ADDR_QUALITY_PASS, false);
- lastCompletedSn = sn;
- logger.info("涂胶工位完成 sn={},已提交 OK 并将 {} 置 false", sn, ADDR_QUALITY_PASS);
- }
- private boolean saveTjData(String sn, int glueSeq, float glueSpeed, int glueLength) {
- try {
- MesProductTj entity = new MesProductTj();
- entity.setSn(sn);
- entity.setOprno(OPRNO);
- entity.setTjData(String.valueOf(glueSeq));
- entity.setTjSpeed(fmt(glueSpeed));
- entity.setTjLength(String.valueOf(glueLength));
- mesProductTjService.save(entity);
- return true;
- } catch (Exception e) {
- logger.error("涂胶数据保存异常 sn={}:{}", sn, e.getMessage());
- return false;
- }
- }
- /**
- * 直接提交工位结果(供测试 / 手工触发),不读写 PLC 信号。
- * 逻辑与 64.5 完成时的 submitStationResult 相同(含回查与强制落库)。
- *
- * @param sn 工件码
- * @param result 结果,通常为 OK
- * @return true=craft=100000 已确认为目标结果
- */
- public boolean submitStationResultNow(String sn, String result) {
- if (StringUtils.isBlank(sn)) {
- logger.error("涂胶工位结果提交跳过:工件码为空");
- return false;
- }
- return submitStationResult(sn.trim(), StringUtils.isBlank(result) ? "OK" : result.trim());
- }
- /**
- * 提交工位结果为 OK,并回查 craft=100000 确认已落库。
- * updateRecordResult 在单部件工位或缺少总结果行时可能静默跳过,此时改走 updateRecord 强制更新。
- */
- private boolean submitStationResult(String sn, String result) {
- String oldOprno = OPRNO + "A";
- String lineSn = getLineSn(sn);
- if (StringUtils.isBlank(lineSn)) {
- logger.warn("涂胶工位结果提交失败:无法解析产线编号 sn={}", sn);
- return false;
- }
- try {
- List<ParamsResp> params = new ArrayList<>();
- List<BindMaterialResp> bmlists = new ArrayList<>();
- mesProductRecordService.updateRecordResult(sn, oldOprno, "100000", "", result,
- "", params, lineSn, true, bmlists, null);
- if (isStationMainResult(sn, lineSn, result)) {
- logger.info("涂胶工位结果已提交 sn={} oprno={} result={}", sn, oldOprno, result);
- return true;
- }
- logger.warn("updateRecordResult 未真正更新 craft=100000,改为 updateRecord 强制提交 sn={} oprno={}",
- sn, oldOprno);
- mesProductRecordService.updateRecord(sn, oldOprno, "100000", "", result, "", lineSn);
- if (!isStationMainResult(sn, lineSn, result)) {
- logger.error("涂胶工位结果强制提交后仍未落库 sn={} oprno={} lineSn={}", sn, oldOprno, lineSn);
- return false;
- }
- logger.info("涂胶工位结果已强制提交 sn={} oprno={} result={}", sn, oldOprno, result);
- return true;
- } catch (Exception e) {
- logger.error("涂胶工位结果提交异常 sn={}:{}", sn, e.getMessage());
- return false;
- }
- }
- /** 回查工位总结果行 craft=100000 是否已为指定结果 */
- private boolean isStationMainResult(String sn, String lineSn, String expectContent) {
- String fOprno = CommonUitl.formatOprno(OPRNO + "A");
- MesProductRecord query = new MesProductRecord();
- query.setSn(sn);
- query.setOprno(fOprno);
- query.setCraft("100000");
- query.setLineSn(lineSn);
- List<MesProductRecord> list = mesProductRecordService.findList(query);
- if (list == null || list.isEmpty()) {
- return false;
- }
- return expectContent.equals(list.get(0).getContent());
- }
- private boolean doQualityCheck(String sn) {
- String oldOprno = OPRNO + "A";
- 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.tj.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.tj.plc.ip");
- return StringUtils.isBlank(ip) ? DEFAULT_PLC_IP : ip.trim();
- }
- private static String fmt(float val) {
- return String.format("%.3f", val);
- }
- }
|