chefz před 1 týdnem
rodič
revize
92028229c5

+ 2 - 18
src/com/mes/component/ConfigPanel.java

@@ -135,24 +135,8 @@ public class ConfigPanel extends JPanel {
             // 1. 持久化到数据库 (包含 A/B 枪 IP 和任务数)
             JdbcUtils.updateConfig(aSet, bSet, ipA, ipB, gw, gwDes);
 
-            // 2. 同步更新内存变量
-            MesClient.mes_gw = gw;
-            MesClient.mes_gw_des = gwDes;
-            MesClient.aSetNum = aSet;
-            MesClient.aMax = aSet;
-            MesClient.bSetNum = bSet;
-            MesClient.bMax = bSet;
-            
-            // 3. 处理 PLC IP 变更逻辑 (独立判断,仅在变化时重连)
-            MesClient.reconnectPlcA(ipA);
-            MesClient.reconnectPlcB(ipB);
-
-            // 4. 同步更新主界面标签和标题
-            if (MesClient.param1 != null) MesClient.param1.setText(String.valueOf(aSet));
-            if (MesClient.param3 != null) MesClient.param3.setText(String.valueOf(bSet));
-            if (MesClient.mesClientFrame != null) {
-                MesClient.mesClientFrame.setTitle("MES系统客户端:" + gw + "- " + gwDes);
-            }
+            // 2. 从数据库重新加载并同步(与「刷新工件」同一套逻辑)
+            MesClient.reloadLocalConfig();
 
             JOptionPane.showMessageDialog(this, "配置保存并同步成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
         } catch (NumberFormatException ex) {

+ 19 - 19
src/com/mes/ui/ErrorMsg.java

@@ -8,46 +8,46 @@ public class ErrorMsg {
         try{
             String msg_type = ProtocolParam.getMsgType(mes_msg);
             String processMsgRet = MesMsgUtils.processMsg(mes_msg, msg_type);
-            String lmsg = "璇ュ伐浠舵湰宸ヤ綅涓嶅彲鍔犲伐";
+            String lmsg = "该工件本工位不可加工";
             if(processMsgRet.equalsIgnoreCase("OK")||processMsgRet.equalsIgnoreCase("NG")) {
-                lmsg = "该工件本工位已加工,结果:"+processMsgRet;
+                lmsg = "该工件本工位已加工,结果:"+processMsgRet;
             }else if(processMsgRet.equalsIgnoreCase("NE")) {
-                lmsg = "璇ュ伐浠舵湭褰曞叆绯荤粺";
+                lmsg = "该工件未录入系统";
             }else if(processMsgRet.equalsIgnoreCase("NN")) {
-                lmsg = "璇ュ伐浠惰烦杩囪�宸ヤ綅";
+                lmsg = "该工件跳过该工位";
             }else if(processMsgRet.equalsIgnoreCase("QN")) {
-                lmsg = "璇ュ伐浠禣P"+ ProtocolParam.getLx(mes_msg)+"0鍔犲伐NG";
+                lmsg = "该工件OP"+ ProtocolParam.getLx(mes_msg)+"0加工NG";
             }else if(processMsgRet.equalsIgnoreCase("QD")) {
-                lmsg = "该工件OP"+ ProtocolParam.getLx(mes_msg)+"0未加工";
+                lmsg = "该工件OP"+ ProtocolParam.getLx(mes_msg)+"0未加工";
             }else if(processMsgRet.equalsIgnoreCase("NF")) {
-                lmsg = "璇ュ伐浠跺凡鍚堟牸涓嬬嚎";
+                lmsg = "该工件已合格下线";
             }else if(processMsgRet.equalsIgnoreCase("NR")) {
-                lmsg = "璇ュ伐浠剁�绾胯繑淇�腑";
+                lmsg = "该工件离线返修中";
             }else if(processMsgRet.equalsIgnoreCase("NB")) {
-                lmsg = "璇ュ伐浠跺凡鎶ュ簾";
+                lmsg = "该工件已报废";
             }else if(processMsgRet.equalsIgnoreCase("ND")) {
-                lmsg = "该工件NG待处理";
+                lmsg = "该工件NG待处理";
             }else if(processMsgRet.equalsIgnoreCase("FN")) {
-                lmsg = "棣栦欢妫€鏌ュ伐浠朵笉鍚堟牸鍋滄満";
+                lmsg = "首件检查工件不合格停机";
             }else if(processMsgRet.equalsIgnoreCase("GN")) {
-                lmsg = "鏇存崲閰嶄欢棣栦欢妫€鏌ヤ笉鍚堟牸鍋滄満";
+                lmsg = "更换配件首件检查不合格停机";
             }else if(processMsgRet.equalsIgnoreCase("DJ")) {
-                lmsg = "鏈�繘琛屽紑鐝�偣妫€";
+                lmsg = "未进行开班点检";
             }else if(processMsgRet.equalsIgnoreCase("BM")) {
-                lmsg = "未绑定物料";
+                lmsg = "未绑定物料";
             }else if(processMsgRet.equalsIgnoreCase("PL")) {
-                lmsg = "閰嶄欢瀵垮懡涓嶈冻";
+                lmsg = "配件寿命不足";
             }else if(processMsgRet.equalsIgnoreCase("QM")) {
-                lmsg = "涓ゆ�姘斿瘑蹇呴』闂撮殧15鍒嗛挓";
+                lmsg = "两次气密必须间隔15分钟";
             }else if(processMsgRet.equalsIgnoreCase("GS")) {
-                lmsg = "宸ヤ欢鐮佹牸寮忎笉姝g‘";
+                lmsg = "工件码格式不正确";
             }else if(processMsgRet.equalsIgnoreCase("CF")) {
-                lmsg = "工件码重复";
+                lmsg = "工件码重复";
             }
 
             MesClient.setMenuStatus(lmsg,-1);
         }catch (Exception e){
-            MesClient.setMenuStatus("鏈�煡閿欒�",-1);
+            MesClient.setMenuStatus("未知错误",-1);
         }
     }
 }

+ 313 - 7
src/com/mes/ui/MesClient.java

@@ -97,8 +97,25 @@ public class MesClient extends JFrame {
     public static JTextField param_bf; // B枪拉力
     public static JTextField param_bs; // B枪位移
 
+    public static JButton repairBtnA;
+    public static JButton repairBtnB;
+    public static JTextField repairInputA;
+    public static JTextField repairInputB;
+    public static boolean repairModeA = false;
+    public static boolean repairModeB = false;
+    /** 返修偏移量(0=未返修);界面/判定有效数 = PLC读数 + repairOffset */
+    public static int repairOffsetA = 0;
+    public static int repairOffsetB = 0;
+    /** 上次 PLC 实际计数,用于检测新铆接(与返修下限独立) */
+    public static int lastPlcCountA = 0;
+    public static int lastPlcCountB = 0;
+    /** 本件是否经返修确认改过数(自动提交时按 OK 上报) */
+    public static boolean repairAppliedA = false;
+    public static boolean repairAppliedB = false;
+    private static final String REPAIR_MODE_PASSWORD = "503833";
+
     public static String curIpA = "192.168.0.200"; // A枪 PLC IP
-    public static String curIpB = "192.168.0.201"; // B枪 PLC IP
+    public static String curIpB = "192.168.0.202"; // B枪 PLC IP
     public static S7PLC plcA = null; // A枪 PLC 对象
     public static S7PLC plcB = null; // B枪 PLC 对象
 
@@ -151,7 +168,7 @@ public class MesClient extends JFrame {
                     readProperty();
 
                     // 3. 初始化本地数据库配置 (第一优先级,覆盖文件配置)
-                    initLocalConfig();
+                    reloadLocalConfig();
 
                     // 3.5 最终初始化 PLC 连接 (根据确定的 IP)
                     if (plcA == null) {
@@ -283,12 +300,11 @@ public class MesClient extends JFrame {
     }
 
     /**
-     * 初始化本地数据库配置 (最高优先级)
+     * 从本地数据库重新加载配置并同步到内存、界面及 PLC 连接
      */
-    private static void initLocalConfig() {
+    public static void reloadLocalConfig() {
         Map<String, Object> config = JdbcUtils.getConfig();
         if (config != null && config.size() > 0) {
-            // 数据库中存在的字段且不为 null 时才覆盖
             if (config.get("a_set_num") != null) {
                 aSetNum = (Integer) config.get("a_set_num");
                 aMax = aSetNum;
@@ -306,7 +322,6 @@ public class MesClient extends JFrame {
             String dbGw = (String) config.get("mes_gw");
             if (dbGw != null && !dbGw.isEmpty()) mes_gw = dbGw;
 
-            // 无论数据库存的是什么描述,都根据工位号重新匹配最新的描述
             mes_gw_des = OprnoUtil.getGwDes(mes_line_sn, mes_gw);
             if (mes_gw_des == null || mes_gw_des.isEmpty()) {
                 String dbGwDes = (String) config.get("mes_gw_des");
@@ -318,9 +333,18 @@ public class MesClient extends JFrame {
             log.info("本地数据库无配置,将维持配置文件或硬编码配置。");
         }
 
-        // 同步 UI (如果已初始化)
+        if (curIpA != null && !curIpA.isEmpty()) {
+            reconnectPlcA(curIpA);
+        }
+        if (curIpB != null && !curIpB.isEmpty()) {
+            reconnectPlcB(curIpB);
+        }
+
         if (param1 != null) param1.setText(String.valueOf(aSetNum));
         if (param3 != null) param3.setText(String.valueOf(bSetNum));
+        if (mesClientFrame != null) {
+            mesClientFrame.setTitle("MES系统客户端:" + mes_gw + "- " + mes_gw_des);
+        }
     }
 
     public static void getPlcParam() {
@@ -466,7 +490,159 @@ public class MesClient extends JFrame {
         resetScanA();
     }
 
+    private static void updateRepairBtnStyle(JButton btn, boolean active) {
+        if (btn == null) {
+            return;
+        }
+        if (active) {
+            btn.setBackground(new Color(76, 175, 80));
+            btn.setForeground(Color.WHITE);
+            btn.setOpaque(true);
+            btn.setBorderPainted(false);
+            btn.setText("返修模式");
+        } else {
+            btn.setBackground(UIManager.getColor("Button.background"));
+            btn.setForeground(UIManager.getColor("Button.foreground"));
+            btn.setOpaque(true);
+            btn.setBorderPainted(true);
+            btn.setText("返修模式");
+        }
+    }
+
+    public static void resetRepairMode() {
+        repairModeA = false;
+        repairModeB = false;
+        updateRepairBtnStyle(repairBtnA, false);
+        updateRepairBtnStyle(repairBtnB, false);
+        if (repairInputA != null) {
+            repairInputA.setText("");
+        }
+        if (repairInputB != null) {
+            repairInputB.setText("");
+        }
+    }
+
+    private static boolean verifyRepairPassword() {
+        String pass = JOptionPane.showInputDialog(mesClientFrame, "请输入密码");
+        if (pass == null) {
+            return false;
+        }
+        if (!REPAIR_MODE_PASSWORD.equals(pass.trim())) {
+            setMenuStatus("密码错误", -1);
+            return false;
+        }
+        return true;
+    }
+
+    public static void toggleRepairModeA() {
+        if (!repairModeA && !verifyRepairPassword()) {
+            return;
+        }
+        repairModeA = !repairModeA;
+        updateRepairBtnStyle(repairBtnA, repairModeA);
+        if (!repairModeA && repairInputA != null) {
+            repairInputA.setText("");
+        }
+    }
+
+    public static void toggleRepairModeB() {
+        if (!repairModeB && !verifyRepairPassword()) {
+            return;
+        }
+        repairModeB = !repairModeB;
+        updateRepairBtnStyle(repairBtnB, repairModeB);
+        if (!repairModeB && repairInputB != null) {
+            repairInputB.setText("");
+        }
+    }
+
+    private static void exitRepairModeA() {
+        repairModeA = false;
+        updateRepairBtnStyle(repairBtnA, false);
+        if (repairInputA != null) {
+            repairInputA.setText("");
+        }
+    }
+
+    private static void exitRepairModeB() {
+        repairModeB = false;
+        updateRepairBtnStyle(repairBtnB, false);
+        if (repairInputB != null) {
+            repairInputB.setText("");
+        }
+    }
+
+    public static void confirmRepairCountA() {
+        if (!repairModeA) {
+            setMenuStatus("请先开启A枪返修模式", -1);
+            return;
+        }
+        if (work_status != 1) {
+            setMenuStatus("请先扫码开始加工", -1);
+            return;
+        }
+        try {
+            int count = Integer.parseInt(repairInputA.getText().trim());
+            if (count <= 0 || count > aMax) {
+                setMenuStatus("A枪返修数量须在 1~" + aMax + " 之间", -1);
+                return;
+            }
+            int plcCount = 0;
+            try {
+                plcCount = plcA.readInt32("DB41.32");
+            } catch (Exception ignored) {
+            }
+            repairOffsetA = count - plcCount;
+            repairAppliedA = true;
+            lastPlcCountA = Math.max(lastPlcCountA, plcCount);
+            sortA = plcCount + repairOffsetA;
+            param2.setText(String.valueOf(sortA));
+            PlcUtil.applyRepairCountA(plcA, count);
+            exitRepairModeA();
+            setMenuStatus("A枪完成数量已设为 " + count, 0);
+            afterRepairCountChanged();
+        } catch (NumberFormatException e) {
+            setMenuStatus("A枪返修数量请输入有效数字", -1);
+        }
+    }
+
+    public static void confirmRepairCountB() {
+        if (!repairModeB) {
+            setMenuStatus("请先开启B枪返修模式", -1);
+            return;
+        }
+        if (work_status != 1) {
+            setMenuStatus("请先扫码开始加工", -1);
+            return;
+        }
+        try {
+            int count = Integer.parseInt(repairInputB.getText().trim());
+            if (count <= 0 || count > bMax) {
+                setMenuStatus("B枪返修数量须在 1~" + bMax + " 之间", -1);
+                return;
+            }
+            int plcCount = 0;
+            try {
+                plcCount = plcB.readInt32("DB41.32");
+            } catch (Exception ignored) {
+            }
+            repairOffsetB = count - plcCount;
+            repairAppliedB = true;
+            lastPlcCountB = Math.max(lastPlcCountB, plcCount);
+            sortB = plcCount + repairOffsetB;
+            param4.setText(String.valueOf(sortB));
+            PlcUtil.applyRepairCountB(plcB, count);
+            exitRepairModeB();
+            setMenuStatus("B枪完成数量已设为 " + count, 0);
+            afterRepairCountChanged();
+        } catch (NumberFormatException e) {
+            setMenuStatus("B枪返修数量请输入有效数字", -1);
+        }
+    }
+
     public static void resetScanA() {
+        reloadLocalConfig();
+
         MesClient.status_menu.setText("工件状态");
         MesClient.curSn = "";
         MesClient.product_sn.setText("");
@@ -481,6 +657,12 @@ public class MesClient extends JFrame {
         MesClient.sortB = 0;
         MesClient.aFinish = 0;
         MesClient.bFinish = 0;
+        MesClient.repairOffsetA = 0;
+        MesClient.repairOffsetB = 0;
+        MesClient.repairAppliedA = false;
+        MesClient.repairAppliedB = false;
+        MesClient.lastPlcCountA = 0;
+        MesClient.lastPlcCountB = 0;
         MesClient.alist.clear();
         MesClient.atids.clear();
         MesClient.param2.setText("0");
@@ -489,12 +671,98 @@ public class MesClient extends JFrame {
         MesClient.param_as.setText("0");
         MesClient.param_bf.setText("0");
         MesClient.param_bs.setText("0");
+        MesClient.aMax = aSetNum;
+        MesClient.bMax = bSetNum;
         MesClient.param1.setText(String.valueOf(aSetNum));
         MesClient.param3.setText(String.valueOf(bSetNum));
 
         PlcUtil.setTask(MesClient.plcA, MesClient.aSetNum, MesClient.bSetNum);
         PlcUtil.setPowerOff(MesClient.plcA); // 远程关机
         PlcUtil.setPowerOff(MesClient.plcB); // 远程关机
+        resetRepairMode();
+    }
+
+    /** 返修改数后立即检查是否双枪已满,触发关气与提交(无 syncGunPowerState 时手工同步气路) */
+    public static void afterRepairCountChanged() {
+        if (work_status != 1) {
+            return;
+        }
+        try {
+            if (plcA != null) {
+                int effectiveA = PlcUtil.getEffectiveCountA(plcA);
+                if (effectiveA >= aMax) {
+                    PlcUtil.setPowerOff(plcA);
+                } else {
+                    PlcUtil.setPowerOn(plcA);
+                }
+            }
+            if (plcB != null && bMax > 0) {
+                int effectiveB = PlcUtil.getEffectiveCountB(plcB);
+                if (effectiveB >= bMax) {
+                    PlcUtil.setPowerOff(plcB);
+                } else {
+                    PlcUtil.setPowerOn(plcB);
+                }
+            }
+        } catch (Exception e) {
+            log.warn("返修改数后同步气路失败", e);
+        }
+        tryAutoSubmitWhenBothGunsDone();
+    }
+
+    /** 双枪模式:A、B 均达到预定数量;单枪模式(bMax<=0):仅 A 达到即可(按有效完成数,含返修偏移) */
+    public static boolean isBothGunsReachTarget() {
+        if (curSn.isEmpty() || aMax <= 0 || plcA == null) {
+            return false;
+        }
+        try {
+            int curA = PlcUtil.getEffectiveCountA(plcA);
+            if (curA < aMax) {
+                return false;
+            }
+            if (bMax <= 0) {
+                return true;
+            }
+            if (plcB == null) {
+                return false;
+            }
+            int curB = PlcUtil.getEffectiveCountB(plcB);
+            return curB >= bMax;
+        } catch (Exception e) {
+            log.warn("读取 PLC 计数失败,使用内存计数兜底", e);
+            if (sortA < aMax) {
+                return false;
+            }
+            if (bMax <= 0) {
+                return true;
+            }
+            return sortB >= bMax;
+        }
+    }
+
+    /** 达到预定数量后启用提交并自动上传结果(双枪须都完成) */
+    public static void tryAutoSubmitWhenBothGunsDone() {
+        if (!isBothGunsReachTarget()) {
+            return;
+        }
+        finish_ok_bt.setEnabled(true);
+        finish_ng_bt.setEnabled(true);
+        if (tjStatus != 0 || !check_quality_result) {
+            return;
+        }
+        tjStatus = 1;
+        setMenuStatus("正在自动提交结果...", 0);
+        getUser();
+        String retf = "OK";
+        boolean repairAdjusted = repairAppliedA || repairAppliedB;
+        if (!repairAdjusted && (aFinish < aMax || (bMax > 0 && bFinish < bMax))) {
+            retf = "NG";
+        }
+        Boolean ret = DataUtil.sendQuality(nettyClient, product_sn.getText(), retf, user20);
+        if (!ret) {
+            tjStatus = 0;
+            setMenuStatus(tjFlagTextErr, -1);
+        }
     }
 
     public static void checkSnType(String sn) {
@@ -824,6 +1092,44 @@ public class MesClient extends JFrame {
         finish_ng_bt.setEnabled(false);
         indexPanelA.add(finish_ng_bt);
 
+        Font repairFont = new Font("微软雅黑", Font.PLAIN, 14);
+
+        repairBtnA = new JButton("返修模式");
+        repairBtnA.setFont(repairFont);
+        repairBtnA.setBounds(25, 150, 95, 32);
+        repairBtnA.addActionListener(e -> toggleRepairModeA());
+        indexPanelA.add(repairBtnA);
+
+        repairInputA = new JTextField();
+        repairInputA.setFont(repairFont);
+        repairInputA.setHorizontalAlignment(SwingConstants.CENTER);
+        repairInputA.setBounds(125, 150, 55, 32);
+        indexPanelA.add(repairInputA);
+
+        JButton repairConfirmA = new JButton("确定数量");
+        repairConfirmA.setFont(repairFont);
+        repairConfirmA.setBounds(185, 150, 95, 32);
+        repairConfirmA.addActionListener(e -> confirmRepairCountA());
+        indexPanelA.add(repairConfirmA);
+
+        repairBtnB = new JButton("返修模式");
+        repairBtnB.setFont(repairFont);
+        repairBtnB.setBounds(480, 150, 95, 32);
+        repairBtnB.addActionListener(e -> toggleRepairModeB());
+        indexPanelA.add(repairBtnB);
+
+        repairInputB = new JTextField();
+        repairInputB.setFont(repairFont);
+        repairInputB.setHorizontalAlignment(SwingConstants.CENTER);
+        repairInputB.setBounds(580, 150, 55, 32);
+        indexPanelA.add(repairInputB);
+
+        JButton repairConfirmB = new JButton("确定数量");
+        repairConfirmB.setFont(repairFont);
+        repairConfirmB.setBounds(640, 150, 95, 32);
+        repairConfirmB.addActionListener(e -> confirmRepairCountB());
+        indexPanelA.add(repairConfirmB);
+
         JLabel lblNewLabel = new JLabel("A枪");
         lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
         lblNewLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));

+ 13 - 0
src/com/mes/ui/MesRevice.java

@@ -13,6 +13,8 @@ public class MesRevice {
         try{
             String sn = ProtocolParam.getSn(mes_msg).trim();
             MesClient.checkSnType(sn);
+            MesClient.aMax = MesClient.aSetNum;
+            MesClient.bMax = MesClient.bSetNum;
 
             if(processMsgRet.equalsIgnoreCase("UD")) {
                 MesClient.status_menu.setForeground(Color.GREEN);
@@ -22,6 +24,17 @@ public class MesRevice {
                 MesClient.work_status = 1;
                 MesClient.f_scan_data_bt_1.setEnabled(false);
                 MesClient.tjStatus = 0;
+                MesClient.sortA = 0;
+                MesClient.sortB = 0;
+                MesClient.aFinish = 0;
+                MesClient.bFinish = 0;
+                MesClient.repairOffsetA = 0;
+                MesClient.repairOffsetB = 0;
+                MesClient.repairAppliedA = false;
+                MesClient.repairAppliedB = false;
+                MesClient.lastPlcCountA = 0;
+                MesClient.lastPlcCountB = 0;
+                MesClient.resetRepairMode();
 
                 PlcUtil.setPowerOn(MesClient.plcA); // 远程开机 A
                 PlcUtil.setPowerOn(MesClient.plcB); // 远程开机 B

+ 1 - 18
src/com/mes/ui/ModbusUtil.java

@@ -57,24 +57,7 @@ public class ModbusUtil {
 
     // 上传总结果
     public static void upResult(){
-        if(!MesClient.curSn.isEmpty() && MesClient.aMax > 0 && MesClient.aMax == MesClient.sortA){
-            MesClient.finish_ok_bt.setEnabled(true);
-            MesClient.finish_ng_bt.setEnabled(true);
-
-            if(MesClient.tjStatus == 0 && MesClient.check_quality_result){
-                MesClient.tjStatus = 1;
-                MesClient.getUser();
-                String retf = "OK";
-                if(MesClient.aFinish != MesClient.aMax){
-                    retf = "NG";
-                }
-                Boolean ret = DataUtil.sendQuality(MesClient.nettyClient, MesClient.product_sn.getText(),retf, MesClient.user20);
-                if(!ret){
-                    MesClient.setMenuStatus(MesClient.tjFlagTextErr,-1);
-                }
-            }
-
-        }
+        MesClient.tryAutoSubmitWhenBothGunsDone();
     }
 
     // 获取控制模式

+ 165 - 143
src/com/mes/ui/PlcUtil.java

@@ -2,72 +2,108 @@ package com.mes.ui;
 
 import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
 import com.mes.util.JdbcUtils;
+
+import javax.swing.*;
 import java.text.DecimalFormat;
 
 /**
  * PLC 通讯工具类
- * 处理 A 枪和 B 枪的数据采集、任务设置及状态控制
+ * LM-2-1:A/B 各一台 PLC,单枪数据块均为 DB40/DB41(非 LM-1-2 同机 DB50/DB51)
  */
 public class PlcUtil {
 
+    private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.###");
+
+    private static void updateUi(Runnable runnable) {
+        if (SwingUtilities.isEventDispatchThread()) {
+            runnable.run();
+        } else {
+            SwingUtilities.invokeLater(runnable);
+        }
+    }
+
+    /**
+     * 有效完成数:PLC + 返修偏移。返修时 offset=设定值-当时PLC值,之后PLC每+1界面同步+1。
+     * A/B 均读各自 PLC 的 DB41.32(双 PLC,非同机 DB51.32)。
+     */
+    public static int getEffectiveCountA(S7PLC plc) {
+        int plcCount = 0;
+        try {
+            if (plc != null) {
+                plcCount = plc.readInt32("DB41.32");
+            }
+        } catch (Exception ignored) {
+        }
+        return plcCount + MesClient.repairOffsetA;
+    }
+
+    public static int getEffectiveCountB(S7PLC plc) {
+        int plcCount = 0;
+        try {
+            if (plc != null) {
+                plcCount = plc.readInt32("DB41.32");
+            }
+        } catch (Exception ignored) {
+        }
+        return plcCount + MesClient.repairOffsetB;
+    }
+
     /**
      * 获取 A 枪数据
-     * @param plc PLC对象
      */
-    public static void getDataA(S7PLC plc){
+    public static void getDataA(S7PLC plc) {
         try {
-            if (plc == null) return;
-            // 读取当前完成数量 (DB41.32)
-            int cur = plc.readInt32("DB41.32.0");
-            MesClient.param1.setText(String.valueOf(MesClient.aMax));
-
-            // 如果当前数量增加,则记录数据
-            if(MesClient.sortA < cur){
-                MesClient.sortA = cur;
-                if (cur == 9){
-                    plc.writeUInt16("DB40.2",2);
-                }
-                
-                // 读取力值和位移
-                int fout = plc.readInt32("DB41.24.0");
-                Float sout = plc.readFloat32("DB41.28.0");
+            if (plc == null) {
+                return;
+            }
+            int cur = plc.readInt32("DB41.32");
+            int fout = plc.readInt32("DB41.24");
+            float sout = plc.readFloat32("DB41.28");
+            String soutstr = DECIMAL_FORMAT.format(sout);
 
-                // 读取最小值设定
-                int fmin = plc.readInt32("DB41.0.0") - plc.readInt32("DB41.8.0");
-                Float smin = plc.readFloat32("DB41.12.0") - plc.readFloat32("DB41.20.0");
+            // 以 PLC 增量判断新铆接,与返修下限无关,避免返修抬高后漏记后续铆点
+            if (cur > MesClient.lastPlcCountA) {
+                MesClient.lastPlcCountA = cur;
+                if (cur == 9) {
+                    plc.writeUInt16("DB40.2", 2);
+                }
 
-                // 读取最大值设定
-                int fmax = plc.readInt32("DB41.0.0") + plc.readInt32("DB41.4.0");
-                Float smax = (float) (plc.readInt32("DB41.12.0") + plc.readFloat32("DB41.16.0"));
+                int fmin = plc.readInt32("DB41.0") - plc.readInt32("DB41.8");
+                float smin = plc.readFloat32("DB41.12") - plc.readFloat32("DB41.20");
+                int fmax = plc.readInt32("DB41.0") + plc.readInt32("DB41.4");
+                float smax = plc.readInt32("DB41.12") + plc.readFloat32("DB41.16");
 
                 String qty = "0";
-                // 判定单次结果
-                if(getResultDingA(plc).equals("OK")){
+                if (getResultDingA(plc).equals("OK")) {
                     MesClient.aFinish++;
                     qty = "1";
                 }
 
-                DecimalFormat decimalFormat = new DecimalFormat("#.###");
-                String soutstr = decimalFormat.format(sout);
-                String sminstr = decimalFormat.format(smin);
-                String smaxstr = decimalFormat.format(smax);
-                
-                MesClient.param2.setText(cur+"");
-                MesClient.param_af.setText(fout + ""); // 实时更新 A 枪拉力显示
-                MesClient.param_as.setText(soutstr); // 实时更新 A 枪位移显示
-                
-                // 插入本地数据库
-                if(!MesClient.product_sn.getText().isEmpty()){
-                    JdbcUtils.insertProdData(MesClient.mes_gw, MesClient.mes_line_sn, MesClient.product_sn.getText(),"A",fout+"",soutstr,fmin+"",sminstr,fmax+"",smaxstr,qty,cur+"", MesClient.user_menu.getText());
+                String sminstr = DECIMAL_FORMAT.format(smin);
+                String smaxstr = DECIMAL_FORMAT.format(smax);
+
+                if (!MesClient.product_sn.getText().isEmpty()) {
+                    JdbcUtils.insertProdData(MesClient.mes_gw, MesClient.mes_line_sn, MesClient.product_sn.getText(),
+                            "A", fout + "", soutstr, fmin + "", sminstr, fmax + "", smaxstr, qty, cur + "",
+                            MesClient.user_menu.getText());
                 }
             }
 
-            // A 枪任务完成,自动关机
-            if (cur >= MesClient.aMax){
+            int effective = getEffectiveCountA(plc);
+            MesClient.sortA = effective;
+            final int displayCount = effective;
+            final int displayFout = fout;
+            final String displaySout = soutstr;
+            updateUi(() -> {
+                MesClient.param1.setText(String.valueOf(MesClient.aMax));
+                MesClient.param2.setText(String.valueOf(displayCount));
+                MesClient.param_af.setText(String.valueOf(displayFout));
+                MesClient.param_as.setText(displaySout);
+            });
+
+            if (effective >= MesClient.aMax) {
                 setPowerOff(plc);
             }
-
-            // 检查是否上传总结果
             upResult();
         } catch (Exception e) {
             e.printStackTrace();
@@ -75,49 +111,55 @@ public class PlcUtil {
     }
 
     /**
-     * 获取 B 枪数据
-     * @param plc PLC对象
+     * 获取 B 枪数据(B 枪独立 PLC,地址与 A 枪相同为 DB41)
      */
-    public static void getDataB(S7PLC plc){
+    public static void getDataB(S7PLC plc) {
         try {
-            if (plc == null) return;
-            // B 枪逻辑点位先留空或按需开启
-            int cur = plc.readInt32("DB51.32.0");
-            MesClient.param3.setText(String.valueOf(MesClient.bMax));
-
-            if(MesClient.sortB < cur){
-                MesClient.sortB = cur;
-
-                int fout = plc.readInt32("DB51.24.0");
-                Float sout = plc.readFloat32("DB51.28.0");
+            if (plc == null || MesClient.bMax <= 0) {
+                return;
+            }
+            int cur = plc.readInt32("DB41.32");
+            int fout = plc.readInt32("DB41.24");
+            float sout = plc.readFloat32("DB41.28");
+            String soutstr = DECIMAL_FORMAT.format(sout);
 
-                int fmin = plc.readInt32("DB51.0.0") - plc.readInt32("DB51.8.0");
-                Float smin = plc.readFloat32("DB51.12.0") - plc.readFloat32("DB51.20.0");
+            if (cur > MesClient.lastPlcCountB) {
+                MesClient.lastPlcCountB = cur;
 
-                int fmax = plc.readInt32("DB51.0.0") + plc.readInt32("DB51.4.0");
-                Float smax = (float) (plc.readInt32("DB51.12.0") + plc.readFloat32("DB51.16.0"));
+                int fmin = plc.readInt32("DB41.0") - plc.readInt32("DB41.8");
+                float smin = plc.readFloat32("DB41.12") - plc.readFloat32("DB41.20");
+                int fmax = plc.readInt32("DB41.0") + plc.readInt32("DB41.4");
+                float smax = plc.readInt32("DB41.12") + plc.readFloat32("DB41.16");
 
                 String qty = "0";
-                if(getResultDingB(plc).equals("OK")){
+                if (getResultDingB(plc).equals("OK")) {
                     MesClient.bFinish++;
                     qty = "1";
                 }
 
-                DecimalFormat decimalFormat = new DecimalFormat("#.###");
-                String soutstr = decimalFormat.format(sout);
-                String sminstr = decimalFormat.format(smin);
-                String smaxstr = decimalFormat.format(smax);
+                String sminstr = DECIMAL_FORMAT.format(smin);
+                String smaxstr = DECIMAL_FORMAT.format(smax);
 
-                MesClient.param4.setText(cur+"");
-                MesClient.param_bf.setText(fout + "");
-                MesClient.param_bs.setText(soutstr);
-
-                if(!MesClient.product_sn.getText().isEmpty()){
-                    JdbcUtils.insertProdData(MesClient.mes_gw, MesClient.mes_line_sn, MesClient.product_sn.getText(),"B",fout+"",soutstr,fmin+"",sminstr,fmax+"",smaxstr,qty,cur+"", MesClient.user_menu.getText());
+                if (!MesClient.product_sn.getText().isEmpty()) {
+                    JdbcUtils.insertProdData(MesClient.mes_gw, MesClient.mes_line_sn, MesClient.product_sn.getText(),
+                            "B", fout + "", soutstr, fmin + "", sminstr, fmax + "", smaxstr, qty, cur + "",
+                            MesClient.user_menu.getText());
                 }
             }
 
-            if (cur >= MesClient.bMax){
+            int effective = getEffectiveCountB(plc);
+            MesClient.sortB = effective;
+            final int displayCount = effective;
+            final int displayFout = fout;
+            final String displaySout = soutstr;
+            updateUi(() -> {
+                MesClient.param3.setText(String.valueOf(MesClient.bMax));
+                MesClient.param4.setText(String.valueOf(displayCount));
+                MesClient.param_bf.setText(String.valueOf(displayFout));
+                MesClient.param_bs.setText(displaySout);
+            });
+
+            if (effective >= MesClient.bMax) {
                 setPowerOff(plc);
             }
             upResult();
@@ -126,111 +168,91 @@ public class PlcUtil {
         }
     }
 
+    public static void upResult() {
+        MesClient.tryAutoSubmitWhenBothGunsDone();
+    }
+
     /**
-     * 上传总结果到 MES
+     * 返修模式:仅更新 MES 侧气路(不写 PLC 实际完成数 DB41.32)
      */
-    public static void upResult(){
-        // 当 A 枪和 B 枪的完成数量都达到预设值时触发
-        if(!MesClient.curSn.isEmpty() && MesClient.aMax >= 0 && MesClient.sortA == MesClient.aMax && MesClient.bMax >= 0 && MesClient.sortB == MesClient.bMax){
-            MesClient.finish_ok_bt.setEnabled(true);
-            MesClient.finish_ng_bt.setEnabled(true);
-
-            // 自动提交逻辑:仅在未提交且质量校验通过时执行一次
-            if(MesClient.tjStatus == 0 && MesClient.check_quality_result){
-                MesClient.tjStatus = 1; // 标记为已提交,防止重复触发
-                MesClient.setMenuStatus("正在自动提交结果...", 0);
-                
-                MesClient.getUser();
-                String retf = "OK";
-                // 如果 OK 的数量不等于预设数量,则总结果为 NG
-                if(MesClient.aFinish < MesClient.aMax || MesClient.bFinish < MesClient.bMax){
-                    retf = "NG";
-                }
-                
-                Boolean ret = DataUtil.sendQuality(MesClient.nettyClient, MesClient.product_sn.getText(), retf, MesClient.user20);
-                if(!ret){
-                    MesClient.tjStatus = 0; // 发送失败则允许重试
-                    MesClient.setMenuStatus(MesClient.tjFlagTextErr, -1);
-                }
-            }
+    public static void applyRepairCountA(S7PLC plc, int count) {
+        if (count < MesClient.aMax && plc != null) {
+            setPowerOn(plc);
+            MesClient.tjStatus = 0;
         }
     }
 
-    /**
-     * 远程开机
-     */
-    public static Boolean setPowerOn(S7PLC plc){
-        try{
-            if (plc == null) return false;
-            plc.writeBoolean("DB40.0.0",true);
+    public static void applyRepairCountB(S7PLC plc, int count) {
+        if (count < MesClient.bMax && plc != null) {
+            setPowerOn(plc);
+            MesClient.tjStatus = 0;
+        }
+    }
+
+    public static Boolean setPowerOn(S7PLC plc) {
+        try {
+            if (plc == null) {
+                return false;
+            }
+            plc.writeBoolean("DB40.0", true);
             return true;
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return false;
         }
     }
 
-    /**
-     * 远程关机
-     */
-    public static Boolean setPowerOff(S7PLC plc){
-        try{
-            if (plc == null) return false;
-            plc.writeBoolean("DB40.0.0",false);
+    public static Boolean setPowerOff(S7PLC plc) {
+        try {
+            if (plc == null) {
+                return false;
+            }
+            plc.writeBoolean("DB40.0", false);
             return true;
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return false;
         }
     }
 
-    /**
-     * 重置任务数量并重置当前计数
-     */
-    public static void setTask(S7PLC plc,int setNumA,int setNumB){
-        try{
-            // 设置 A 枪任务
+    public static void setTask(S7PLC plc, int setNumA, int setNumB) {
+        try {
             if (MesClient.plcA != null) {
                 MesClient.plcA.writeUInt16("DB40.2", 1);
                 MesClient.plcA.writeUInt16("DB40.4", setNumA);
-                MesClient.plcA.writeInt32("DB41.32.0", 0);
+                MesClient.plcA.writeInt32("DB41.32", 0);
             }
-
-            // 设置 B 枪任务
-            if (MesClient.plcB != null) {
-                // B 枪点位先空着或占位,按需开启
-                 MesClient.plcB.writeUInt16("DB40.2", 1);
-                 MesClient.plcB.writeUInt16("DB40.4", setNumB);
-                 MesClient.plcB.writeInt32("DB41.32.0", 0);
+            if (MesClient.plcB != null && setNumB > 0) {
+                MesClient.plcB.writeUInt16("DB40.2", 1);
+                MesClient.plcB.writeUInt16("DB40.4", setNumB);
+                MesClient.plcB.writeInt32("DB41.32", 0);
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    /**
-     * 获取 A 枪单次拉铆结果
-     */
-    public static String getResultDingA(S7PLC plc){
-        try{
-            if (plc == null) return "NG";
-            byte retang = plc.readByte("DB41.26");
+    public static String getResultDingA(S7PLC plc) {
+        try {
+            if (plc == null) {
+                return "NG";
+            }
+            byte retang = plc.readByte("DB41.36");
             return (retang == 1) ? "OK" : "NG";
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return "NG";
         }
     }
 
-    /**
-     * 获取 B 枪单次拉铆结果
-     */
-    public static String getResultDingB(S7PLC plc){
-        try{
-            if (plc == null) return "NG";
-            byte retang = plc.readByte("DB41.26");
+    public static String getResultDingB(S7PLC plc) {
+        try {
+            if (plc == null) {
+                return "NG";
+            }
+            byte retang = plc.readByte("DB41.36");
             return (retang == 1) ? "OK" : "NG";
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
             return "NG";
         }

+ 2 - 2
src/resources/config/config.properties

@@ -1,6 +1,6 @@
 mes.gw=OP281A
-mes.server_ip=127.0.0.1
-#mes.server_ip=192.168.15.99
+#mes.server_ip=127.0.0.1
+mes.server_ip=192.168.15.99
 mes.tcp_port=3000
 mes.heart_beat_cycle=60
 mes.line_sn=XT