Explorar el Código

扫码传值PLC

wangxichen hace 20 horas
padre
commit
f00b84d84e
Se han modificado 2 ficheros con 127 adiciones y 10 borrados
  1. 125 9
      src/com/mes/component/WorkJPanelScan.java
  2. 2 1
      src/com/mes/ui/MesClient.java

+ 125 - 9
src/com/mes/component/WorkJPanelScan.java

@@ -12,6 +12,7 @@ import javax.swing.*;
 import java.awt.*;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.nio.charset.StandardCharsets;
 import java.util.Timer;
 import java.util.TimerTask;
 
@@ -21,6 +22,7 @@ import java.util.TimerTask;
  * 枪号 0=未发;正常同一时间 92、93 只有一个非 0,PLC 收到扫码完成反馈后清零
  * 请求扫码时 PLC 给几号就触发几号枪,读到码(不校验长度)存入本 PLC 的正面扫码码
  * 92 扫的码给 OP130A-E 取,93 扫的码给 OP130F(93 DB7004)取
+ * 推码给 PLC(pushAddrs 按枪号对应 S7String 地址):扫到码后留存 15s 写入,到期清空,独立定时器,失败只记日志
  */
 public class WorkJPanelScan extends JPanel {
 
@@ -65,16 +67,45 @@ public class WorkJPanelScan extends JPanel {
 
     public Timer oprnoTimer;
 
+    // 推码给 PLC:S7String 布局 [最大长度][实际长度][字符...]
+    public static final int PUSH_SN_LENGTH = 24;
+    public static final long PUSH_HOLD_MS = 15000;
+    public PushPort[] pushPorts;
+    public Timer pushTimer;
+
+    // 一个推码地址的状态,holdSn/holdUntil 由扫码线程写,其余仅推码定时器线程使用
+    public static class PushPort {
+        public final int gunNo;
+        public final String addr;    // 起始地址,如 DB7020.1034
+        public final String lenAddr; // 实际长度字节,起始地址+1
+        public volatile String holdSn = "";
+        public volatile long holdUntil = 0;
+        public String writtenSn = null; // 初始 null,登录后首次运行必定清零一次,清掉上次残留
+        public JTextField echoField;
+
+        PushPort(int gunNo, String addr) {
+            this.gunNo = gunNo;
+            this.addr = addr;
+            int dot = addr.lastIndexOf('.');
+            this.lenAddr = addr.substring(0, dot + 1) + (Integer.parseInt(addr.substring(dot + 1)) + 1);
+        }
+    }
+
     /**
-     * @param plcIp PLC 地址
-     * @param slot  扫到的码存入的码槽(92 给 92 正面机,93 给 93 DB7004 正面机)
+     * @param plcIp     PLC 地址
+     * @param slot      扫到的码存入的码槽(92 给 92 正面机,93 给 93 DB7004 正面机)
+     * @param pushAddrs 推码地址,第 i 个对应 i+1 号枪;不传则不推码
      */
-    public WorkJPanelScan(String plcIp, SnSlot slot) {
+    public WorkJPanelScan(String plcIp, SnSlot slot, String... pushAddrs) {
         this.setLayout(null);
         this.setBackground(Color.WHITE);
         this.plcIp = plcIp;
         this.plcName = plcIp.substring(plcIp.lastIndexOf('.') + 1);
         this.slot = slot;
+        this.pushPorts = new PushPort[pushAddrs.length];
+        for (int i = 0; i < pushAddrs.length; i++) {
+            pushPorts[i] = new PushPort(i + 1, pushAddrs[i]);
+        }
 
         s7PLC = new S7PLC(EPlcType.S1500, plcIp);
         s7PLC.setConnectTimeout(1000);
@@ -84,6 +115,9 @@ public class WorkJPanelScan extends JPanel {
         productSnFeeding.setText(slot.getSn());
         setMenuStatus("等待请求扫码", 0);
         startOprnoTimer();
+        if (pushPorts.length > 0) {
+            startPushTimer();
+        }
     }
 
     private String tag() {
@@ -188,15 +222,40 @@ public class WorkJPanelScan extends JPanel {
 
         setManualMode(MesClient.manualScanEnabled);
 
+        // 回显行:每个推码地址一个框,显示从 PLC 读回的值
+        int footerY = 230;
+        if (pushPorts.length > 0) {
+            JLabel echoLabel = new JLabel("回显");
+            echoLabel.setHorizontalAlignment(SwingConstants.CENTER);
+            echoLabel.setForeground(Color.BLACK);
+            echoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
+            echoLabel.setBounds(0, 230, workWidth / 5, 32);
+            this.add(echoLabel);
+
+            int w = (workWidth * 4 / 5 - 5 * (pushPorts.length - 1)) / pushPorts.length;
+            for (int i = 0; i < pushPorts.length; i++) {
+                PushPort p = pushPorts[i];
+                p.echoField = new JTextField("");
+                p.echoField.setHorizontalAlignment(SwingConstants.CENTER);
+                p.echoField.setForeground(Color.BLACK);
+                p.echoField.setFont(new Font("微软雅黑", Font.PLAIN, 12));
+                p.echoField.setEditable(false);
+                p.echoField.setToolTipText(p.gunNo + "号枪 → " + p.addr);
+                p.echoField.setBounds(workWidth / 5 + i * (w + 5), 230, w, 32);
+                this.add(p.echoField);
+            }
+            footerY = 268;
+        }
+
         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);
+        ipLabel.setBounds(5, footerY, 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);
+        signalBtn.setBounds(workWidth * 2 / 3 + 63, footerY, 90, 26);
         this.add(signalBtn);
     }
 
@@ -299,6 +358,61 @@ public class WorkJPanelScan extends JPanel {
         }, 100, 1500);
     }
 
+    // 推码定时器(独立于扫码主流程):留存期内写码,到期清空,并读回显
+    public void startPushTimer() {
+        if (pushTimer != null) {
+            pushTimer.cancel();
+        }
+        pushTimer = new Timer();
+        pushTimer.schedule(new TimerTask() {
+            public void run() {
+                if (MesClient.isLogin != 1) {
+                    return;
+                }
+                for (PushPort p : pushPorts) {
+                    pushCycle(p);
+                }
+            }
+        }, 1000, 500);
+    }
+
+    private void pushCycle(PushPort p) {
+        try {
+            String target = System.currentTimeMillis() <= p.holdUntil ? p.holdSn : "";
+            if (!target.equals(p.writtenSn)) {
+                if (target.isEmpty()) {
+                    // writeString 不支持空串:从实际长度字节起连同字符一起写 0,最大长度字节不动
+                    s7PLC.writeByte(p.lenAddr, new byte[1 + PUSH_SN_LENGTH]);
+                } else {
+                    s7PLC.writeString(p.addr, target);
+                }
+                p.writtenSn = target;
+                log.info("<{}>推码{}-->成功,写入内容:{}", tag(), p.addr, target.isEmpty() ? "清空" : target);
+            }
+            p.echoField.setText(readPushSn(p));
+        } catch (Exception e) {
+            p.echoField.setText("通讯失败");
+            log.error("<{}>推码{}-->失败:{}", tag(), p.addr, e.getMessage());
+        }
+    }
+
+    // 按原始字节读 S7String,实际长度为 0 时返回空串(不用 readString)
+    private String readPushSn(PushPort p) {
+        byte[] bs = s7PLC.readByte(p.addr, 2 + PUSH_SN_LENGTH);
+        int len = Math.min(bs[1] & 0xFF, PUSH_SN_LENGTH);
+        return new String(bs, 2, len, StandardCharsets.US_ASCII).trim();
+    }
+
+    // 留存扫到的码,只改内存不碰 PLC;没有对应推码地址的枪号不推
+    private void holdPushSn(int gunNo, String sn) {
+        if (gunNo < 1 || gunNo > pushPorts.length) {
+            return;
+        }
+        PushPort p = pushPorts[gunNo - 1];
+        p.holdSn = sn; // 先写码再写截止时间,定时器不会读到新时间配旧码
+        p.holdUntil = System.currentTimeMillis() + PUSH_HOLD_MS;
+    }
+
     private void cycle() {
         boolean retScan = readBoolean(plcDb + ".0.0");
         boolean retFinish = readBoolean(plcDb + ".0.1");
@@ -354,7 +468,8 @@ public class WorkJPanelScan extends JPanel {
             pendingManualSn = "";
             log.info("<{}>手动码执行:{}", tag(), manualSn);
             gunField.setText("手动输入");
-            saveScan(manualSn, "手动输入(" + plcName + ")");
+            // 手动码按 PLC 当前枪号推码,枪号 0 或读不到则不推
+            saveScan(manualSn, "手动输入(" + plcName + ")", plcGunNo == null ? 0 : plcGunNo);
             return;
         }
         // 读不到枪号不扫码
@@ -385,14 +500,15 @@ public class WorkJPanelScan extends JPanel {
             return;
         }
 
-        saveScan(sn, gunNo + "号枪(" + plcName + ")");
+        saveScan(sn, gunNo + "号枪(" + plcName + ")", gunNo);
     }
 
-    // 存入正面扫码码并回写扫码完成反馈
-    private void saveScan(String sn, String source) {
+    // 存入正面扫码码并回写扫码完成反馈,同时留存推码
+    private void saveScan(String sn, String source, int gunNo) {
         slot.put(sn, source);
         productSnFeeding.setText(sn);
         writeScanFinish(true);
+        holdPushSn(gunNo, sn);
         scanDone = true;
         setMenuStatus("扫码成功" + (source.startsWith("手动") ? "(手动码)" : ""), 0);
     }

+ 2 - 1
src/com/mes/ui/MesClient.java

@@ -313,7 +313,8 @@ public class MesClient extends JFrame {
             WorkJPanelCnc panel = new WorkJPanelCnc(s[0], s[1], s[2], s[3], source, target);
             (front ? op130Panels : op135Panels).add(panel);
         }
-        workJPanelScan92 = new WorkJPanelScan(WorkJPanelCnc.PLC_92, frontSlot92);
+        // 92 推码:1 号枪 -> OP190 上料口1 DB7020.1034,2 号枪 -> OP190 上料口2 DB7020.1290
+        workJPanelScan92 = new WorkJPanelScan(WorkJPanelCnc.PLC_92, frontSlot92, "DB7020.1034", "DB7020.1290");
         workJPanelScan93 = new WorkJPanelScan(WorkJPanelCnc.PLC_93, frontSlot93);
         workJPanelBackSlot = new WorkJPanelBackSlot();