Jelajahi Sumber

Merge branch 'master' of http://121.36.46.135:3000/mes-ljz/mesclient-qm

# Conflicts:
#	src/com/mes/util/WorkTimer.java
sunxianglong 18 jam lalu
induk
melakukan
021c9834e0

+ 1 - 0
last_submitted_sn.txt

@@ -0,0 +1 @@
+0050010200173E5240017067171A02

+ 187 - 35
src/com/mes/ui/MesClient.java

@@ -74,6 +74,11 @@ public class MesClient extends JFrame {
     public static JButton finish_ng_bt;
     public static JTextField product_sn;
     public static JButton f_scan_data_bt_1;
+    public static JButton f_start_bt;
+    public static JButton f_retest_bt;
+    /** 最近一次提交成功的工件码(再测一次用) */
+    public static String lastSubmittedSn = "";
+    private static final String LAST_SN_FILE = "last_submitted_sn.txt";
 
     public static String user20 = "";
     public static boolean mes_enable = true; // true=MES开启
@@ -341,7 +346,7 @@ public class MesClient extends JFrame {
         MesClient.finish_ok_bt.setEnabled(false);
         MesClient.finish_ng_bt.setEnabled(false);
         product_sn.setText("");
-        product_sn.setEditable(true);
+        product_sn.setEditable(false);
         tjFlag = 0;
         mesStartFlag = 0;
         WorkTimer.resetEndConfirm();
@@ -354,14 +359,141 @@ public class MesClient extends JFrame {
         if (param4 != null) {
             param4.setText("");
         }
-
-//        f_scan_data_bt_1.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
-//        f_scan_data_bt_1.setText("扫码");
-//        MesClient.f_scan_data_bt_1.setEnabled(true);
+        setStartButtonEnabled(false);
+        if (f_scan_data_bt_1 != null) {
+            f_scan_data_bt_1.setEnabled(true);
+        }
+        // 测完恢复常态:继续每 5s 发停止/复位
+        if (serialPortUtils != null && serialPortUtils.isOpen) {
+            WorkTimer.startStopTimer();
+        }
+        refreshRetestButton();
         MesClient.setMenuStatus("请扫工件码",0);
+    }
+
+    /** 启动按钮可用状态(校验成功后点亮;检测中强制不可用) */
+    public static void setStartButtonEnabled(boolean enabled) {
+        if (f_start_bt == null) {
+            return;
+        }
+        // 设备检测中(已进入测试)不允许再点启动
+        if (enabled && mesStartFlag != null && mesStartFlag == 1) {
+            f_start_bt.setEnabled(false);
+            refreshRetestButton();
+            return;
+        }
+        f_start_bt.setEnabled(enabled);
+        refreshRetestButton();
+    }
 
-//        MesClient.result.setText("等待结果");
-//        MesClient.result.setForeground(Color.GRAY);
+    /** 结果提交成功后保存最近工件码(内存 + 本地文件) */
+    public static void saveLastSubmittedSn(String sn) {
+        if (sn == null || sn.trim().isEmpty()) {
+            return;
+        }
+        lastSubmittedSn = sn.trim();
+        try {
+            java.io.FileWriter fw = new java.io.FileWriter(LAST_SN_FILE, false);
+            fw.write(lastSubmittedSn);
+            fw.close();
+            log.info("已保存再测工件码: {}", lastSubmittedSn);
+        } catch (Exception e) {
+            log.error("保存再测工件码失败: {}", e.getMessage());
+        }
+        refreshRetestButton();
+    }
+
+    /** 启动时加载上次提交的工件码 */
+    public static void loadLastSubmittedSn() {
+        try {
+            java.io.File f = new java.io.File(LAST_SN_FILE);
+            if (!f.exists()) {
+                return;
+            }
+            BufferedReader br = new BufferedReader(new java.io.FileReader(f));
+            String line = br.readLine();
+            br.close();
+            if (line != null && !line.trim().isEmpty()) {
+                lastSubmittedSn = line.trim();
+                log.info("已加载再测工件码: {}", lastSubmittedSn);
+            }
+        } catch (Exception e) {
+            log.error("加载再测工件码失败: {}", e.getMessage());
+        }
+        refreshRetestButton();
+    }
+
+    /** 空闲且有历史码时,「再测一次」可点 */
+    public static void refreshRetestButton() {
+        if (f_retest_bt == null) {
+            return;
+        }
+        boolean idle = work_status != null && work_status == 0
+                && (mesStartFlag == null || mesStartFlag == 0);
+        boolean hasSn = lastSubmittedSn != null && !lastSubmittedSn.trim().isEmpty();
+        f_retest_bt.setEnabled(idle && hasSn);
+    }
+
+    /**
+     * 再测一次:回填上次提交工件码,走与扫码相同的校验流程。
+     */
+    public static void retestLastSn() {
+        if (lastSubmittedSn == null || lastSubmittedSn.trim().isEmpty()) {
+            setMenuStatus("没有可再测的工件码", -1);
+            return;
+        }
+        if (work_status != null && work_status == 1) {
+            setMenuStatus("当前已有工件在测,请先完成", -1);
+            return;
+        }
+        if (mesStartFlag != null && mesStartFlag == 1) {
+            setMenuStatus("检测中,无法再测", -1);
+            return;
+        }
+        product_sn.setText(lastSubmittedSn.trim());
+        scan_type = 1;
+        setMenuStatus("再测:正在校验工件码", 0);
+        scanBarcode2();
+    }
+
+    /**
+     * 扫码/质量校验成功后:停止复位定时器,并按启动模式点亮启动或自动启动。
+     * MES:在 checkQuality 返回 UD 时调用;本地无 MES:扫码本地校验通过后调用。
+     */
+    public static void onScanValidated() {
+        WorkTimer.stopStopTimer();
+        refreshRetestButton();
+        if (configParam != null && "自动启动".equals(configParam.getStartMode())) {
+            doStartTest();
+        } else {
+            setStartButtonEnabled(true);
+        }
+    }
+
+    /**
+     * 点击启动:发启动报文,然后熄灭启动按钮。
+     * 复位定时器已在扫码成功时停止,此处不再发停止信号。
+     */
+    public static void doStartTest() {
+        if (work_status != 1) {
+            setMenuStatus("请先扫工件码", -1);
+            return;
+        }
+        WorkTimer.stopStopTimer();
+        loadAteqProgramTimes();
+        boolean ok = serialPortUtils != null && serialPortUtils.start();
+        setStartButtonEnabled(false);
+        if (ok) {
+            // 等轮询见到测试中(bit5=0)再置 mesStartFlag=1,避免沿用上一轮周期结束状态误收尾
+            mesStartFlag = 0;
+            refreshRetestButton();
+            setMenuStatus("已启动,测试中", 0);
+            result.setText("等待结果");
+            result.setForeground(Color.GRAY);
+        } else {
+            setMenuStatus("启动失败,请检查设备后重试", -1);
+            setStartButtonEnabled(true);
+        }
     }
 
     public static int userLoginHours;//用户登录所处小时
@@ -629,12 +761,23 @@ public class MesClient extends JFrame {
     }
 
     public static void scanBarcode() {
+        doStartTest();
+    }
+
+    /** 扫码按钮:弹窗输入工件码后走与输入框回车相同的校验流程 */
+    public static void scanBarcodeByButton() {
         if (work_status == 1) {
-            loadAteqProgramTimes();
-            MesClient.serialPortUtils.start();
+            setMenuStatus("已扫码,请点击启动", 0);
             return;
         }
-        setMenuStatus("请先扫工件码", -1);
+        String scanBarcode = JOptionPane.showInputDialog(mesClientFrame, "请扫工件码");
+        if (scanBarcode == null || scanBarcode.trim().isEmpty()) {
+            setMenuStatus("请扫工件码", -1);
+            return;
+        }
+        product_sn.setText(scanBarcode.trim());
+        scan_type = 1;
+        scanBarcode2();
     }
 
     /**
@@ -745,7 +888,7 @@ public class MesClient extends JFrame {
             loadAteqProgramTimes();
             if(MesClient.mes_enable){
                 getUser();
-                // 查询工件质量
+                // 查询工件质量(常态停止定时器保持运行,待质量校验成功 UD 后再停)
                 Boolean sendret = DataUtil.checkQuality(nettyClient,scanBarcode,user20);
                 if(!sendret){
                     MesClient.setMenuStatus("消息发送失败,请重试",-1);
@@ -756,9 +899,7 @@ public class MesClient extends JFrame {
                 MesClient.tjFlag = 1;
                 MesClient.setMenuStatus("扫码成功,等待测试",0);
                 product_sn.setEditable(false);
-                if(MesClient.configParam.getStartMode().equals("自动启动")){
-                    MesClient.serialPortUtils.start();
-                }
+                MesClient.onScanValidated();
                 MesClient.result.setText("等待结果");
                 MesClient.result.setForeground(Color.GRAY);
             }
@@ -822,15 +963,9 @@ public class MesClient extends JFrame {
             mesClientFrame.repaint();
 
             work_status = 1;
-//            f_scan_data_bt_1.setEnabled(false);
             MesClient.tjFlag = 1;
             MesClient.setMenuStatus("扫码成功,等待测试",0);
-
-            if(MesClient.configParam.getStartMode().equals("自动启动")){
-                MesClient.serialPortUtils.start();
-            }
-            f_scan_data_bt_1.setIcon(null);
-            f_scan_data_bt_1.setText("启动");
+            MesClient.onScanValidated();
 
         }else {
             JOptionPane.showMessageDialog(mesClientFrame,"请扫工件码","提示窗口", JOptionPane.INFORMATION_MESSAGE);
@@ -1001,30 +1136,47 @@ public class MesClient extends JFrame {
 
         product_sn = new JTextField();
         product_sn.setHorizontalAlignment(SwingConstants.LEFT);
-        product_sn.setEditable(true);
-        product_sn.setFont(new Font("微软雅黑", Font.PLAIN, 28));
-        product_sn.setBounds(81, 70, 602, 70);
+        product_sn.setEditable(false);
+        product_sn.setFocusable(false);
+        product_sn.setFont(new Font("微软雅黑", Font.PLAIN, 24));
+        // 尺寸不变:宽550;与按钮统一间距 18px
+        product_sn.setBounds(50, 70, 550, 70);
         indexPanelA.add(product_sn);
         product_sn.setColumns(10);
-        product_sn.addActionListener(new ActionListener() {
+
+        f_scan_data_bt_1 = new JButton("扫码");
+        f_scan_data_bt_1.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 scan_type = 1;
-                scanBarcode2();
+                scanBarcodeByButton();
             }
         });
+        f_scan_data_bt_1.setFont(new Font("微软雅黑", Font.PLAIN, 28));
+        f_scan_data_bt_1.setBounds(618, 70, 120, 70);
+        indexPanelA.add(f_scan_data_bt_1);
 
-//        f_scan_data_bt_1 = new JButton("扫码");
-        f_scan_data_bt_1 = new JButton("启动");
-        f_scan_data_bt_1.addActionListener(new ActionListener() {
+        f_start_bt = new JButton("启动");
+        f_start_bt.setEnabled(false);
+        f_start_bt.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
-                scan_type = 1;
-                scanBarcode();
+                doStartTest();
             }
         });
-//        f_scan_data_bt_1.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
-        f_scan_data_bt_1.setFont(new Font("微软雅黑", Font.PLAIN, 32));
-        f_scan_data_bt_1.setBounds(693, 70, 198, 70);
-        indexPanelA.add(f_scan_data_bt_1);
+        f_start_bt.setFont(new Font("微软雅黑", Font.PLAIN, 28));
+        f_start_bt.setBounds(756, 70, 120, 70);
+        indexPanelA.add(f_start_bt);
+
+        f_retest_bt = new JButton("重测");
+        f_retest_bt.setEnabled(false);
+        f_retest_bt.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                retestLastSn();
+            }
+        });
+        f_retest_bt.setFont(new Font("微软雅黑", Font.PLAIN, 28));
+        f_retest_bt.setBounds(756, 148, 120, 70);
+        indexPanelA.add(f_retest_bt);
+        loadLastSubmittedSn();
         
         JLabel lblNewLabel = new JLabel("测试压");
         lblNewLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));

+ 8 - 3
src/com/mes/ui/MesRevice.java

@@ -3,6 +3,7 @@ package com.mes.ui;
 import com.mes.netty.ProtocolParam;
 import com.mes.component.MyDialog;
 import com.mes.util.ErrorMsg;
+import com.mes.util.WorkTimer;
 
 import javax.swing.*;
 import java.awt.*;
@@ -16,16 +17,20 @@ public class MesRevice {
                 MesClient.check_quality_result = true;//质量合格,可以绑定加工
                 MesClient.work_status = 1;
                 MesClient.tjFlag = 1;
+                // 工件码质量校验成功:停止常态停止/复位定时器
+                WorkTimer.stopStopTimer();
                 MesClient.setMenuStatus("该工件可以测试,等待测试",0);
                 MesClient.product_sn.setEditable(false);
                 MesClient.loadAteqProgramTimes();
-                if(MesClient.configParam.getStartMode().equals("自动启动")){
-                    MesClient.serialPortUtils.start();
-                }
+                MesClient.onScanValidated();
                 MesClient.result.setText("等待结果");
                 MesClient.result.setForeground(Color.GRAY);
             }else {
                 MesClient.check_quality_result = false;
+                MesClient.setStartButtonEnabled(false);
+                if (MesClient.serialPortUtils != null && MesClient.serialPortUtils.isOpen) {
+                    WorkTimer.startStopTimer();
+                }
                 String lmsg = ErrorMsg.getErrorMsg(processMsgRet, ProtocolParam.getLx(mes_msg));
                 MesClient.setMenuStatus(lmsg,-1);
             }

+ 130 - 10
src/com/mes/util/ATEQ.java

@@ -80,16 +80,99 @@ public class ATEQ {
     }
 
     /**
-     * 读取测试状态字 0x2204(1个字)
-     * 高字节: 00=测试中, 21=OK, 其他=各类NG
-     * 读法: 先 10 30 04 选程序,再 03 22 04
+     * 读取实时测试状态字 0x2204(完整 1 字)
+     * 手册: bit5=周期结束, bit0=pass(最终合格请用 Last Time 2303,勿单独用本寄存器定结果)
+     * 读法: 先 10 30 04 选程序,再 03 22 04 00 01
      */
     public static int readTestStatusCode() {
         byte[] read = selectThenReadHolding("22 04", 1);
-        if (read == null || read.length < 7) {
+        if (read == null || read.length < 5) {
             throw new RuntimeException("读取测试状态时, 返回的数据长度不够, hex=" + DataUtils.bytesToHex(read == null ? new byte[0] : read));
         }
-        return read[3] & 0xFF;
+        // 例: 应答 01 03 02 21 80 → 状态字 0x8021(小端)
+        return DataUtils.bytesToInt(Arrays.copyOfRange(read, 3, 5));
+    }
+
+    /**
+     * 读取 Last Time 最终测试状态 0x2303(ok/NG/alarm)
+     * 命令: 站号 03 23 03 00 01 + CRC;bit0=1 合格
+     */
+    public static int readLastTimeStatusWord() {
+        byte[] read = selectThenReadHolding("23 03", 1);
+        if (read == null || read.length < 5) {
+            throw new RuntimeException("读取最终状态2303失败, hex=" + DataUtils.bytesToHex(read == null ? new byte[0] : read));
+        }
+        int word = DataUtils.bytesToInt(Arrays.copyOfRange(read, 3, 5));
+        log.info("LastTime 2303 状态字=0x{}, bit0(合格)={}, 原始={}",
+                String.format("%04X", word), DataUtils.getBit(word, 0), DataUtils.bytesToHex(read));
+        return word;
+    }
+
+    /** Last Time 压力 2305-2306 */
+    public static double readLastTimePressure() {
+        return parseDwordValue(selectThenReadHolding("23 05", 2));
+    }
+
+    /** Last Time 压力单位 2307-2308 */
+    public static String readLastTimePressureUnit() {
+        return parseUnit(selectThenReadHolding("23 07", 2));
+    }
+
+    /** Last Time 泄漏 2309-230A */
+    public static double readLastTimeLeak() {
+        return parseDwordValue(selectThenReadHolding("23 09", 2));
+    }
+
+    /** Last Time 泄漏单位 230B-230C */
+    public static String readLastTimeLeakUnit() {
+        return parseUnit(selectThenReadHolding("23 0B", 2));
+    }
+
+    /**
+     * 测完后读取 Last Time 最终结果(2303 判 OK/NG,2305/2309 压力泄漏)
+     */
+    public static FinalResult readLastTimeFinalResult() {
+        int statusWord = 0;
+        boolean isPass = false;
+        double pressureValue = 0;
+        String pressureUnit = "";
+        double leakValue = 0;
+        String leakUnit = "";
+
+        try {
+            statusWord = readLastTimeStatusWord();
+            isPass = DataUtils.getBit(statusWord, 0) == 1;
+        } catch (Exception e) {
+            log.error("读最终状态2303失败: {}", e.getMessage());
+        }
+        sleepQuiet(50);
+        try {
+            pressureValue = readLastTimePressure();
+        } catch (Exception e) {
+            log.error("读最终压力2305失败: {}", e.getMessage());
+        }
+        sleepQuiet(50);
+        try {
+            pressureUnit = readLastTimePressureUnit();
+        } catch (Exception e) {
+            log.error("读最终压力单位2307失败: {}", e.getMessage());
+        }
+        sleepQuiet(50);
+        try {
+            leakValue = readLastTimeLeak();
+        } catch (Exception e) {
+            log.error("读最终泄漏2309失败: {}", e.getMessage());
+        }
+        sleepQuiet(50);
+        try {
+            leakUnit = readLastTimeLeakUnit();
+        } catch (Exception e) {
+            log.error("读最终泄漏单位230B失败: {}", e.getMessage());
+        }
+
+        FinalResult finalResult = new FinalResult(statusWord, isPass, pressureValue, pressureUnit, leakValue, leakUnit);
+        log.info(finalResult.toString());
+        return finalResult;
     }
 
     /** 压力 0x2206:先 10 30 04,再 03 22 06 00 02 */
@@ -237,12 +320,17 @@ public class ATEQ {
     }
 
     public static class MeasureResult {
-        public final int statusCode;       // 状态高字节: 00测试中, 21=OK, 其他=NG
+        /** 2204 完整状态字(如 0x8021) */
+        public final int statusCode;
         public final Double pressureValue;
         public final String pressureUnit;
         public final Double leakValue;
         public final String leakUnit;
+        /** bit5=0:测试进行中 */
         public final boolean isTesting;
+        /** bit5=1:周期结束(最终 OK/NG 请读 2303) */
+        public final boolean isCycleEnd;
+        /** 2204 的 bit0,仅参考,收尾以 FinalResult 为准 */
         public final boolean isPass;
 
         public MeasureResult(int statusCode, Double pressureValue, String pressureUnit,
@@ -252,22 +340,54 @@ public class ATEQ {
             this.pressureUnit = pressureUnit != null ? pressureUnit : "";
             this.leakValue = leakValue;
             this.leakUnit = leakUnit != null ? leakUnit : "";
-            this.isTesting = statusCode == 0x00;
-            this.isPass = statusCode == 0x21;
+            this.isCycleEnd = DataUtils.getBit(statusCode, 5) == 1;
+            this.isTesting = !this.isCycleEnd;
+            this.isPass = DataUtils.getBit(statusCode, 0) == 1;
         }
 
         @Override
         public String toString() {
             return "测量结果{" +
-                    " 状态码: 0x" + String.format("%02X", statusCode) +
+                    " 状态字: 0x" + String.format("%04X", statusCode) +
                     ", 测试中: " + isTesting +
-                    ", 通过: " + isPass +
+                    ", 周期结束: " + isCycleEnd +
+                    ", bit0参考合格: " + isPass +
                     ", 压力值: " + pressureValue + pressureUnit +
                     ", 泄漏值: " + leakValue + leakUnit +
                     '}';
         }
     }
 
+    /** Last Time(23xx)最终结果 */
+    public static class FinalResult {
+        public final int statusWord;       // 2303
+        public final boolean isPass;       // bit0
+        public final Double pressureValue;
+        public final String pressureUnit;
+        public final Double leakValue;
+        public final String leakUnit;
+
+        public FinalResult(int statusWord, boolean isPass, Double pressureValue, String pressureUnit,
+                           Double leakValue, String leakUnit) {
+            this.statusWord = statusWord;
+            this.isPass = isPass;
+            this.pressureValue = pressureValue;
+            this.pressureUnit = pressureUnit != null ? pressureUnit : "";
+            this.leakValue = leakValue;
+            this.leakUnit = leakUnit != null ? leakUnit : "";
+        }
+
+        @Override
+        public String toString() {
+            return "最终结果LastTime{" +
+                    " 2303=0x" + String.format("%04X", statusWord) +
+                    ", 通过: " + isPass +
+                    ", 压力: " + pressureValue + pressureUnit +
+                    ", 泄漏: " + leakValue + leakUnit +
+                    '}';
+        }
+    }
+
     // 读取实时结果(旧协议,保留备用)
     public static RealtimeResult readRealtimeResult() {
         String s1 = DataUtils.CRC16(ATEQ.getStationNo()+" 03 00 30 00 0D");

+ 3 - 0
src/com/mes/util/SerialPortUtils.java

@@ -167,6 +167,7 @@ public class SerialPortUtils {
                                                         String testDate = DateLocalUtils.getCurrentTime();
                                                         testParam.setCreateTime(testDate);
                                                         JdbcUtils.insertTestRecord(testParam);
+                                                        MesClient.saveLastSubmittedSn(testParam.getSn());
 
                                                         if(MesClient.mes_enable){ // MES开启
                                                             JSONObject retObj = DataUtil.qmResultData(testParam);
@@ -237,6 +238,7 @@ public class SerialPortUtils {
                     });
 
                     WorkTimer.start();
+                    WorkTimer.startStopTimer();
                 }
             }
         }catch (Exception e) {
@@ -266,6 +268,7 @@ public class SerialPortUtils {
     }
 
     public boolean closePort() {
+        WorkTimer.stopStopTimer();
         if (serialPort != null && serialPort.isOpen()) {
             if (serialPort.closePort()) {
                 log.info("串口已关闭.");