package com.mes.component; import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType; import com.github.xingshuangs.iot.protocol.s7.service.S7PLC; import com.mes.ui.MesClient; import com.mes.util.SnSlot; import com.mes.util.TCPClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Timer; import java.util.TimerTask; /** * 扫码面板(每台 PLC 一个,92 发 1、2 号枪,93 发 3 号枪,由 PLC 决定) * DB7010.0.0 请求扫码 / DB7010.0.1 扫码完成反馈 / DB7010.6(INT) 扫码枪号;0.2、2、4 为预留,不读不写 * 枪号 0=未发;正常同一时间 92、93 只有一个非 0,PLC 收到扫码完成反馈后清零 * 请求扫码时 PLC 给几号就触发几号枪,读到 16 位码存入正面扫码槽,正面机台请求上料时取用 */ public class WorkJPanelScan extends JPanel { public static final Logger log = LoggerFactory.getLogger(WorkJPanelScan.class); public static final int SN_LENGTH = 16; public String oprno = "OP130"; public String plcIp; public String plcName; // 92 / 93,用于界面和日志 public String plcDb = "DB7010"; public String gunAddr = "DB7010.6"; public S7PLC s7PLC; public SnSlot slot; public Integer workWidth = 470; public Integer workHeight = 300; private boolean scanDone = false; // 本次请求已扫码成功,等 PLC 复位请求 private String lastGunState = ""; // 上次读到的枪号状态,变化时才记日志 public JButton status_menu; public JTextField productSnFeeding; // 扫码槽当前码 public JTextField gunField; // 本次扫码使用的枪 public JButton resetFeedingBtn; public JLabel feedinglabel; // 0.0 请求扫码 public JLabel feedingmeslabel; // 0.1 扫码完成反馈 public JLabel gunNoLabel; // DB7010.6 实时枪号 public ImageIcon imageGreen; public ImageIcon imageGray; public JButton signalBtn; public PlcSignalMonitor02 signalMonitor; public Timer oprnoTimer; public WorkJPanelScan(String plcIp) { this.setLayout(null); this.setBackground(Color.WHITE); this.plcIp = plcIp; this.plcName = plcIp.substring(plcIp.lastIndexOf('.') + 1); this.slot = MesClient.frontSlot; s7PLC = new S7PLC(EPlcType.S1500, plcIp); s7PLC.setConnectTimeout(1000); s7PLC.setReceiveTimeout(1000); initUI(); productSnFeeding.setText(slot.getSn()); setMenuStatus("等待请求扫码", 0); startOprnoTimer(); } private String tag() { return oprno + "扫码" + plcName; } private void initUI() { JLabel oprnoLabel = new JLabel(oprno); 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); JLabel nameLabel = new JLabel(plcName + "扫码"); nameLabel.setHorizontalAlignment(SwingConstants.CENTER); nameLabel.setForeground(Color.BLACK); nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); nameLabel.setBounds(0, 17, workWidth / 5, 40); this.add(nameLabel); 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); addRowLabel("扫码", 50); productSnFeeding = createField(50, workWidth * 3 / 5); productSnFeeding.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { String sn = productSnFeeding.getText().trim(); if (!sn.isEmpty()) { MesClient.createQRCode(sn, tag()); } } }); resetFeedingBtn = new JButton("清空"); resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, 22)); resetFeedingBtn.setBounds(workWidth * 4 / 5 + 10, 50, workWidth / 5 - 10, 40); resetFeedingBtn.addActionListener(e -> { String sn = slot.getSn(); if (sn.isEmpty()) { return; } if (confirm("确定清空扫码结果" + sn + "吗?\n请求扫码仍在时会重新扫码")) { resetScan(); } }); this.add(resetFeedingBtn); addRowLabel("扫码枪", 100); gunField = createField(100, workWidth * 4 / 5); gunField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); gunField.setText("未扫码"); imageGreen = scaledIcon("/bg/green_dot.png"); imageGray = scaledIcon("/bg/grey_dot.png"); feedinglabel = createLight("请求扫码", 0, 150); feedingmeslabel = createLight("扫码完成反馈", workWidth / 3, 150); feedingmeslabel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { boolean ret = readBoolean(plcDb + ".0.1"); if (confirm("确定修改" + plcName + "扫码完成反馈(" + plcDb + ".0.1)为" + !ret + "吗?")) { writeScanFinish(!ret); } } }); // 实时显示 PLC 给的枪号(DB7010.6),每轮刷新 gunNoLabel = new JLabel("PLC枪号:-"); gunNoLabel.setHorizontalAlignment(SwingConstants.LEFT); gunNoLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); gunNoLabel.setForeground(Color.DARK_GRAY); gunNoLabel.setBounds(workWidth * 2 / 3, 150, workWidth / 3, 30); this.add(gunNoLabel); JLabel ipLabel = new JLabel("PLC " + plcIp + " | " + plcDb + " | 枪号 " + gunAddr); ipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12)); ipLabel.setForeground(Color.DARK_GRAY); ipLabel.setBounds(5, 230, workWidth * 2 / 3 + 55, 26); this.add(ipLabel); signalMonitor = new PlcSignalMonitor02(s7PLC, plcDb, gunAddr, tag(), MesClient.mesClientFrame); signalBtn = signalMonitor.createSignalButton(); signalBtn.setBounds(workWidth * 2 / 3 + 63, 230, 90, 26); this.add(signalBtn); } private void addRowLabel(String text, int y) { JLabel label = new JLabel(text); label.setHorizontalAlignment(SwingConstants.CENTER); label.setForeground(Color.BLACK); label.setFont(new Font("微软雅黑", Font.PLAIN, 20)); label.setBounds(0, y, workWidth / 5, 40); this.add(label); } private JTextField createField(int y, int width) { JTextField field = new JTextField(""); field.setHorizontalAlignment(SwingConstants.CENTER); field.setForeground(Color.BLACK); field.setFont(new Font("微软雅黑", Font.PLAIN, 16)); field.setEditable(false); field.setBounds(workWidth / 5, y, width, 40); this.add(field); return field; } private JLabel createLight(String text, int x, int y) { JLabel label = new JLabel(text); label.setHorizontalAlignment(SwingConstants.LEFT); label.setIcon(imageGray); label.setForeground(Color.BLACK); label.setBounds(x, y, workWidth / 3, 30); this.add(label); return label; } private static ImageIcon scaledIcon(String path) { ImageIcon icon = new ImageIcon(MesClient.class.getResource(path)); icon.setImage(icon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH)); return icon; } private static boolean confirm(String msg) { return JOptionPane.showConfirmDialog(null, msg, "操作确认", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; } public void setMenuStatus(String msg, int error) { status_menu.setForeground(error == 0 ? Color.GREEN : Color.RED); status_menu.setText(msg); } // scan.ips 里配了这把枪即有效,PLC 发几号就用几号 private boolean isValidGun(int no) { return no >= 1 && no <= MesClient.scanClients.length; } // 定时任务 ----------------------------------------------------------------------------------------------------- public void startOprnoTimer() { if (oprnoTimer != null) { oprnoTimer.cancel(); } oprnoTimer = new Timer(); oprnoTimer.schedule(new TimerTask() { public void run() { try { if (MesClient.isLogin == 1) { cycle(); } } catch (Exception e) { log.error("<{}>轮询异常", tag(), e); } } }, 100, 1500); } private void cycle() { boolean retScan = readBoolean(plcDb + ".0.0"); boolean retFinish = readBoolean(plcDb + ".0.1"); feedinglabel.setIcon(retScan ? imageGreen : imageGray); feedingmeslabel.setIcon(retFinish ? imageGreen : imageGray); Integer plcGunNo = readGunNo(); showGunNo(plcGunNo); // 显示扫码槽当前码,被正面机台取走后变空 productSnFeeding.setText(slot.getSn()); if (!retScan) { scanDone = false; if (retFinish) { writeScanFinish(false); } return; } if (scanDone) { if (!retFinish) { writeScanFinish(true); } return; } doScan(plcGunNo); } // 刷新界面上的 PLC 枪号:0=未发(灰),1-3 绿色,其他值/读不到红色 private void showGunNo(Integer no) { if (no == null) { gunNoLabel.setForeground(Color.RED); gunNoLabel.setText("PLC枪号:读取失败"); gunNoLabel.setToolTipText(gunAddr + " 读取失败,不扫码"); return; } if (no == 0) { gunNoLabel.setForeground(Color.GRAY); gunNoLabel.setText("PLC枪号:0(未发)"); gunNoLabel.setToolTipText("本PLC未发枪号"); return; } boolean valid = isValidGun(no); gunNoLabel.setForeground(valid ? new Color(0, 128, 0) : Color.RED); gunNoLabel.setText("PLC枪号:" + no + (valid ? "" : "(无效)")); gunNoLabel.setToolTipText(valid ? no + "号枪 " + MesClient.scanClients[no - 1].serverIP : "有效范围 1-" + MesClient.scanClients.length); } private void doScan(Integer plcGunNo) { // 读不到枪号不扫码 if (plcGunNo == null) { gunField.setText("未读到枪号"); setMenuStatus("读取" + gunAddr + "失败,不扫码", -1); return; } int gunNo = plcGunNo; // 0 = PLC 还没发枪号,等下一轮 if (gunNo == 0) { setMenuStatus("请求扫码,等待PLC发枪号", 0); return; } if (!isValidGun(gunNo)) { gunField.setText("枪号 " + gunNo + " 无效"); setMenuStatus("扫码枪号无效:" + gunNo + "(" + gunAddr + " 应为 1-" + MesClient.scanClients.length + ")", -1); return; } TCPClient client = MesClient.scanClients[gunNo - 1]; gunField.setText(gunNo + "号枪 " + client.serverIP + ":" + client.serverPort); setMenuStatus(gunNo + "号枪扫码中...", 0); String sn = MesClient.scanQrcode(gunNo); if (sn.length() != SN_LENGTH) { String reason = sn.isEmpty() ? "未读到码" : "码长度" + sn.length() + "位"; setMenuStatus(gunNo + "号枪扫码失败(" + reason + "),重试中", -1); return; } slot.put(sn, gunNo + "号枪(" + plcName + ")"); productSnFeeding.setText(sn); writeScanFinish(true); scanDone = true; setMenuStatus("扫码成功", 0); } public void resetScan() { slot.clear("人工清空(" + plcName + ")"); productSnFeeding.setText(""); scanDone = false; writeScanFinish(false); setMenuStatus("扫码结果已清空,等待请求扫码", 0); } // 与设备交互 ----------------------------------------------------------------------------------------- // 读扫码枪号,读失败返回 null;每轮都读,只在值变化时记日志 private Integer readGunNo() { Integer no = null; String err = ""; try { no = (int) s7PLC.readInt16(gunAddr); } catch (Exception e) { err = e.getMessage(); } String state = no == null ? "ERR" : String.valueOf(no); if (!state.equals(lastGunState)) { lastGunState = state; if (no == null) { log.warn("<{}>读取{}-->失败,不扫码:{}", tag(), gunAddr, err); } else { log.info("<{}>读取{}-->枪号变为:{}", tag(), gunAddr, no); } } return no; } public void writeScanFinish(boolean b) { writeBoolean(plcDb + ".0.1", b); } public boolean readBoolean(String addr) { try { return s7PLC.readBoolean(addr); } catch (Exception e) { log.error("<{}>读取{}-->失败:{}", tag(), addr, e.getMessage()); return false; } } public boolean writeBoolean(String addr, boolean value) { try { s7PLC.writeBoolean(addr, value); log.info("<{}>写入{}-->成功,写入内容:{}", tag(), addr, value); return true; } catch (Exception e) { log.error("<{}>写入{}-->失败,写入内容:{},{}", tag(), addr, value, e.getMessage()); return false; } } }