package com.mes.component; import com.alibaba.fastjson2.JSONObject; import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; import com.mes.ui.DataUtil; import com.mes.ui.MesClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Timer; import java.util.TimerTask; public class WorkJPanelOP40Transfer extends JPanel { public static final Logger log = LoggerFactory.getLogger(WorkJPanelOP40Transfer.class); public String oprno = "OP040"; public String pos = "A"; public String lastSn = ""; // 最近的码 public String lineSn = "XT"; public JLabel feedinglabel; // 请求扫码信号 public JLabel feedingmeslabel; // MES扫码反馈 public JLabel feedingtotransferlabel; // 去中转台信号 public JTextField productSnFeeding; // 工件码显示 public JButton status_menu; // 状态显示 public JButton resetFeedingBtn; // 刷新按钮 public JButton signalBtn; // 信号监控按钮 public PlcSignalMonitor signalMonitor; // 信号监控组件 public ImageIcon imageGreen; public ImageIcon imageGray; public String plcIp1 = "192.168.90.83"; // PLC3 西侧 public String plcIp2 = "192.168.90.84"; // PLC4 东侧 // 只连接一个PLC public S7PLC s7PLC = null; public Integer workWidth = 470; public Integer workHeight = 300; public String plcDb = "DB7005"; // 共用工位6的DB块 public Integer tjFeedingFlag = 0; // 上料状态标识: 0=等待请求, 1=已获取工件码等待清空 public Boolean toTransfer = false; // 去中转台信号(用于触发清空) public int scanRetryCount = 0; // 扫码重试计数器,最多重试1次(共2次扫码机会) public static final int MAX_SCAN_RETRY = 1; // 最大重试次数 public Boolean plcHeart = false; // 用于检测状态变化的上一次值(减少日志输出) private java.util.Map lastReadValues = new java.util.HashMap<>(); private java.util.Map lastWriteValues = new java.util.HashMap<>(); public Integer isLocal = 0; // 0=正式 1=本地 public WorkJPanelOP40Transfer(String position) { this.setLayout(null); this.setBackground(Color.WHITE); this.pos = position; // 根据位置选择连接的PLC,DB块都是DB7005(共用工位6的DB块) if (pos.equals("A")) { // 中转车1 连接 PLC3(西侧) s7PLC = new S7PLC(EPlcType.S1500, plcIp1); } else if (pos.equals("B")) { // 中转车2 连接 PLC4(东侧) s7PLC = new S7PLC(EPlcType.S1500, plcIp2); } JLabel oprnoLabel = new JLabel("中转车" + (pos.equals("A") ? "1" : "2")); oprnoLabel.setHorizontalAlignment(SwingConstants.CENTER); oprnoLabel.setForeground(Color.BLACK); oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20)); oprnoLabel.setBounds(0, 0, workWidth / 5, 40); this.add(oprnoLabel); status_menu = new JButton("等待请求"); status_menu.setBounds(workWidth / 5, 0, workWidth * 4 / 5, 40); status_menu.setForeground(Color.GREEN); status_menu.setFont(new Font("微软雅黑", Font.PLAIN, 16)); status_menu.setBackground(Color.BLACK); this.add(status_menu); JLabel oprnoLabelFeeding = new JLabel("上料"); oprnoLabelFeeding.setHorizontalAlignment(SwingConstants.CENTER); oprnoLabelFeeding.setForeground(Color.BLACK); oprnoLabelFeeding.setFont(new Font("微软雅黑", Font.PLAIN, 20)); oprnoLabelFeeding.setBounds(0, 50, workWidth / 5, 40); this.add(oprnoLabelFeeding); productSnFeeding = new JTextField(""); productSnFeeding.setHorizontalAlignment(SwingConstants.CENTER); productSnFeeding.setForeground(Color.BLACK); productSnFeeding.setFont(new Font("微软雅黑", Font.PLAIN, 16)); productSnFeeding.setEditable(false); productSnFeeding.setBounds(workWidth / 5, 50, workWidth * 3 / 5, 40); productSnFeeding.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { String sn = productSnFeeding.getText().trim(); if (!sn.isEmpty()) { MesClient.createQRCode(productSnFeeding.getText(), oprno + pos); } } }); this.add(productSnFeeding); resetFeedingBtn = new JButton("刷新"); resetFeedingBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int choice = JOptionPane.showConfirmDialog(null, "确定刷新工件吗?", "操作确认", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.YES_OPTION) { resetFeeding(); } } }); resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, 22)); resetFeedingBtn.setBounds(workWidth * 4 / 5 + 10, 50, workWidth / 5 - 10, 40); this.add(resetFeedingBtn); imageGreen = new ImageIcon(MesClient.class.getResource("/bg/green_dot.png")); imageGreen.setImage(imageGreen.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH)); imageGray = new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png")); imageGray.setImage(imageGray.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH)); feedinglabel = new JLabel("请求扫码"); feedinglabel.setHorizontalAlignment(SwingConstants.LEFT); feedinglabel.setIcon(imageGreen); feedinglabel.setForeground(Color.BLACK); feedinglabel.setBounds(0, 150, workWidth / 3, 30); this.add(feedinglabel); feedingmeslabel = new JLabel("MES扫码反馈"); feedingmeslabel.setHorizontalAlignment(SwingConstants.LEFT); feedingmeslabel.setIcon(imageGreen); feedingmeslabel.setForeground(Color.BLACK); feedingmeslabel.setBounds(workWidth / 3, 150, workWidth / 3, 30); feedingmeslabel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { boolean ret = readAllowFeeding(); int choice = JOptionPane.showConfirmDialog(null, "确定修改MES扫码反馈结果为" + (ret ? "false" : "true") + "吗?", "操作确认", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.YES_OPTION) { writeAllowFeeding(!ret); } } }); this.add(feedingmeslabel); feedingtotransferlabel = new JLabel("去中转台"); feedingtotransferlabel.setHorizontalAlignment(SwingConstants.LEFT); feedingtotransferlabel.setIcon(imageGray); feedingtotransferlabel.setForeground(Color.BLACK); feedingtotransferlabel.setBounds(workWidth * 2 / 3, 150, workWidth / 3, 30); this.add(feedingtotransferlabel); // 信号监控按钮 String stationName = "中转车" + (pos.equals("A") ? "1(西侧)" : "2(东侧)"); signalMonitor = new PlcSignalMonitor(s7PLC, plcDb, stationName, MesClient.mesClientFrame); signalBtn = signalMonitor.createSignalButton(); signalBtn.setBounds(4, 190, 90, 26); this.add(signalBtn); startOprnoTimer(); heartData(); resetScanA(); } public void setMenuStatus(String msg, int error) { if (error == 0) { this.status_menu.setForeground(Color.GREEN); } else { this.status_menu.setForeground(Color.RED); } this.status_menu.setText(msg); } // 定时任务 ----------------------------------------------------------------------------------------------------- public Timer cjTimerText2; public void heartData() { // 中转车不写心跳,由工位F/L (DB7005) 统一写入 // 避免同一个地址被多个组件写入导致冲突 // // 原代码(已禁用): // if (cjTimerText2 != null) { // cjTimerText2.cancel(); // } // cjTimerText2 = new Timer(); // cjTimerText2.schedule(new TimerTask() { // public void run() { // if (MesClient.isLogin == 1 && isLocal == 0) { // plcHeart = !plcHeart; // writeBoolean(plcDb + ".0.6", plcHeart); // } // } // }, 1000, 1000); } public Timer oprnoTimer; public void startOprnoTimer() { //心跳显示图标 if (oprnoTimer != null) { oprnoTimer.cancel(); } oprnoTimer = new Timer(); oprnoTimer.schedule(new TimerTask() { public void run() { try { if (MesClient.isLogin == 1) { // 读取PLC信号并更新UI Boolean retFeeding = readFeeding(); feedinglabel.setIcon(retFeeding ? imageGreen : imageGray); Boolean retFeedingAllow = readAllowFeeding(); feedingmeslabel.setIcon(retFeedingAllow ? imageGreen : imageGray); toTransfer = readFeedingToTransfer(); feedingtotransferlabel.setIcon(toTransfer ? imageGreen : imageGray); // 请求信号消失时,清除MES反馈 if (!retFeeding && retFeedingAllow) { writeAllowFeeding(false); } // 状态机逻辑(简化版) // 扫码和saveSn由工位6(F/L)的barCodeForTransfer()方法处理 // 中转车面板只负责显示和重置 if (tjFeedingFlag == 1) { // 状态1:已获取工件码,等待toTransfer信号变为false后清空 if (!toTransfer) { log.info("[" + oprno + pos + "]toTransfer变为false,清空工件码"); resetFeeding(); } } } } catch (Exception e) { log.error(e.getMessage()); } } }, 100, 3000); } public void barCode() { if (!productSnFeeding.getText().isEmpty()) { return; } String sn = readQrcode(); if (sn.equals("")) { if (productSnFeeding.getText().isEmpty()) { productSnFeeding.setText("空结果"); this.setMenuStatus("获取工件码为空", -1); } } else { productSnFeeding.setText(sn); lastSn = sn; // 查询工件质量 JSONObject retObj = DataUtil.checkQuality(sn, oprno + pos, MesClient.user_menu.getText()); if (retObj.get("result") != null && retObj.get("result").toString().equalsIgnoreCase("true")) { tjFeedingFlag = 1;//可加工 setMenuStatus("工件可以加工", 0); writeAllowFeeding(true); resetFeedingBtn.setEnabled(false); } else { writeAllowFeeding(false); resetFeedingBtn.setEnabled(true); if (retObj.get("result") == null) { setMenuStatus("请求失败,请重试", -1); } else { if (retObj.get("result").toString().equalsIgnoreCase("false")) { setMenuStatus(retObj.getString("message"), -1); } } } } } public void resetFeeding() { productSnFeeding.setText(""); tjFeedingFlag = 0; scanRetryCount = 0; // 重置重试计数器 setMenuStatus("等待请求", 0); resetFeedingBtn.setEnabled(true); writeAllowFeeding(false); } public void resetScanA() { tjFeedingFlag = 0; scanRetryCount = 0; // 重置重试计数器 productSnFeeding.setText(""); setMenuStatus("等待请求", 0); resetFeedingBtn.setEnabled(true); // 延迟写入PLC,避免初始化时连接超时 if(MesClient.isLogin == 1) { writeAllowFeeding(false); } } // 与设备交互----------------------------------------------------------------------------------------- // 请求扫码信号 (DB7005.0.0) public boolean readFeeding() { if (isLocal == 1) { return false; } else { return readBoolean(plcDb + ".0.0"); } } // 读取去中转台信号 (DB7005.1.0) public boolean readFeedingToTransfer() { if (isLocal == 1) { return false; } else { return readBoolean(plcDb + ".1.0"); } } // 读取MES允许上料信号 (DB7005.0.4) public boolean readAllowFeeding() { if (isLocal == 1) { return false; } else { return readBoolean(plcDb + ".0.4"); } } // 写入MES允许上料信号 (DB7005.0.4) public boolean writeAllowFeeding(Boolean b) { if (isLocal == 1) { return true; } else { return writeBoolean(plcDb + ".0.4", b); } } // 读取中转车号 (DB7005.2.0) public int readTransferNo() { if (isLocal == 1) { return 1; } else { return s7PLC.readInt16(plcDb + ".2.0"); } } // 读取中转车层数 (DB7005.4.0) public int readTransferFloor() { if (isLocal == 1) { return 1; } else { return s7PLC.readInt16(plcDb + ".4.0"); } } // 中转车不扫码,直接从数据库获取工件码 public String readQrcode() { String sn = ""; if (isLocal == 1) { return ""; } else { try { // if(this.pos.equals("A")){ // sn = MesClient.scanQrcode1(); // }else{ // sn = MesClient.scanQrcode2(); // } if(pos.equals("A")){ log.info("[{}]触发西侧摄像头扫码", oprno+pos); // sn = MesClient.scanQrcode1(); sn = MesClient.scanSnA; }else{ log.info("[{}]触发东侧摄像头扫码", oprno+pos); sn = MesClient.scanSnB; // sn = MesClient.scanQrcode2(); } } catch (Exception e) { log.error("[" + oprno + pos + "]获取工件码异常: " + e.getMessage(), e); } } return sn; } // 读取布尔值(只在值变化时打印日志) public boolean readBoolean(String db) { boolean permit; try { // 检查PLC连接是否可用 if(s7PLC == null) { return false; } permit = s7PLC.readBoolean(db); // 只在值变化时打印日志 Boolean lastValue = lastReadValues.get(db); if (lastValue == null || lastValue != permit) { log.info("<" + this.oprno + this.pos + ">读取" + db + "-->成功,结果:{}", permit ? "true" : "false"); lastReadValues.put(db, permit); } } catch (Exception e) { // 连接超时时不打印完整堆栈 if(e.getMessage() != null && e.getMessage().contains("connect timed out")) { // 连接超时是常见情况,降低日志级别 } else { log.error("<" + this.oprno + this.pos + ">读取" + db + "-->失败: {}", e.getMessage()); } return false; } return permit; } // 写入布尔值(只在值变化时打印日志) public boolean writeBoolean(String db, boolean permit) { try { // 检查PLC连接是否可用 if(s7PLC == null) { log.warn("<" + this.oprno + this.pos + ">PLC未初始化,跳过写入" + db); return false; } // 只在值变化时打印日志 Boolean lastValue = lastWriteValues.get(db); if (lastValue == null || lastValue != permit) { s7PLC.writeBoolean(db, permit); log.info("<" + this.oprno + this.pos + ">写入" + db + "-->成功,写入内容:{}", permit ? "true" : "false"); lastWriteValues.put(db, permit); } else { // 值未变化,仍然执行写入但不打印日志 s7PLC.writeBoolean(db, permit); } } catch (Exception e) { // 连接超时时不打印完整堆栈 if(e.getMessage() != null && e.getMessage().contains("connect timed out")) { log.warn("<" + this.oprno + this.pos + ">写入" + db + "-->连接超时(PLC未连接)"); } else { log.error("<" + this.oprno + this.pos + ">写入" + db + "-->失败,写入内容:{}", permit ? "true" : "false"); log.error(e.getMessage()); } return false; } return true; } }