chefz 3 dagar sedan
förälder
incheckning
5f1a418b39

+ 6 - 1
.classpath

@@ -10,7 +10,12 @@
 	<classpathentry kind="src" path="src/resources"/>
 	<classpathentry kind="output" path="bin"/>
 	<classpathentry kind="lib" path="lib/s7connector-2.1.jar"/>
-	<classpathentry kind="lib" path="lib/slf4j-api-2.0.6.jar"/>
+	<classpathentry kind="lib" path="lib/slf4j-api-1.7.36.jar"/>
+	<classpathentry kind="lib" path="lib/logback-classic-1.2.13.jar"/>
+	<classpathentry kind="lib" path="lib/logback-core-1.2.13.jar"/>
+	<classpathentry kind="lib" path="lib/iot-communication-1.4.4.jar"/>
+	<classpathentry kind="lib" path="lib/core-3.5.1.jar"/>
+	<classpathentry kind="lib" path="lib/javase-3.5.1.jar"/>
 	<classpathentry kind="lib" path="lib/okhttp-4.10.0.jar"/>
 	<classpathentry kind="lib" path="lib/kotlin-stdlib-1.8.10.jar"/>
 	<classpathentry kind="lib" path="lib/okio-jvm-3.3.0.jar"/>

+ 169 - 29
src/com/mes/component/WorkJPanelDevice.java

@@ -138,6 +138,8 @@ public class WorkJPanelDevice extends JPanel {
     private static final String OP190_TEST_SN = "000020022144-0100101426053100009";
     /** OP190 拉铆打满后是否已触发完工提交,防止重复提交 */
     private volatile boolean op190RivetingComplete = false;
+    /** OP190 质量结果是否已异步提交(拉铆完成时提交,下料时不再重复) */
+    private volatile boolean op190QualitySubmitted = false;
 
     public int aMax = 16; // A枪预设总数
     public int sortA = 0; // A枪当前已读取的数量
@@ -296,8 +298,8 @@ public class WorkJPanelDevice extends JPanel {
 
         Font rowLabelFont = new Font("微软雅黑", Font.PLAIN, 20);
         Font rowFieldFont = new Font("微软雅黑", Font.BOLD, 20);
-        int actionW = 88;
-        int fieldW = contentW - labelW - actionW - gap;
+        int actionW = 80;
+        int fieldW = contentW - labelW - actionW * 2 - gap * 2;
 
         JLabel feedingLabel = new JLabel("工件码");
         feedingLabel.setHorizontalAlignment(SwingConstants.CENTER);
@@ -318,9 +320,19 @@ public class WorkJPanelDevice extends JPanel {
         });
         this.add(productSnFeeding);
 
+        scanBtn = new JButton("读码");
+        scanBtn.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        scanBtn.setBounds(contentX + labelW + fieldW + gap, y, actionW, rowH);
+        scanBtn.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                manualBarCode();
+            }
+        });
+        this.add(scanBtn);
+
         resetFeedingBtn = new JButton("重置");
         resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, 18));
-        resetFeedingBtn.setBounds(contentX + labelW + fieldW + gap, y, actionW, rowH);
+        resetFeedingBtn.setBounds(contentX + labelW + fieldW + gap * 2 + actionW, y, actionW, rowH);
         resetFeedingBtn.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 int choice = JOptionPane.showConfirmDialog(null, "确定刷新上料工件吗?", "操作确认",
@@ -547,9 +559,11 @@ public class WorkJPanelDevice extends JPanel {
         int rowH = (int)(40 * scale);
         int labelW = controlWidth / 5;
         int fieldX = labelW;
-        int fieldW = controlWidth * 3 / 5;
-        int btnX = controlWidth * 4 / 5 + (int)(10 * scale);
-        int btnW = controlWidth / 5 - (int)(10 * scale);
+        int fieldW = controlWidth * 2 / 5;
+        int btnGap = (int)(5 * scale);
+        int btnW = controlWidth / 5 - btnGap;
+        int scanBtnX = controlWidth * 3 / 5 + btnGap;
+        int resetBtnX = controlWidth * 4 / 5 + btnGap;
         Font rowLabelFont = new Font("微软雅黑", Font.PLAIN, (int)(20 * scale));
         Font rowFieldFont = new Font("微软雅黑", Font.PLAIN, (int)(16 * scale));
 
@@ -576,6 +590,16 @@ public class WorkJPanelDevice extends JPanel {
         });
         this.add(productSnFeeding);
 
+        scanBtn = new JButton("读码");
+        scanBtn.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                manualBarCode();
+            }
+        });
+        scanBtn.setFont(new Font("微软雅黑", Font.PLAIN, (int)(18 * scale)));
+        scanBtn.setBounds(scanBtnX, row1Y, btnW, rowH);
+        this.add(scanBtn);
+
         resetFeedingBtn = new JButton("刷新");
         resetFeedingBtn.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
@@ -587,8 +611,8 @@ public class WorkJPanelDevice extends JPanel {
                 }
             }
         });
-        resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, (int)(22 * scale)));
-        resetFeedingBtn.setBounds(btnX, row1Y, btnW, rowH);
+        resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, (int)(18 * scale)));
+        resetFeedingBtn.setBounds(resetBtnX, row1Y, btnW, rowH);
         this.add(resetFeedingBtn);
 
         productSn = new JTextField("");
@@ -878,11 +902,12 @@ public class WorkJPanelDevice extends JPanel {
             }
         }
         
-        // 当完成数量达到预设时,自动关闭拉铆机电源;下料请求与反馈与其它工序一致,由 oprnoTimer 处理
+        // 当完成数量达到预设时:关电源 + OP190 立即异步提交质量;下料信号仅做 MES 反馈
         if (cur == aMax) {
             powerOffRiveting();
-            if (oprno.equals("OP190")) {
+            if (oprno.equals("OP190") && !op190RivetingComplete) {
                 op190RivetingComplete = true;
+                submitOp190QualityAsync();
             }
         } else if (bindLbStatus && tjFeedingFlag >= 1 && sortA < aMax) {
             // 绑定冷板后,若拉铆机意外停机则尝试恢复
@@ -897,7 +922,30 @@ public class WorkJPanelDevice extends JPanel {
         }
     }
 
-    /** OP190 测试:拉铆完成数直接变为预设值,等待下料请求后走统一提交流程 */
+    /**
+     * OP190:拉铆完成后立即异步提交质量结果,不阻塞等待下料信号。
+     */
+    private void submitOp190QualityAsync() {
+        if (op190QualitySubmitted) {
+            return;
+        }
+        final String sn = productSnFeeding.getText() == null
+                ? "" : productSnFeeding.getText().trim();
+        if (sn.isEmpty()) {
+            setMenuStatus("拉铆完成但工件码为空,无法提交", -1);
+            log.warn("OP190 拉铆完成但工件码为空,跳过提交");
+            return;
+        }
+        op190QualitySubmitted = true;
+        final String station = oprno + pos;
+        final String user = MesClient.user_menu.getText();
+        // 与 readResult() 一致:OP190 暂固定 OK
+        DataUtil.submitQualityAsync(sn, station, "OK", user);
+        setMenuStatus("拉铆完成,结果后台提交中,等待下料", 0);
+        log.info("OP190 拉铆完成,已异步提交质量 sn={}", sn);
+    }
+
+    /** OP190 测试:拉铆完成数直接变为预设值,并立即异步提交质量 */
     private void simulateOp190RivetingComplete() {
         if (op190RivetingComplete) {
             return;
@@ -917,6 +965,7 @@ public class WorkJPanelDevice extends JPanel {
             log.warn("OP190 测试模式:关闭拉铆机电源跳过(无 PLC 时可忽略)", e);
         }
         op190RivetingComplete = true;
+        submitOp190QualityAsync();
     }
 
     public Timer cjTimerText2;
@@ -1063,9 +1112,7 @@ public class WorkJPanelDevice extends JPanel {
                         Boolean retFeedingAllow = readAllowFeeding();
                         feedingmeslabel.setIcon(retFeedingAllow?imageGreen:imageGray);
 
-                        if(!retFeeding && retFeedingAllow){
-                            writeAllowFeeding(false);
-                        }
+                        // 允许上料仅在工位开始工作(tjFeedingFlag 1→2)时清除,不在空闲阶段自动复位
 
                         // 读取下料信号
                         Boolean retBlanking = readBlanking();
@@ -1104,6 +1151,7 @@ public class WorkJPanelDevice extends JPanel {
                                     }
                                 } else {
                                     tjFeedingFlag = 2;
+                                    writeAllowFeeding(false); // 工位开始工作后再清除允许上料
                                     resetFeeding();
                                     setMenuStatus("工件加工中", 0);
                                     getToolLife();
@@ -1119,21 +1167,39 @@ public class WorkJPanelDevice extends JPanel {
                                 tjStatus = 1;
                                 writeAllowBlanking(true);
                                 blankingmeslabel.setIcon(imageGreen);
-                                final String sn = productSnFeeding.getText() == null
-                                        ? "" : productSnFeeding.getText().trim();
-                                final String station = oprno + pos;
-                                final String user = MesClient.user_menu.getText();
-                                final String result = "OK".equals(readResult()) ? "OK" : "NG";
-                                final boolean queued = !sn.isEmpty();
-                                if (queued) {
-                                    DataUtil.submitQualityAsync(sn, station, result, user);
-                                }
-                                tjFeedingFlag = 3;
-                                resetBlanking();
-                                if (queued) {
-                                    setMenuStatus("已下料,结果后台提交中", 0);
+
+                                if (oprno.equals("OP190")) {
+                                    // OP190:质量已在拉铆完成时异步提交;下料仅反馈。若尚未提交则兜底补交
+                                    if (!op190QualitySubmitted) {
+                                        final String sn = productSnFeeding.getText() == null
+                                                ? "" : productSnFeeding.getText().trim();
+                                        if (!sn.isEmpty()) {
+                                            DataUtil.submitQualityAsync(sn, oprno + pos, "OK",
+                                                    MesClient.user_menu.getText());
+                                            op190QualitySubmitted = true;
+                                            log.warn("OP190 下料时质量尚未提交,已兜底异步提交 sn={}", sn);
+                                        }
+                                    }
+                                    tjFeedingFlag = 3;
+                                    resetBlanking();
+                                    setMenuStatus("已下料,等待下一件", 0);
                                 } else {
-                                    setMenuStatus("工件码为空,无法提交", -1);
+                                    final String sn = productSnFeeding.getText() == null
+                                            ? "" : productSnFeeding.getText().trim();
+                                    final String station = oprno + pos;
+                                    final String user = MesClient.user_menu.getText();
+                                    final String result = "OK".equals(readResult()) ? "OK" : "NG";
+                                    final boolean queued = !sn.isEmpty();
+                                    if (queued) {
+                                        DataUtil.submitQualityAsync(sn, station, result, user);
+                                    }
+                                    tjFeedingFlag = 3;
+                                    resetBlanking();
+                                    if (queued) {
+                                        setMenuStatus("已下料,结果后台提交中", 0);
+                                    } else {
+                                        setMenuStatus("工件码为空,无法提交", -1);
+                                    }
                                 }
                             }
                         }
@@ -1192,10 +1258,84 @@ public class WorkJPanelDevice extends JPanel {
         }
     }
 
+    /**
+     * 手动从 PLC 读码(不依赖“允许MES读码”信号)
+     */
+    public void manualBarCode() {
+        if (MesClient.isLogin != 1) {
+            setMenuStatus("请先登录", -1);
+            return;
+        }
+        if (!productSnFeeding.getText().trim().isEmpty()) {
+            int choice = JOptionPane.showConfirmDialog(null,
+                    "当前已有工件码,确定重新从PLC读码吗?",
+                    "操作确认",
+                    JOptionPane.YES_NO_OPTION,
+                    JOptionPane.QUESTION_MESSAGE);
+            if (choice != JOptionPane.YES_OPTION) {
+                return;
+            }
+            log.info("人工手动读码,清空原工件码:" + productSnFeeding.getText().trim());
+            productSnFeeding.setText("");
+            tjFeedingFlag = 0;
+            writeAllowFeeding(false);
+            if (oprno.equals("OP190")) {
+                resetLbBinding();
+            }
+        }
+        log.info("人工点击手动读码:" + oprno + pos);
+        setMenuStatus("正在从PLC读码...", 0);
+        barCode();
+    }
+
+    /**
+     * 手动提交当前工件质量结果(异步)。有工件码才提交。
+     * @return true 已入队提交
+     */
+    public boolean manualSubmitQuality() {
+        if (MesClient.isLogin != 1) {
+            setMenuStatus("请先登录", -1);
+            return false;
+        }
+        final String sn = productSnFeeding.getText() == null
+                ? "" : productSnFeeding.getText().trim();
+        if (sn.isEmpty()) {
+            return false;
+        }
+        if (oprno.equals("OP190") && op190QualitySubmitted) {
+            int choice = JOptionPane.showConfirmDialog(null,
+                    oprno + pos + " 已提交过,确定再次提交吗?\n" + sn,
+                    "手动提交确认",
+                    JOptionPane.YES_NO_OPTION,
+                    JOptionPane.QUESTION_MESSAGE);
+            if (choice != JOptionPane.YES_OPTION) {
+                return false;
+            }
+        }
+        final String station = oprno + pos;
+        final String user = MesClient.user_menu.getText();
+        final String result = oprno.equals("OP190")
+                ? "OK"
+                : ("OK".equals(readResult()) ? "OK" : "NG");
+        DataUtil.submitQualityAsync(sn, station, result, user);
+        if (oprno.equals("OP190")) {
+            op190QualitySubmitted = true;
+        }
+        writeAllowBlanking(true);
+        if (blankingmeslabel != null) {
+            blankingmeslabel.setIcon(imageGreen);
+        }
+        resetBlanking();
+        setMenuStatus("手动提交已入队,已给下料反馈", 0);
+        log.info("人工手动提交质量并给下料反馈 sn={}, station={}, result={}", sn, station, result);
+        return true;
+    }
+
     public void resetBlanking() {
         tjFeedingFlag = 0;
         tjStatus = 0;
         op190RivetingComplete = false;
+        op190QualitySubmitted = false;
         productSnFeeding.setText("");
         resetLbBinding();
         setMenuStatus("工件结果已提交,等待下一件",0);
@@ -1233,7 +1373,6 @@ public class WorkJPanelDevice extends JPanel {
 //        tjFeedingFlag = 0;
 //        setMenuStatus("等待请求扫码",0);
         resetFeedingBtn.setEnabled(true);
-        writeAllowFeeding(false);
     }
 
     public void resetScanA() {
@@ -1241,6 +1380,7 @@ public class WorkJPanelDevice extends JPanel {
         tjFlag = 0;
         tjStatus = 0;
         op190RivetingComplete = false;
+        op190QualitySubmitted = false;
 //        productSn.setText("");
         productSnFeeding.setText("");
         resetLbBinding();

+ 64 - 0
src/com/mes/ui/MesClient.java

@@ -149,6 +149,60 @@ public class MesClient extends JFrame {
         }, 5000, 5000);
     }
 
+    /** 设置菜单:手动提交当前有工件码的工位质量结果 */
+    public static void manualSubmitQuality() {
+        if (isLogin != 1) {
+            JOptionPane.showMessageDialog(mesClientFrame, "请先登录", "提示", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        WorkJPanelDevice[] devices = new WorkJPanelDevice[]{
+                workJPanelDeviceA, workJPanelDeviceB, workJPanelDeviceC, workJPanelDeviceD,
+                workJPanelDeviceE, workJPanelDeviceF, workJPanelDeviceG
+        };
+        StringBuilder preview = new StringBuilder();
+        int count = 0;
+        for (WorkJPanelDevice device : devices) {
+            if (device == null || device.productSnFeeding == null) {
+                continue;
+            }
+            String sn = device.productSnFeeding.getText() == null
+                    ? "" : device.productSnFeeding.getText().trim();
+            if (sn.isEmpty()) {
+                continue;
+            }
+            count++;
+            preview.append(device.oprno).append(device.pos)
+                    .append("  ").append(sn).append("\n");
+        }
+        if (count == 0) {
+            JOptionPane.showMessageDialog(mesClientFrame, "当前没有可提交的工件码", "提示",
+                    JOptionPane.INFORMATION_MESSAGE);
+            return;
+        }
+        int choice = JOptionPane.showConfirmDialog(mesClientFrame,
+                "确定手动提交以下工位质量结果,并给 MES 下料反馈?\n\n" + preview,
+                "手动提交",
+                JOptionPane.YES_NO_OPTION,
+                JOptionPane.QUESTION_MESSAGE);
+        if (choice != JOptionPane.YES_OPTION) {
+            return;
+        }
+        int ok = 0;
+        for (WorkJPanelDevice device : devices) {
+            if (device == null) {
+                continue;
+            }
+            if (device.manualSubmitQuality()) {
+                ok++;
+            }
+        }
+        log.info("人工点击手动提交,成功入队 {} 个工位", ok);
+        JOptionPane.showMessageDialog(mesClientFrame,
+                "已提交 " + ok + " 个工位,已给下料反馈,结果后台上传中",
+                "手动提交",
+                JOptionPane.INFORMATION_MESSAGE);
+    }
+
     public static void upParams() {
         if(cjTimer3!=null) {
             cjTimer3.cancel();
@@ -285,6 +339,16 @@ public class MesClient extends JFrame {
         settingMenu.setFont(new Font("微软雅黑", Font.PLAIN, 20));
         menuBar.add(settingMenu);
 
+        JMenuItem manualSubmitItem = new JMenuItem("手动提交");
+        manualSubmitItem.setIcon(new ImageIcon(MesClient.class.getResource("/bg/save_bg.png")));
+        manualSubmitItem.setFont(new Font("微软雅黑", Font.PLAIN, 22));
+        settingMenu.add(manualSubmitItem);
+        manualSubmitItem.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                manualSubmitQuality();
+            }
+        });
+
         contentPane = new JPanel();
         contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
         setContentPane(contentPane);

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

@@ -1,5 +1,5 @@
 #mes.server_ip=127.0.0.1
 mes.server_ip=192.168.15.99
 mes.line_sn=XT
-mes.gw=OP190
+mes.gw=OP140
 mes.line_pos=west