浏览代码

feat: OP080 dual-side (A/B) rework and infra refactor

- Switch workstation to OP080; submit per side as OP080A/OP080B
- Force-finish and report on PLC run timeout (mes.run_timeout_min)
- Cache welding samples in local SQLite, sync on timer and after submit
- Refactor config loading (ConfigLoader) and console encoding (EncodingUtil)
- Bump slf4j-api to 1.7.36, add logback 1.2.13
chefz 1 天之前
父节点
当前提交
2b15acb3d6

+ 3 - 1
.classpath

@@ -10,7 +10,9 @@
 	<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/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"/>

二进制
lib/slf4j-api-2.0.6.jar


+ 31 - 0
sources.txt

@@ -0,0 +1,31 @@
+src\com\mes\component\CallFromJs.java
+src\com\mes\component\MesRadio.java
+src\com\mes\component\MesWebView.java
+src\com\mes\component\MyDialog.java
+src\com\mes\netty\MesMsgUtils.java
+src\com\mes\netty\NettyClient.java
+src\com\mes\netty\NettyClientHandler.java
+src\com\mes\netty\ProtocolParam.java
+src\com\mes\netty\XDecoder.java
+src\com\mes\ui\BindMaterialResp.java
+src\com\mes\ui\CmtReq.java
+src\com\mes\ui\DataUtil.java
+src\com\mes\ui\ErrorMsg.java
+src\com\mes\ui\LoginFarme.java
+src\com\mes\ui\MesClient.java
+src\com\mes\ui\MesRevice.java
+src\com\mes\ui\ModbusRtu.java
+src\com\mes\ui\OprnoUtil.java
+src\com\mes\ui\PlcUtil.java
+src\com\mes\ui\ProdReq.java
+src\com\mes\ui\TableCellEditorButton.java
+src\com\mes\ui\TableCellRendererButton.java
+src\com\mes\ui\Test.java
+src\com\mes\ui\TextMain.java
+src\com\mes\util\Base64Utils.java
+src\com\mes\util\ConfigLoader.java
+src\com\mes\util\DateLocalUtils.java
+src\com\mes\util\EncodingUtil.java
+src\com\mes\util\ErrorMsg.java
+src\com\mes\util\HttpUtils.java
+src\com\mes\util\JdbcUtils.java

+ 12 - 1
src/com/mes/component/MesWebView.java

@@ -15,7 +15,10 @@ public class MesWebView extends JFXPanel {
 
     public static WebEngine engine = null;
 
+    private final String pageUrl;
+
     public MesWebView(String url){
+        this.pageUrl = url;
 
         Platform.runLater(() -> {
             WebView webView = new WebView();
@@ -39,6 +42,14 @@ public class MesWebView extends JFXPanel {
     }
 
     public void reloadWeb(){
-        engine.reload();
+        loadPageUrl(pageUrl);
+    }
+
+    public void loadPageUrl(String url){
+        Platform.runLater(() -> {
+            if (engine != null) {
+                engine.load(url);
+            }
+        });
     }
 }

+ 66 - 46
src/com/mes/ui/DataUtil.java

@@ -2,6 +2,7 @@ package com.mes.ui;
 
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.netty.NettyClient;
+import com.mes.util.ConfigLoader;
 import com.mes.util.DateLocalUtils;
 import com.mes.util.JdbcUtils;
 import org.slf4j.Logger;
@@ -11,6 +12,7 @@ import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLEncoder;
 import java.util.Properties;
 
 public class DataUtil {
@@ -69,6 +71,41 @@ public class DataUtil {
         }
     }
 
+    /** HTTP pccheck:获取含具体工位号的质量检查提示(QD/QN 等) */
+    public static JSONObject pcCheckQuality(String sn, String user, String side) {
+        try {
+            String oprno = MesClient.resolveOprnoForSubmit(side != null ? side : "");
+            String lineSn = MesClient.mes_line_sn == null ? "" : MesClient.mes_line_sn.trim();
+            String url = "http://" + MesClient.mes_server_ip + ":8980/js/a/mes/mesProductRecord/pccheck";
+            String params = "__ajax=json"
+                    + "&oprno=" + URLEncoder.encode(oprno, "UTF-8")
+                    + "&lineSn=" + URLEncoder.encode(lineSn, "UTF-8")
+                    + "&sn=" + URLEncoder.encode(sn != null ? sn.trim() : "", "UTF-8")
+                    + "&ucode=" + URLEncoder.encode(user != null ? user.trim() : "", "UTF-8");
+            log.info("pccheck params={}", params);
+            String result = doPost(url, params);
+            log.info("pccheck result={}", result);
+            if (result == null || result.trim().isEmpty() || result.equalsIgnoreCase("false")) {
+                return null;
+            }
+            return JSONObject.parseObject(result);
+        } catch (Exception e) {
+            log.error("pccheck error", e);
+            return null;
+        }
+    }
+
+    public static String getPcCheckMessage(String sn, String user, String side) {
+        JSONObject ret = pcCheckQuality(sn, user, side);
+        if (ret != null && ret.get("message") != null) {
+            String msg = ret.getString("message");
+            if (msg != null && !msg.trim().isEmpty()) {
+                return msg.trim();
+            }
+        }
+        return null;
+    }
+
     public static Boolean startWork(NettyClient nettyClient,String sn,String user,String op){
         try{
             String msgType = "MKSW";
@@ -159,21 +196,9 @@ public class DataUtil {
 
     public static Boolean sendMessage(NettyClient nettyClient,String msgType,String craft,String lx,String sn,String result,String user,String paramNums,String params,String op){
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
-
-            if(MesClient.mes_gwflag.equals("B")){
-                if(op.equals("A")){
-                    op = "C";
-                }else{
-                    op = "D";
-                }
-            }
+            Properties pro = ConfigLoader.loadProperties();
 
-            String gw = "GW"+rightPad(pro.getProperty("mes.gw")+op, 6);
+            String gw = "GW"+rightPad(pro.getProperty("mes.gw").trim()+op, 6);
             String start = "aaaabbbbbABW";
             String gy = "GY" + rightPad(craft, 6);
             String reslx = "LX" + rightPad(lx, 2);
@@ -207,12 +232,7 @@ public class DataUtil {
 
     public static JSONObject upParams(String upparams) {
         try{
-            String enconding = "UTF-8";    //涓嶇windows杩樻槸linux閮芥槸UTF-8
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            //閬垮厤涓枃涔辩爜
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String url = "http://"+mes_server_ip+":8980/js/a/mes/mesProductCmt/batchsave";
@@ -233,11 +253,7 @@ public class DataUtil {
 
     public static JSONObject getBindMaterail() {
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -247,23 +263,20 @@ public class DataUtil {
             String result = doPost(url,params);
             System.out.println("result="+result);
 
-            if(result.equalsIgnoreCase("false")) {
+            if(result == null || result.equalsIgnoreCase("false")) {
                 return null;
             }else {
                 return JSONObject.parseObject(result);
             }
         }catch (Exception e){
+            log.warn("getBindMaterail failed", e);
             return null;
         }
     }
 
     public static JSONObject saveBindMaterail(String batchSn,String craft,String materialId,String type) {
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -285,11 +298,7 @@ public class DataUtil {
 
     public static JSONObject saveGzCheck(String content,String userCode) {
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -371,11 +380,7 @@ public class DataUtil {
 
     public static JSONObject bindWarehouseData(String sn,String wsn,String userCode,String craft){
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -397,11 +402,7 @@ public class DataUtil {
 
     public static JSONObject unBindWarehouseData(String sn,String wsn,String userCode,String craft){
         try{
-            String enconding = "UTF-8";
-            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-            Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-            pro.load(br);
+            Properties pro = ConfigLoader.loadProperties();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -420,4 +421,23 @@ public class DataUtil {
             return null;
         }
     }
+
+    /** 同步 CMT 导电嘴寿命到 MES(实际数量/设定数量) */
+    public static void syncCmtTipLife(String name, String title, int curLife, int maxLife) {
+        new Thread(() -> {
+            try {
+                Properties pro = ConfigLoader.loadProperties();
+                String mes_server_ip = pro.getProperty("mes.server_ip");
+                String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesToolLife/syncCmtTip";
+                String params = "__ajax=json&name=" + URLEncoder.encode(name, "UTF-8")
+                        + "&title=" + URLEncoder.encode(title == null ? "" : title, "UTF-8")
+                        + "&curLife=" + curLife
+                        + "&maxLife=" + maxLife;
+                String result = doPost(url, params);
+                log.info("syncCmtTipLife name={}, result={}", name, result);
+            } catch (Exception e) {
+                log.warn("syncCmtTipLife failed: {}", e.getMessage());
+            }
+        }, "CmtTipSync").start();
+    }
 }

+ 5 - 4
src/com/mes/ui/LoginFarme.java

@@ -42,21 +42,21 @@ public class LoginFarme extends JFrame {
 
         //登录页面
         userNameLabel.setBounds(300, 100, 120, 40);
-        userNameLabel.setFont(new java.awt.Font("Dialog",   1,   16));
+        userNameLabel.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
         userPasswordLabel.setBounds(300, 150, 120, 40);
-        userPasswordLabel.setFont(new java.awt.Font("Dialog",   1,   16));
+        userPasswordLabel.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
         userNameTxt = new JTextField(20);
         userNameTxt.setText("system");
         userNameTxt.setBounds(400, 105, 150, 30);
         userPasswordTxt = new JPasswordField(20);
         userPasswordTxt.setBounds(400, 155, 150, 30);
         userPasswordTxt.setText("Aa111111");
-        loginButton.setFont(new java.awt.Font("Dialog",   1,   16));
+        loginButton.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
         loginButton.setBounds(300, 200, 255, 40);
         loginButton.setIcon(new ImageIcon(MesClient.class.getResource("/bg/user.png")));
 
 //        scanLoginButton.setVisible(false);
-        scanLoginButton.setFont(new java.awt.Font("Dialog",   1,   16));
+        scanLoginButton.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
         scanLoginButton.setBounds(300, 250, 255, 40);
         scanLoginButton.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
 
@@ -201,6 +201,7 @@ public class LoginFarme extends JFrame {
                     }
 
                     MesClient.initWarehouseData();
+                    MesClient.getMaterailData();
 
                     /*MesClient.product_sn.setText("5024026600851241241999000091");
                     String lmsg = "该工件OP040加工NG";

+ 230 - 53
src/com/mes/ui/MesClient.java

@@ -11,7 +11,9 @@ import com.mes.component.MesRadio;
 import com.mes.component.MesWebView;
 import com.mes.component.MyDialog;
 import com.mes.netty.NettyClient;
+import com.mes.util.ConfigLoader;
 import com.mes.util.DateLocalUtils;
+import com.mes.util.EncodingUtil;
 import com.mes.util.HttpUtils;
 import com.mes.util.JdbcUtils;
 import javafx.embed.swing.JFXPanel;
@@ -20,6 +22,7 @@ import org.slf4j.LoggerFactory;
 
 import javax.swing.*;
 import javax.swing.border.EmptyBorder;
+import javax.swing.plaf.FontUIResource;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import java.awt.*;
@@ -42,6 +45,11 @@ import java.util.TimerTask;
 import java.util.List;
 
 public class MesClient extends JFrame {
+
+    static {
+        EncodingUtil.init();
+    }
+
     public static final Logger log = LoggerFactory.getLogger(MesClient.class);
     private static String Drivde = "org.sqlite.JDBC";
 
@@ -53,7 +61,6 @@ public class MesClient extends JFrame {
     public static int mes_heart_beat_cycle = 10;
     public static int mes_heart_icon_cycle = 1;
     public static String mes_line_sn = "";
-    public static String mes_gwflag = ""; // 工位标识
 
     public static NettyClient nettyClient;
     public static boolean tcp_connect_flag = false;
@@ -112,6 +119,9 @@ public class MesClient extends JFrame {
 
     public static String curPage = "";
 
+    /** 最近一次镭雕扫码所在面(A/B),用于 MES 开工与 PLC 允许信号 */
+    public static String lastScanSide = "A";
+
     public static Integer checkA = 0;
 
     public static Integer checkB = 0;
@@ -136,7 +146,7 @@ public class MesClient extends JFrame {
     public static JPanel indexPanelC;
     public static MesWebView jfxPanel2 = null;
 
-    public static S7PLC s7PLC = new S7PLC(EPlcType.S1200, "192.168.7.223"); //100 B工位
+    public static S7PLC s7PLC = new S7PLC(EPlcType.S1200, "192.168.7.100"); //100 B工位
 
     public static List<String> hjparams = new ArrayList<>();
 
@@ -148,14 +158,13 @@ public class MesClient extends JFrame {
 
 
     public static void main(String[] args) {
+        initUiFont();
         EventQueue.invokeLater(new Runnable() {
             @Override
             public void run() {
                 try{
-                    //鐠囩粯鏋冩禒鍫曞帳缂冿拷
                     readProperty();
 
-                    // 閺勫墽銇氶悾宀勬桨
                     mesClientFrame = new MesClient();
                     mesClientFrame.setVisible(false);
 
@@ -182,6 +191,37 @@ public class MesClient extends JFrame {
         });
     }
 
+    /** 优先使用系统中文字体,避免 jar 部署机器字体名不一致 */
+    private static Font resolveUiFont(int size) {
+        String[] candidates = {"Microsoft YaHei UI", "微软雅黑", "Microsoft YaHei", "SimSun"};
+        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+        String[] available = ge.getAvailableFontFamilyNames();
+        java.util.Set<String> set = new java.util.HashSet<>();
+        for (String name : available) {
+            set.add(name);
+        }
+        for (String name : candidates) {
+            if (set.contains(name)) {
+                return new Font(name, Font.PLAIN, size);
+            }
+        }
+        return new Font(Font.SANS_SERIF, Font.PLAIN, size);
+    }
+
+    /** 全局 UI 字体,避免登录页等使用 Dialog 字体时中文显示异常 */
+    private static void initUiFont() {
+        Font baseFont = resolveUiFont(12);
+        java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
+        while (keys.hasMoreElements()) {
+            Object key = keys.nextElement();
+            Object value = UIManager.get(key);
+            if (value instanceof Font) {
+                Font old = (Font) value;
+                UIManager.put(key, new FontUIResource(baseFont.getName(), old.getStyle(), old.getSize()));
+            }
+        }
+    }
+
     public static Timer cjTimerText;
     public static void textFocus() {
         if(cjTimerText!=null) {
@@ -210,26 +250,91 @@ public class MesClient extends JFrame {
         cjTimer3 = new Timer();
         cjTimer3.schedule(new TimerTask() {
             public void run() {
-                try{
-                    List<CmtReq> prods = JdbcUtils.getCmtParams();
-                    System.out.println("prods:"+ JSON.toJSONString(prods));
-                    if(prods.size() > 0){
-                        // 定时上传参数
-                        JSONObject retObj = DataUtil.upParams(JSON.toJSONString(prods));
-                        if(retObj.get("result")!=null&&retObj.get("result").toString().equalsIgnoreCase("true")) {
-                            // 更新sqlite已同步
-                            for(CmtReq prodReq:prods){
-                                JdbcUtils.updateProdSync(prodReq.getId(),1);
-                            }
-                        }
-                    }
-                }catch (Exception e){
-                    e.printStackTrace();
-                }
+                uploadPendingCmtParams();
             }
         }, 1000,30*1000);
     }
 
+    /** 立即上传本地未同步的焊接采样数据 */
+    public static void uploadPendingCmtParams() {
+        try{
+            List<CmtReq> prods = JdbcUtils.getCmtParams();
+            if(prods.isEmpty()){
+                return;
+            }
+            JSONObject retObj = DataUtil.upParams(JSON.toJSONString(prods));
+            if(retObj != null && retObj.get("result") != null
+                    && retObj.get("result").toString().equalsIgnoreCase("true")) {
+                for(CmtReq prodReq : prods){
+                    JdbcUtils.updateProdSync(prodReq.getId(),1);
+                }
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    /** 焊接报工后:先上传采样数据,再刷新工作记录页 */
+    public static void notifyWorkSubmitted() {
+        uploadPendingCmtParams();
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                refreshWorkRecord();
+            }
+        }, 1000);
+    }
+
+    /** 刷新「工作记录」WebView */
+    public static void refreshWorkRecord() {
+        if(jfxPanel == null || sessionid == null || sessionid.isEmpty()){
+            return;
+        }
+        String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/work?__sid="
+                + sessionid + "&oprno=" + mes_gw + "&lineSn=" + mes_line_sn
+                + "&t=" + System.currentTimeMillis();
+        jfxPanel.loadPageUrl(url);
+    }
+
+    public static String resolveOprnoForSubmit(String op) {
+        return mes_gw + op;
+    }
+
+    /** 根据 MES 返回工位号或最近扫码面,判定 A/B(仅 OP080A / OP080B) */
+    public static String resolveScanSide(String oprno) {
+        if (oprno != null) {
+            String o = oprno.trim();
+            if (o.endsWith("B")) {
+                return "B";
+            }
+            if (o.endsWith("A")) {
+                return "A";
+            }
+        }
+        if ("B".equals(lastScanSide)) {
+            return "B";
+        }
+        if ("B".equals(curPage)) {
+            return "B";
+        }
+        return "A";
+    }
+
+    /** 焊接结束时落库未满 60 条的剩余采样 */
+    public static void flushHjparamsToDb(String oprno, String sn) {
+        if (hjparams == null || hjparams.isEmpty() || sn == null || sn.isEmpty()) {
+            return;
+        }
+        try {
+            JdbcUtils.insertCmtData(oprno, mes_line_sn, sn, JSON.toJSONString(hjparams));
+            hjparams = new ArrayList<>();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static final int TAB_WORK_RECORD = 2;
+
     public static Timer cjTimer;
     public static void getPlcParam() {
         if(cjTimer!=null) {
@@ -254,7 +359,8 @@ public class MesClient extends JFrame {
                     e.printStackTrace();
                 }
             }
-        }, 1000,2000);
+        // 运行中 500ms 轮询,降低漏检 PLC「焊接完成」短脉冲的概率(不改 PLC)
+        }, 1000,500);
     }
 
     public static Timer cjTimer2;
@@ -270,41 +376,37 @@ public class MesClient extends JFrame {
                 }catch (Exception e){
                     e.printStackTrace();
                 }
+                try{
+                    PlcUtil.getTipLife(s7PLC);
+                }catch (Exception e){
+                    e.printStackTrace();
+                }
             }
         }, 1000,500);
     }
 
-    //鐠囧鍘ょ純顔芥瀮娴狅拷
+    //读配置文件
     private static void readProperty() throws IOException{
-        String enconding = "UTF-8";
-        InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
-        Properties pro = new Properties();
-        BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
-        pro.load(br);
-        mes_gw =  pro.getProperty("mes.gw");
+        Properties pro = ConfigLoader.loadProperties();
+        mes_gw = pro.getProperty("mes.gw").trim();
 
 //        mes_gw_des = pro.getProperty("mes.gw_des");
-        mes_server_ip = pro.getProperty("mes.server_ip");
+        mes_server_ip = pro.getProperty("mes.server_ip").trim();
         mes_tcp_port = Integer.parseInt(pro.getProperty("mes.tcp_port"));
         mes_heart_beat_cycle = Integer.parseInt(pro.getProperty("mes.heart_beat_cycle"));
-        mes_line_sn = pro.getProperty("mes.line_sn");
+        mes_line_sn = pro.getProperty("mes.line_sn").trim();
 
         mes_gw_des = OprnoUtil.getGwDes(mes_line_sn,mes_gw);
 
-        mes_gwflag = pro.getProperty("mes.gwflag");
-
         System.out.println(mes_gw + ";" + mes_gw_des + ";" + mes_server_ip + ";" + mes_tcp_port + ";" + mes_heart_beat_cycle);
     }
 
-    // 閸掓繂顫愰崠鏈P
+    // 初始化TCP
     public static void initTcpConnection() {
         try {
             if(nettyClient==null) {
-                //閸掓繂顫愰崠鏈P鏉╃偞甯�
                 nettyClient = new NettyClient();
-                //TCP鏉╃偞甯撮悩鑸碉拷锟�
                 tcp_connect_flag = false;
-                //鐠佸墽鐤員CP鐠囬攱鐪伴悩鑸碉拷锟�
                 connect_request_flag = true;
 
                 DataUtil.synrTcp(nettyClient,mes_gw);
@@ -315,7 +417,6 @@ public class MesClient extends JFrame {
         }
     }
 
-    //閸氼垰濮╄箛鍐儲閸栧懐鈻兼惔锟�
     public static java.util.Timer heartBeatTimer;
     public static java.util.Timer heartBeatIconTimer;
     public static boolean iconREDFlag = true;
@@ -324,6 +425,18 @@ public class MesClient extends JFrame {
     public static JTextField param3;
     public static JTextField param4;
     public static JTextField param5;
+    public static JTextField tipLifeA;
+    public static JTextField tipLifeB;
+    public static JTextField tipLifeC;
+    public static volatile boolean tipAlarmAShown = false;
+    public static volatile boolean tipAlarmBShown = false;
+    public static volatile boolean tipAlarmCShown = false;
+    public static int lastTipSyncAActual = Integer.MIN_VALUE;
+    public static int lastTipSyncASet = Integer.MIN_VALUE;
+    public static int lastTipSyncBActual = Integer.MIN_VALUE;
+    public static int lastTipSyncBSet = Integer.MIN_VALUE;
+    public static int lastTipSyncCActual = Integer.MIN_VALUE;
+    public static int lastTipSyncCSet = Integer.MIN_VALUE;
     public static JTextField product_sn2;
     public static JTextField param21;
     public static JTextField param22;
@@ -353,7 +466,6 @@ public class MesClient extends JFrame {
         heartBeatTimer.schedule(new TimerTask() {
             public void run() {
                 if(nettyClient!=null&&tcp_connect_flag) {
-                    //System.out.println("閸欐垿锟戒礁绺剧捄铏Г閺傦拷");
                     DataUtil.heartBeat(nettyClient,mes_gw);
 
                     heart_beat_menu.setText(DateLocalUtils.getCurrentTime());
@@ -361,7 +473,6 @@ public class MesClient extends JFrame {
             }
         }, 100,mes_heart_beat_cycle*1000);
 
-        //韫囧啳鐑﹂弰鍓с仛閸ョ偓鐖�
         if(heartBeatIconTimer!=null) {
             heartBeatIconTimer.cancel();
         }
@@ -383,13 +494,9 @@ public class MesClient extends JFrame {
                     heart_beat_menu.setText(DateLocalUtils.getCurrentTime());
                     heart_beat_menu.repaint();
 
-                    //閼汇儲婀潻鐐村复閸掓瑥鐨剧拠鏇$箾閹猴拷
                     if(nettyClient!=null&&!tcp_connect_flag){
-                        System.out.println("TCP瀹稿弶鏌囧锟�");
-                        //TCP闁插秵鏌婇崥灞绢劄鏉╃偞甯�
+                        // TCP未连接,尝试重连
                         if(!connect_request_flag) {
-                            //System.out.println("TCP闁插秵鏌婇崥灞绢劄鏉╃偞甯�");
-                            //鐠佸墽鐤員CP鐠囬攱鐪伴悩鑸碉拷锟�,閸欘亪鍣搁弬鏉挎倱濮濄儴绻涢幒銉ょ濞嗭拷
                             connect_request_flag = true;
 
                             DataUtil.synrTcp(nettyClient,mes_gw);
@@ -401,7 +508,7 @@ public class MesClient extends JFrame {
         }, 100,mes_heart_icon_cycle*1000);
     }
 
-    //鐠佸墽鐤唗cp鏉╃偞甯撮悩鑸碉拷浣规▔缁�锟�
+    // 设置TCP连接状态
     public static void setTcpStatus() {
         if(tcp_connect_flag) {
             status_menu.setText("已连接MES服务器");
@@ -459,6 +566,8 @@ public class MesClient extends JFrame {
         MesClient.curSna = "";
         MesClient.tjFlaga = 0;
         MesClient.checkA = 0;
+        PlcUtil.resetRunDetectA();
+        MesClient.hjparams = new ArrayList<>();
 
 //        MesClient.batch_status = 0;
 //        MesClient.textField.setText("");
@@ -536,7 +645,9 @@ public class MesClient extends JFrame {
 
         MesClient.curSnb = "";
         MesClient.tjFlagb = 0;
+        MesClient.hjparams = new ArrayList<>();
         MesClient.checkB = 0;
+        PlcUtil.resetRunDetectB();
 
 //        MesClient.batch_status2 = 0;
 //        MesClient.textField_1.setText("");
@@ -552,7 +663,7 @@ public class MesClient extends JFrame {
 //        shiftUserCheck();
     }
 
-    //閼惧嘲褰囬悽銊﹀煕20娴o拷
+    // 获取用户20位
     public static void getUser() {
         user20 = user_menu.getText().toString();
         String space_tmp1 = "";
@@ -564,7 +675,6 @@ public class MesClient extends JFrame {
         user20 = user20 + space_tmp1;
     }
 
-    //閼惧嘲褰嘼arcode閸愬懎顔�36娴o拷
     public static String getBarcode(String barcodeTmp) {
         String barcodeRet = barcodeTmp;
         if(barcodeTmp.equalsIgnoreCase("")) {
@@ -616,7 +726,7 @@ public class MesClient extends JFrame {
         fileMenu.add(exitMenuItem);
         exitMenuItem.addMouseListener(new MouseAdapter() {
             @Override
-            public void mousePressed(MouseEvent e) {//閸欘亣鍏樺Λ锟藉ù瀣煂mousePressed娴滃娆�
+            public void mousePressed(MouseEvent e) {
                 super.mouseClicked(e);
                 //dispose();
                 logoff();
@@ -636,7 +746,6 @@ public class MesClient extends JFrame {
             @Override
             public void mousePressed(MouseEvent e) {
                 super.mouseClicked(e);
-                //闁插秷绻沵es
                 nettyClient.future.channel().close();
             }
         });
@@ -831,6 +940,7 @@ public class MesClient extends JFrame {
                         MesClient.pxstatus1.setText("A:提交成功");
                         MesClient.status_menu.setText("A件提交成功");
                         MesClient.resetScanA();
+                        MesClient.notifyWorkSubmitted();
                     }
                 }
 
@@ -872,6 +982,7 @@ public class MesClient extends JFrame {
                         MesClient.pxstatus2.setText("B:提交成功");
                         MesClient.status_menu.setText("B件提交成功");
                         MesClient.resetScanB();
+                        MesClient.notifyWorkSubmitted();
                     }
                 }
             }
@@ -903,6 +1014,7 @@ public class MesClient extends JFrame {
                         MesClient.pxstatus2.setText("B:提交成功");
                         MesClient.status_menu.setText("B件提交成功");
                         MesClient.resetScanB();
+                        MesClient.notifyWorkSubmitted();
                     }
                 }
 
@@ -944,6 +1056,7 @@ public class MesClient extends JFrame {
                         MesClient.pxstatus2.setText("B:提交成功");
                         MesClient.status_menu.setText("B件提交成功");
                         MesClient.resetScanB();
+                        MesClient.notifyWorkSubmitted();
                     }
                 }
             }
@@ -1040,6 +1153,21 @@ public class MesClient extends JFrame {
         param5.setBounds(196, 400, 70, 40);
         indexPanelA.add(param5);
 
+        JLabel lblTipA = new JLabel("导电嘴");
+        lblTipA.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblTipA.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
+        lblTipA.setBounds(276, 400, 70, 40);
+        indexPanelA.add(lblTipA);
+
+        tipLifeA = new JTextField("0/0");
+        tipLifeA.setEnabled(false);
+        tipLifeA.setEditable(false);
+        tipLifeA.setHorizontalAlignment(SwingConstants.CENTER);
+        tipLifeA.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
+        tipLifeA.setBounds(356, 400, 110, 40);
+        tipLifeA.setToolTipText("实际数量/设定数量");
+        indexPanelA.add(tipLifeA);
+
 
         // --- B枪 区域 ---
         JLabel lblB_Title = new JLabel("焊枪 B");
@@ -1117,6 +1245,21 @@ public class MesClient extends JFrame {
         param25.setBounds(687, 400, 70, 40);
         indexPanelA.add(param25);
 
+        JLabel lblTipB = new JLabel("导电嘴");
+        lblTipB.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblTipB.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
+        lblTipB.setBounds(767, 400, 70, 40);
+        indexPanelA.add(lblTipB);
+
+        tipLifeB = new JTextField("0/0");
+        tipLifeB.setEnabled(false);
+        tipLifeB.setEditable(false);
+        tipLifeB.setHorizontalAlignment(SwingConstants.CENTER);
+        tipLifeB.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
+        tipLifeB.setBounds(847, 400, 110, 40);
+        tipLifeB.setToolTipText("实际数量/设定数量");
+        indexPanelA.add(tipLifeB);
+
 
         // --- C枪 区域 ---
         JLabel lblC_Title = new JLabel("焊枪 C");
@@ -1189,6 +1332,21 @@ public class MesClient extends JFrame {
         param35.setBounds(196, 620, 70, 40);
         indexPanelA.add(param35);
 
+        JLabel lblTipC = new JLabel("导电嘴");
+        lblTipC.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblTipC.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 16));
+        lblTipC.setBounds(276, 620, 70, 40);
+        indexPanelA.add(lblTipC);
+
+        tipLifeC = new JTextField("0/0");
+        tipLifeC.setEnabled(false);
+        tipLifeC.setEditable(false);
+        tipLifeC.setHorizontalAlignment(SwingConstants.CENTER);
+        tipLifeC.setFont(new Font("Microsoft YaHei UI", Font.BOLD, 16));
+        tipLifeC.setBounds(356, 620, 110, 40);
+        tipLifeC.setToolTipText("实际数量/设定数量");
+        indexPanelA.add(tipLifeC);
+
 
         // --- D枪 区域 ---
         JLabel lblD_Title = new JLabel("焊枪 D");
@@ -1459,8 +1617,8 @@ public class MesClient extends JFrame {
                 int selectedIndex = tabbedPane.getSelectedIndex();
                 System.out.println("selectedIndex:"+selectedIndex);
 
-                if(selectedIndex == 1){
-
+                if(selectedIndex == TAB_WORK_RECORD){
+                    refreshWorkRecord();
                 }
             }
         });
@@ -1494,6 +1652,7 @@ public class MesClient extends JFrame {
             }
             if(MesClient.curPage.equals("A")){
                 System.out.println("typeA");
+                MesClient.lastScanSide = "A";
                 if(!product_sn.getText().isEmpty() && mesQualityFlagA){
                     MesClient.setMenuStatus("A面已存在工件码,勿重复扫码",1);
                     return;
@@ -1501,6 +1660,7 @@ public class MesClient extends JFrame {
                 product_sn.setText(scanBarcodeSn);
             }else{
                 System.out.println("typeB");
+                MesClient.lastScanSide = "B";
                 if(!product_sn2.getText().isEmpty() && mesQualityFlagB){
                     MesClient.setMenuStatus("B面已存在工件码,勿重复扫码",1);
                     return;
@@ -1514,7 +1674,7 @@ public class MesClient extends JFrame {
             System.out.println("123");
             getUser();
             // 查询工件质量
-            Boolean sendret = DataUtil.checkQuality(nettyClient,scanBarcodeSn,user20,MesClient.curPage);
+            Boolean sendret = DataUtil.checkQuality(nettyClient,scanBarcodeSn,user20,MesClient.lastScanSide);
             if(!sendret){
                 MesClient.setMenuStatus("消息发送失败,请重试",1);
             }
@@ -1532,8 +1692,15 @@ public class MesClient extends JFrame {
 
     public static void getMaterailData(){
         JSONObject retObj = DataUtil.getBindMaterail();
+        if (retObj == null) {
+            log.warn("加载物料列表失败,请检查MES服务或网络连接");
+            return;
+        }
         if(retObj.get("result")!=null&&retObj.get("result").toString().equalsIgnoreCase("true")) {
             List<BindMaterialResp> arrs = retObj.getList("data",BindMaterialResp.class);
+            if (arrs == null || arrs.isEmpty()) {
+                return;
+            }
             int i = 0;
             rowData = new Object[arrs.size()][7];
             for (BindMaterialResp bindMaterialResp:arrs){
@@ -1552,9 +1719,15 @@ public class MesClient extends JFrame {
 
     public static void updateMaterailData(){
         JSONObject retObj = DataUtil.getBindMaterail();
+        if (retObj == null) {
+            log.warn("更新物料列表失败,请检查MES服务或网络连接");
+            return;
+        }
         if(retObj.get("result")!=null&&retObj.get("result").toString().equalsIgnoreCase("true")) {
             List<BindMaterialResp> arrs = retObj.getList("data",BindMaterialResp.class);
-
+            if (arrs == null || arrs.isEmpty() || rowData == null) {
+                return;
+            }
             int i = 0;
             for (BindMaterialResp bindMaterialResp:arrs){
                 rowData[i][0] = bindMaterialResp.getMaterialTitle();
@@ -1579,6 +1752,10 @@ public class MesClient extends JFrame {
         if(scanBarcode!=null&&!scanBarcode.equalsIgnoreCase("")) {
 
             JSONObject retObj = DataUtil.saveBindMaterail(scanBarcode,bindMaterialResp.getCraft(),bindMaterialResp.getMaterialId(),bindMaterialResp.getType());
+            if (retObj == null) {
+                MesClient.setMenuStatus("请求失败,请重试",-1);
+                return;
+            }
             if(retObj.get("result")!=null&&retObj.get("result").toString().equalsIgnoreCase("true")) {
                 MesClient.setMenuStatus("扫物料:"+bindMaterialResp.getMaterialTitle()+"成功",0);
                 updateMaterailData();

+ 84 - 128
src/com/mes/ui/MesRevice.java

@@ -1,181 +1,137 @@
 package com.mes.ui;
 
 import com.mes.netty.ProtocolParam;
-import com.mes.component.MyDialog;
 import com.mes.util.ErrorMsg;
 
-import javax.swing.*;
-import java.awt.*;
-
 public class MesRevice {
 
-    // 质量查询
-    public static void checkQualityRevice(String processMsgRet,String mes_msg){
-        try{
-            if(processMsgRet.equalsIgnoreCase("UD")) {
-                MesClient.setMenuStatus("该工件可以加工",0);
+    /** QD/QN ??? HTTP pccheck ????????????TCP ??? RSQD ?????? OP080? */
+    private static String resolveQualityErrorMsg(String processMsgRet, String mes_msg) {
+        if (processMsgRet.equalsIgnoreCase("QD") || processMsgRet.equalsIgnoreCase("QN")) {
+            String sn = ProtocolParam.getSn(mes_msg).trim();
+            if (sn.isEmpty()) {
+                if (MesClient.product_sn != null && !MesClient.product_sn.getText().trim().isEmpty()) {
+                    sn = MesClient.product_sn.getText().trim();
+                } else if (MesClient.product_sn2 != null && !MesClient.product_sn2.getText().trim().isEmpty()) {
+                    sn = MesClient.product_sn2.getText().trim();
+                }
+            }
+            String side = MesClient.resolveScanSide(ProtocolParam.getOprno(mes_msg).trim());
+            MesClient.getUser();
+            String httpMsg = DataUtil.getPcCheckMessage(sn, MesClient.user20, side);
+            if (httpMsg != null && !httpMsg.isEmpty()) {
+                return httpMsg;
+            }
+            if (processMsgRet.equalsIgnoreCase("QD")) {
+                return "\u8be5\u5de5\u4ef6\u524d\u5e8f\u5de5\u4f4d\u672a\u52a0\u5de5";
+            }
+            return "\u8be5\u5de5\u4ef6\u524d\u5e8f\u5de5\u4f4d\u52a0\u5de5NG";
+        }
+        return ErrorMsg.getErrorMsg(processMsgRet, mes_msg);
+    }
+
+    // ????
+    public static void checkQualityRevice(String processMsgRet, String mes_msg) {
+        try {
+            if (processMsgRet.equalsIgnoreCase("UD")) {
+                MesClient.setMenuStatus("\u8be5\u5de5\u4ef6\u53ef\u4ee5\u52a0\u5de5", 0);
 
                 String oprno = ProtocolParam.getOprno(mes_msg).trim();
                 String sn = ProtocolParam.getSn(mes_msg).trim();
-                System.out.println(":"+oprno);
-                if(oprno.equals("OP110A")){
-                    String barcode36 = MesClient.getBarcode(sn);//处理36为码
-                    MesClient.getUser();
-                    DataUtil.startWork(MesClient.nettyClient,barcode36,MesClient.user20,"A");
-                }else if(oprno.equals("OP110B")){
-                    String barcode36 = MesClient.getBarcode(sn);//处理36为码
-                    MesClient.getUser();
-                    DataUtil.startWork(MesClient.nettyClient,barcode36,MesClient.user20,"B");
-                }else if(oprno.equals("OP110C")){
-                    String barcode36 = MesClient.getBarcode(sn);//处理36为码
-                    MesClient.getUser();
-                    DataUtil.startWork(MesClient.nettyClient,barcode36,MesClient.user20,"A");
-                }else if(oprno.equals("OP110D")){
-                    String barcode36 = MesClient.getBarcode(sn);//处理36为码
-                    MesClient.getUser();
-                    DataUtil.startWork(MesClient.nettyClient,barcode36,MesClient.user20,"B");
-                }
+                String barcode36 = MesClient.getBarcode(sn);
+                String side = MesClient.resolveScanSide(oprno);
+                MesClient.getUser();
+                DataUtil.startWork(MesClient.nettyClient, barcode36, MesClient.user20, side);
 
-            }else {
-                if(MesClient.curPage.equals("A")){
+            } else {
+                if (MesClient.curPage.equals("A")) {
                     MesClient.check_quality_result = false;
-                }else{
+                } else {
                     MesClient.check_quality_result2 = false;
                 }
 
-                String lmsg = ErrorMsg.getErrorMsg(processMsgRet, mes_msg);
-                MesClient.setMenuStatus(lmsg,-1);
-                PlcUtil.changeEnable(MesClient.s7PLC,false);
+                String lmsg = resolveQualityErrorMsg(processMsgRet, mes_msg);
+                MesClient.setMenuStatus(lmsg, -1);
+                PlcUtil.changeEnable(MesClient.s7PLC, false);
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    // 开始回复处理
-    public static void startRevice(String processMsgRet,String mes_msg){
-        try{
-            if(processMsgRet.equalsIgnoreCase("OK")) {
+    // ??????
+    public static void startRevice(String processMsgRet, String mes_msg) {
+        try {
+            if (processMsgRet.equalsIgnoreCase("OK")) {
 
                 String oprno = ProtocolParam.getOprno(mes_msg).trim();
-                String sn = ProtocolParam.getSn(mes_msg).trim();
+                String side = MesClient.resolveScanSide(oprno);
 
-                if(oprno.equals(MesClient.mes_gw+"A") || oprno.equals(MesClient.mes_gw+"C")){
-                    MesClient.mesQualityFlagA = true;
-                    MesClient.pxstatus1.setText("A:可以加工,等待设备启动");
-                    MesClient.curSna = MesClient.product_sn.getText();
-                    MesClient.tjFlaga = 1;
-                }else{
+                if ("B".equals(side)) {
                     MesClient.mesQualityFlagB = true;
-                    MesClient.pxstatus2.setText("B:可以加工,等待设备启动");
+                    MesClient.pxstatus2.setText("B:\u53ef\u4ee5\u52a0\u5de5\uff0c\u7b49\u5f85\u8bbe\u5907\u542f\u52a8");
                     MesClient.curSnb = MesClient.product_sn2.getText();
                     MesClient.tjFlagb = 1;
+                } else {
+                    MesClient.mesQualityFlagA = true;
+                    MesClient.pxstatus1.setText("A:\u53ef\u4ee5\u52a0\u5de5\uff0c\u7b49\u5f85\u8bbe\u5907\u542f\u52a8");
+                    MesClient.curSna = MesClient.product_sn.getText();
+                    MesClient.tjFlaga = 1;
                 }
-//                System.out.println("scan_type1:"+MesClient.scan_type);
-//                MesClient.formatScanType(2);// 1=镭雕码 2=后梁码
-//                System.out.println("scan_type2:"+MesClient.scan_type);
-                PlcUtil.changeEnable(MesClient.s7PLC,true);
+                PlcUtil.changeEnable(MesClient.s7PLC, true);
 
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    // 绑定
-    public static void bindRevice(String processMsgRet,String mes_msg){
-        try{
-            String craft = ProtocolParam.getCraft(mes_msg);
-            if(processMsgRet.equalsIgnoreCase("OK")) {
-//                if(craft.equals("400007")){
-//                    if(MesClient.scan_type == 1){
-//                        MesClient.batch_status = 1;
-//                        MesClient.batch_scan_1.setIcon(new ImageIcon(MesClient.class.getResource("/bg/delete.png")));
-//                        MesClient.batch_scan_1.setText("解绑");
-//                        MesClient.batch_scan_1.setEnabled(true);
-//                    }else{
-//                        MesClient.batch_status2 = 1;
-//                        MesClient.batch_scan_2.setIcon(new ImageIcon(MesClient.class.getResource("/bg/delete.png")));
-//                        MesClient.batch_scan_2.setText("解绑");
-//                        MesClient.batch_scan_2.setEnabled(true);
-//                    }
-//
-//                    PlcUtil.changeEnable(MesClient.s7PLC,true);
-//
-////                    MesClient.formatBatchSn();
-//                }
-            }else{
-//                if(craft.equals("400007")){
-//                    JOptionPane.showMessageDialog(MesClient.mesClientFrame,"绑定失败,请重试","提醒", JOptionPane.INFORMATION_MESSAGE);
-//                    return;
-//                }
+    // ??
+    public static void bindRevice(String processMsgRet, String mes_msg) {
+        try {
+            if (processMsgRet.equalsIgnoreCase("OK")) {
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    // 解绑
-    public static void unbindRevice(String processMsgRet,String mes_msg){
-        try{
-            String craft = ProtocolParam.getCraft(mes_msg);
-//            if(processMsgRet.equalsIgnoreCase("OK")) {
-//                if(craft.equals("400007")){
-//                    if(MesClient.scan_type == 1){
-//                        MesClient.batch_status = 0;
-//                        MesClient.batch_scan_1.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
-//                        MesClient.batch_scan_1.setText("扫码");
-//                        MesClient.batch_scan_1.setEnabled(true);
-//                    }else {
-//                        MesClient.batch_status2 = 0;
-//                        MesClient.batch_scan_2.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
-//                        MesClient.batch_scan_2.setText("扫码");
-//                        MesClient.batch_scan_2.setEnabled(true);
-//                    }
-//                    PlcUtil.changeEnable(MesClient.s7PLC,false);
-////                    MesClient.formatBatchSn();
-//                }
-//            }else{
-//                if(craft.equals("400007")){
-//                    JOptionPane.showMessageDialog(MesClient.mesClientFrame,"解绑失败,请重试","提醒", JOptionPane.INFORMATION_MESSAGE);
-//                    return;
-//                }
-//            }
-        }catch (Exception e){
+    // ??
+    public static void unbindRevice(String processMsgRet, String mes_msg) {
+        try {
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }
 
-    // 上传质量
-    public static void updateResultRevice(String processMsgRet,String mes_msg){
-        try{
+    // ????
+    public static void updateResultRevice(String processMsgRet, String mes_msg) {
+        try {
             String sn = ProtocolParam.getSn(mes_msg).trim();
-            if(processMsgRet.equalsIgnoreCase("OK")) {
-                if(sn.equals(MesClient.product_sn.getText())){
-                    System.out.println("resetScanA");
-                    MesClient.status_menu.setText("A件提交成功");
-                    MesClient.pxstatus1.setText("A:提交成功");
+            if (processMsgRet.equalsIgnoreCase("OK")) {
+                if (sn.equals(MesClient.product_sn.getText())) {
+                    MesClient.status_menu.setText("A\u4ef6\u63d0\u4ea4\u6210\u529f");
+                    MesClient.pxstatus1.setText("A:\u63d0\u4ea4\u6210\u529f");
                     MesClient.resetScanA();
-                }else{
-                    System.out.println("resetScanB");
-                    MesClient.pxstatus2.setText("B:提交成功");
-                    MesClient.status_menu.setText("B件提交成功");
+                    MesClient.notifyWorkSubmitted();
+                } else {
+                    MesClient.pxstatus2.setText("B:\u63d0\u4ea4\u6210\u529f");
+                    MesClient.status_menu.setText("B\u4ef6\u63d0\u4ea4\u6210\u529f");
                     MesClient.resetScanB();
+                    MesClient.notifyWorkSubmitted();
                 }
-            }else{
-                if(sn.equals(MesClient.product_sn.getText())){
-                    MesClient.status_menu.setText("A件提交失败");
-                    MesClient.pxstatus1.setText("A:提交失败");
-//                    MesClient.finish_ok_bt.setEnabled(true);
+            } else {
+                if (sn.equals(MesClient.product_sn.getText())) {
+                    MesClient.status_menu.setText("A\u4ef6\u63d0\u4ea4\u5931\u8d25");
+                    MesClient.pxstatus1.setText("A:\u63d0\u4ea4\u5931\u8d25");
                     MesClient.tjStatusa = 1;
-                }else{
-                    MesClient.status_menu.setText("B件提交失败");
-                    MesClient.pxstatus2.setText("B:提交失败");
-//                    MesClient.finish_ng_bt.setEnabled(true);
+                } else {
+                    MesClient.status_menu.setText("B\u4ef6\u63d0\u4ea4\u5931\u8d25");
+                    MesClient.pxstatus2.setText("B:\u63d0\u4ea4\u5931\u8d25");
                     MesClient.tjStatusb = 1;
                 }
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             e.printStackTrace();
         }
     }

+ 182 - 52
src/com/mes/ui/PlcUtil.java

@@ -14,23 +14,50 @@ import java.util.Arrays;
 
 public class PlcUtil {
 
+    /** 「启动中」连续为真的次数;>=2 认为是电平信号而非脉冲,之后下降沿可判完成 */
+    private static int startTrueCountA = 0;
+    private static int startTrueCountB = 0;
+    private static long runStartMsA = 0L;
+    private static long runStartMsB = 0L;
+    /** 设备运行超时(分钟),超时后强制结束并报工;可用 config mes.run_timeout_min 覆盖 */
+    private static final int DEFAULT_RUN_TIMEOUT_MIN = 5;
+
+    public static void resetRunDetectA() {
+        startTrueCountA = 0;
+        runStartMsA = 0L;
+    }
+
+    public static void resetRunDetectB() {
+        startTrueCountB = 0;
+        runStartMsB = 0L;
+    }
+
+    private static long getRunTimeoutMs() {
+        int minutes = DEFAULT_RUN_TIMEOUT_MIN;
+        try {
+            String v = com.mes.util.ConfigLoader.loadProperties().getProperty("mes.run_timeout_min");
+            if (v != null && !v.trim().isEmpty()) {
+                int min = Integer.parseInt(v.trim());
+                if (min > 0) {
+                    minutes = min;
+                }
+            }
+        } catch (Exception ignore) {
+        }
+        return minutes * 60L * 1000L;
+    }
+
     public static void getParamDyDl(S7PLC s7PLC){
         Boolean starta = s7PLC.readBoolean("DB1.2.2"); //A启动中
-        Boolean startb = s7PLC.readBoolean("DB1.2.3"); //B启动中
+        Boolean startb = s7PLC.readBoolean("DB1.2.6"); //B启动中
 
         String oprno = MesClient.mes_gw+"A";
         if(startb){
             oprno = MesClient.mes_gw+"B";
         }
-        if(MesClient.mes_gwflag.equals("B")){
-            if(oprno.equals(MesClient.mes_gw+"A")){
-                oprno = MesClient.mes_gw+"C";
-            }else{
-                oprno = MesClient.mes_gw+"D";
-            }
-        }
 
-        if(MesClient.tjFlaga == 2 || MesClient.tjFlagb == 2){ //运行中
+        // A/B 两把枪 + 第三把焊枪:工件运行中统一读 PLC、统一刷新界面、统一采样
+        if(MesClient.tjFlaga == 2 || MesClient.tjFlagb == 2){
             Float dya = s7PLC.readFloat32("DB1010.2");
             Float dla = s7PLC.readFloat32("DB1010.8");
             Float ssa = s7PLC.readFloat32("DB1010.14");
@@ -61,24 +88,11 @@ public class PlcUtil {
             Short cwc = s7PLC.readInt16("DB1010.74");
             Short jobc = s7PLC.readInt16("DB1010.76");
 
-//            Float dyd = s7PLC.readFloat32("DB1010.82");
-//            Float dld = s7PLC.readFloat32("DB1010.90");
-//            Float ssd = s7PLC.readFloat32("DB1010.98");
-//            Short cwd = s7PLC.readInt16("DB1010.104");
-//            Short jobd = s7PLC.readInt16("DB1010.102");
-
-            // 实时刷新 UI 显示
-            if (MesClient.param31 != null) MesClient.param31.setText(dyc + "");
-            if (MesClient.param32 != null) MesClient.param32.setText(dlc + "");
-            if (MesClient.param33 != null) MesClient.param33.setText(ssc + "");
-            if (MesClient.param34 != null) MesClient.param34.setText(cwc + "");
-            if (MesClient.param35 != null) MesClient.param35.setText(jobc + "");
-
-//            if (MesClient.param41 != null) MesClient.param41.setText(dyd + "");
-//            if (MesClient.param42 != null) MesClient.param42.setText(dld + "");
-//            if (MesClient.param43 != null) MesClient.param43.setText(ssd + "");
-//            if (MesClient.param44 != null) MesClient.param44.setText(cwd + "");
-//            if (MesClient.param45 != null) MesClient.param45.setText(jobd + "");
+            MesClient.param31.setText(dyc+"");
+            MesClient.param32.setText(dlc+"");
+            MesClient.param33.setText(ssc+"");
+            MesClient.param34.setText(cwc+"");
+            MesClient.param35.setText(jobc+"");
 
             String record_time = DateLocalUtils.getCurrentTime();
             // 更新采样数据拼接逻辑,包含 A, B, C, D 四把枪的参数
@@ -208,25 +222,27 @@ public class PlcUtil {
             if(starta){
                 MesClient.curFlag = "A";
                 MesClient.tjFlaga = 2;
+                startTrueCountA = 1;
+                runStartMsA = System.currentTimeMillis();
                 MesClient.pxstatus1.setText("A:设备运行中");
 //                MesClient.formatScanType(1);
 //                MesClient.batch_scan_1.setEnabled(false);
                 PlcUtil.changeEnable(s7PLC,false);
             }
         }else if(MesClient.tjFlaga == 2){
-            Boolean afinish = getAFinish(s7PLC); //A焊接完成
-            if(afinish){ // B启动中,A当做完成
-                MesClient.tjFlaga = 3;
-                MesClient.pxstatus1.setText("A:设备运行结束,提交结果中");
-                MesClient.finish_ok_bt.setEnabled(true);
-                MesClient.finish_ng_bt.setEnabled(true);
-
-                Boolean sendret = DataUtil.sendQuality(MesClient.nettyClient,MesClient.product_sn.getText(),"OK",MesClient.user20,"A");
-                if(!sendret){
-                    MesClient.pxstatus1.setText("A:结果上传MES失败");
-                    MesClient.tjStatusa = 1;
-                }
+            // 不改 PLC:完成位可能是短脉冲会被轮询漏掉;用「启动中」下降沿 + 超时强制结束兜底
+            Boolean starta = getAStart(s7PLC);
+            if(Boolean.TRUE.equals(starta)){
+                startTrueCountA++;
+            }
+            boolean startFell = startTrueCountA >= 2 && !Boolean.TRUE.equals(starta);
+            boolean timedOut = runStartMsA > 0
+                    && (System.currentTimeMillis() - runStartMsA) >= getRunTimeoutMs();
+            if(getAFinish(s7PLC) || startFell || timedOut){
+                finishAndSubmitA(timedOut);
             }
+        }else{
+            resetRunDetectA();
         }
     }
 
@@ -236,25 +252,66 @@ public class PlcUtil {
             if(startb){
                 MesClient.curFlag = "B";
                 MesClient.tjFlagb = 2;
+                startTrueCountB = 1;
+                runStartMsB = System.currentTimeMillis();
                 MesClient.pxstatus2.setText("B:设备运行中");
                 MesClient.batch_scan_2.setEnabled(false);
                 MesClient.formatScanType(1);
                 PlcUtil.changeEnable(s7PLC,false);
             }
         }else if(MesClient.tjFlagb == 2){
-            Boolean bfinish = getBFinish(s7PLC); //B焊接完成
-            if(bfinish){
-                MesClient.tjFlagb = 3;
-                MesClient.pxstatus2.setText("B:设备运行结束,提交结果中");
-                MesClient.finish_ng_bt2.setEnabled(true);
-                MesClient.finish_ok_bt2.setEnabled(true);
-
-                Boolean sendret = DataUtil.sendQuality(MesClient.nettyClient,MesClient.product_sn2.getText(),"OK",MesClient.user20,"B");
-                if(!sendret){
-                    MesClient.pxstatus2.setText("B:结果上传MES失败");
-                    MesClient.tjStatusb = 1;
-                }
+            Boolean startb = getBStart(s7PLC);
+            if(Boolean.TRUE.equals(startb)){
+                startTrueCountB++;
             }
+            boolean startFell = startTrueCountB >= 2 && !Boolean.TRUE.equals(startb);
+            boolean timedOut = runStartMsB > 0
+                    && (System.currentTimeMillis() - runStartMsB) >= getRunTimeoutMs();
+            if(getBFinish(s7PLC) || startFell || timedOut){
+                finishAndSubmitB(timedOut);
+            }
+        }else{
+            resetRunDetectB();
+        }
+    }
+
+    private static void finishAndSubmitA(boolean timedOut){
+        resetRunDetectA();
+        MesClient.tjFlaga = 3;
+        MesClient.pxstatus1.setText(timedOut
+                ? "A:运行超时,强制结束并提交结果"
+                : "A:设备运行结束,提交结果中");
+        MesClient.finish_ok_bt.setEnabled(true);
+        MesClient.finish_ng_bt.setEnabled(true);
+
+        MesClient.flushHjparamsToDb(
+                MesClient.resolveOprnoForSubmit("A"),
+                MesClient.product_sn.getText());
+
+        Boolean sendret = DataUtil.sendQuality(MesClient.nettyClient,MesClient.product_sn.getText(),"OK",MesClient.user20,"A");
+        if(!sendret){
+            MesClient.pxstatus1.setText("A:结果上传MES失败");
+            MesClient.tjStatusa = 1;
+        }
+    }
+
+    private static void finishAndSubmitB(boolean timedOut){
+        resetRunDetectB();
+        MesClient.tjFlagb = 3;
+        MesClient.pxstatus2.setText(timedOut
+                ? "B:运行超时,强制结束并提交结果"
+                : "B:设备运行结束,提交结果中");
+        MesClient.finish_ng_bt2.setEnabled(true);
+        MesClient.finish_ok_bt2.setEnabled(true);
+
+        MesClient.flushHjparamsToDb(
+                MesClient.resolveOprnoForSubmit("B"),
+                MesClient.product_sn2.getText());
+
+        Boolean sendret = DataUtil.sendQuality(MesClient.nettyClient,MesClient.product_sn2.getText(),"OK",MesClient.user20,"B");
+        if(!sendret){
+            MesClient.pxstatus2.setText("B:结果上传MES失败");
+            MesClient.tjStatusb = 1;
         }
     }
 
@@ -307,7 +364,7 @@ public class PlcUtil {
     public static Boolean changeEnable(S7PLC s7PLC,Boolean result){
         try{
             //DB9.21.7 A//DB9.26.4 B
-            s7PLC.writeBoolean("DB9.26.4",result);
+            s7PLC.writeBoolean("DB9.22.3",result);
 //            s7PLC.writeBoolean("DB9.21.7",result);
             return true;
         }catch (Exception e){
@@ -316,6 +373,79 @@ public class PlcUtil {
         }
     }
 
+    /**
+     * 读取导电嘴寿命并刷新界面:
+     * A 设定 DB8.32 / 实际 DB8.34;B 设定 DB8.56 / 实际 DB8.58;C 设定 DB8.80 / 实际 DB8.82。
+     * 显示格式:实际/设定;达到设定数量后报警。
+     */
+    public static void getTipLife(S7PLC s7PLC){
+        try{
+            int setA = s7PLC.readInt16("DB8.32") & 0xFFFF;
+            int actA = s7PLC.readInt16("DB8.34") & 0xFFFF;
+            int setB = s7PLC.readInt16("DB8.56") & 0xFFFF;
+            int actB = s7PLC.readInt16("DB8.58") & 0xFFFF;
+            int setC = s7PLC.readInt16("DB8.80") & 0xFFFF;
+            int actC = s7PLC.readInt16("DB8.82") & 0xFFFF;
+
+            final int fSetA = setA, fActA = actA, fSetB = setB, fActB = actB, fSetC = setC, fActC = actC;
+            SwingUtilities.invokeLater(() -> {
+                updateTipLifeUi(MesClient.tipLifeA, fActA, fSetA, "A",
+                        () -> MesClient.tipAlarmAShown, v -> MesClient.tipAlarmAShown = v);
+                updateTipLifeUi(MesClient.tipLifeB, fActB, fSetB, "B",
+                        () -> MesClient.tipAlarmBShown, v -> MesClient.tipAlarmBShown = v);
+                updateTipLifeUi(MesClient.tipLifeC, fActC, fSetC, "C",
+                        () -> MesClient.tipAlarmCShown, v -> MesClient.tipAlarmCShown = v);
+            });
+
+            // 数值变化时同步到 MES(供大屏展示)
+            if (actA != MesClient.lastTipSyncAActual || setA != MesClient.lastTipSyncASet) {
+                MesClient.lastTipSyncAActual = actA;
+                MesClient.lastTipSyncASet = setA;
+                DataUtil.syncCmtTipLife(MesClient.mes_line_sn + "_" + MesClient.mes_gw + "_DZZ_A",
+                        MesClient.mes_gw + "导电嘴A", actA, setA);
+            }
+            if (actB != MesClient.lastTipSyncBActual || setB != MesClient.lastTipSyncBSet) {
+                MesClient.lastTipSyncBActual = actB;
+                MesClient.lastTipSyncBSet = setB;
+                DataUtil.syncCmtTipLife(MesClient.mes_line_sn + "_" + MesClient.mes_gw + "_DZZ_B",
+                        MesClient.mes_gw + "导电嘴B", actB, setB);
+            }
+            if (actC != MesClient.lastTipSyncCActual || setC != MesClient.lastTipSyncCSet) {
+                MesClient.lastTipSyncCActual = actC;
+                MesClient.lastTipSyncCSet = setC;
+                DataUtil.syncCmtTipLife(MesClient.mes_line_sn + "_" + MesClient.mes_gw + "_DZZ_C",
+                        MesClient.mes_gw + "导电嘴C", actC, setC);
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    private interface BoolGetter { boolean get(); }
+    private interface BoolSetter { void set(boolean v); }
+
+    private static void updateTipLifeUi(JTextField field, int act, int set, String gun,
+                                        BoolGetter alarmShown, BoolSetter setAlarmShown) {
+        if (field == null) {
+            return;
+        }
+        field.setText(act + "/" + set);
+        if (set > 0 && act >= set) {
+            field.setForeground(Color.RED);
+            if (!alarmShown.get()) {
+                setAlarmShown.set(true);
+                MesClient.setMenuStatus("导电嘴" + gun + "寿命已到期,请更换!", -1);
+                PlcUtil.changeEnable(MesClient.s7PLC, false);
+                JOptionPane.showMessageDialog(MesClient.mesClientFrame,
+                        "导电嘴" + gun + "实际数量已达设定数量(" + act + "/" + set + ")\n请更换导电嘴!",
+                        "导电嘴寿命报警", JOptionPane.WARNING_MESSAGE);
+            }
+        } else {
+            field.setForeground(Color.BLACK);
+            setAlarmShown.set(false);
+        }
+    }
+
     // 获取当前是A面还是B面
     public static String getCurAside(S7PLC s7PLC){
         String str = "A";

+ 1 - 1
src/com/mes/ui/TextMain.java

@@ -5,7 +5,7 @@ import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
 
 public class TextMain {
 
-    public static S7PLC s7PLC = new S7PLC(EPlcType.S1200, "192.168.7.2");
+    public static S7PLC s7PLC = new S7PLC(EPlcType.S1200, "192.168.7.100");
 
     public static void main(String[] args) {
         System.out.println(s7PLC.readBoolean("DB1.2.0 "));

+ 4 - 4
src/com/mes/util/Base64Utils.java

@@ -4,12 +4,12 @@ import org.apache.commons.codec.binary.Base64;
 
 public class Base64Utils {
 	public static String getBase64(String str) {
-		// 待编码的二进制数据
+		// 待编码的二进制数据
         byte[] binaryData = str.getBytes();
-        // 使用Base64.encodeBase64String进行编码
+        // 浣跨敤Base64.encodeBase64String杩涜�缂栫爜
         String encodedString = Base64.encodeBase64String(binaryData);
-        // 输出编码后的结果
-        System.out.println("Base64编码结果: " + encodedString);
+        // 杈撳嚭缂栫爜鍚庣殑缁撴灉
+        System.out.println("Base64编码结果: " + encodedString);
         return encodedString;
 	}
 }

+ 46 - 0
src/com/mes/util/ConfigLoader.java

@@ -0,0 +1,46 @@
+package com.mes.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+public class ConfigLoader {
+    private static final String CONFIG_PATH = "config/config.properties";
+
+    public static InputStream openConfigStream() throws IOException {
+        ClassLoader cl = ConfigLoader.class.getClassLoader();
+        InputStream is = cl != null ? cl.getResourceAsStream(CONFIG_PATH) : null;
+        if (is == null) {
+            is = ConfigLoader.class.getResourceAsStream("/" + CONFIG_PATH);
+        }
+        if (is == null) {
+            File[] candidates = new File[]{
+                    new File("src/resources/" + CONFIG_PATH),
+                    new File(CONFIG_PATH),
+                    new File("bin/" + CONFIG_PATH)
+            };
+            for (File file : candidates) {
+                if (file.isFile()) {
+                    return new FileInputStream(file);
+                }
+            }
+        }
+        if (is == null) {
+            throw new IOException("找不到配置文件: " + CONFIG_PATH);
+        }
+        return is;
+    }
+
+    public static Properties loadProperties() throws IOException {
+        Properties pro = new Properties();
+        try (InputStream is = openConfigStream();
+             BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"))) {
+            pro.load(br);
+        }
+        return pro;
+    }
+}

+ 3 - 3
src/com/mes/util/DateLocalUtils.java

@@ -4,7 +4,7 @@ import java.text.SimpleDateFormat;
 import java.util.Date;
 
 public class DateLocalUtils {
-	public static SimpleDateFormat DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//璁剧疆鏃ユ湡鏍煎紡
+	public static SimpleDateFormat DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//璁剧疆鏃ユ湡鏍煎紡
 	public static SimpleDateFormat DATA_FORMAT1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 	
 	
@@ -18,14 +18,14 @@ public class DateLocalUtils {
 		return currentTime;
 	}
 	
-	//获取当前时间,年月日
+	//鑾峰彇褰撳墠鏃堕棿锛屽勾鏈堟棩
 	public static SimpleDateFormat DATA_FORMAT2 = new SimpleDateFormat("yyyy-MM-dd");
 	public static String getCurrentDate() {
 		String currentTime = DATA_FORMAT2.format(new Date());
 		return currentTime;
 	}
 	
-	//获取当前时间,时分秒
+	//鑾峰彇褰撳墠鏃堕棿锛屾椂鍒嗙�
 	public static SimpleDateFormat DATA_FORMAT3 = new SimpleDateFormat("HH:mm:ss");
 	public static String getCurrentTimeHMS() {
 		String currentTime = DATA_FORMAT3.format(new Date());

+ 51 - 0
src/com/mes/util/EncodingUtil.java

@@ -0,0 +1,51 @@
+package com.mes.util;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 控制台编码须与终端代码页一致:UTF-8 终端(chcp 65001)用 UTF-8,默认 CMD(936)用 GBK。
+ * 配置文件/HTTP 仍按 UTF-8 读取。
+ */
+public final class EncodingUtil {
+
+    private static final boolean WINDOWS = System.getProperty("os.name", "").toLowerCase().contains("windows");
+
+    private EncodingUtil() {
+    }
+
+    public static void init() {
+        String consoleCharset = resolveConsoleCharset();
+        System.setProperty("console.charset", consoleCharset);
+        if (System.getProperty("file.encoding") == null || System.getProperty("file.encoding").trim().isEmpty()) {
+            System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
+        }
+        try {
+            PrintStream out = new PrintStream(new FileOutputStream(FileDescriptor.out), true, consoleCharset);
+            PrintStream err = new PrintStream(new FileOutputStream(FileDescriptor.err), true, consoleCharset);
+            System.setOut(out);
+            System.setErr(err);
+        } catch (Exception ignored) {
+        }
+    }
+
+    /** 优先 -Dconsole.charset;否则 file.encoding=UTF-8 时控制台也用 UTF-8;Windows 默认 GBK */
+    private static String resolveConsoleCharset() {
+        String explicit = System.getProperty("console.charset");
+        if (explicit != null && !explicit.trim().isEmpty()) {
+            return explicit.trim();
+        }
+        String fileEnc = System.getProperty("file.encoding");
+        if (fileEnc != null && fileEnc.equalsIgnoreCase("UTF-8")) {
+            return "UTF-8";
+        }
+        return WINDOWS ? "GBK" : "UTF-8";
+    }
+
+    public static Charset consoleCharset() {
+        return Charset.forName(System.getProperty("console.charset", resolveConsoleCharset()));
+    }
+}

+ 20 - 8
src/com/mes/util/ErrorMsg.java

@@ -6,12 +6,6 @@ public class ErrorMsg {
     public static String getErrorMsg(String processMsgRet,String mes_msg){
         String lmsg = "该工件本工位不可加工";
         try{
-
-            String lxpre = ProtocolParam.getLx(mes_msg);
-            String da = mes_msg.substring(74,76);
-            String lastChar = da.substring(0,1);
-            String lx = lxpre + lastChar;
-
             if(processMsgRet.equalsIgnoreCase("OK")||processMsgRet.equalsIgnoreCase("NG")) {
                 lmsg = "该工件本工位已加工,结果:"+processMsgRet;
             }else if(processMsgRet.equalsIgnoreCase("NE")) {
@@ -19,9 +13,9 @@ public class ErrorMsg {
             }else if(processMsgRet.equalsIgnoreCase("NN")) {
                 lmsg = "该工件跳过该工位";
             }else if(processMsgRet.equalsIgnoreCase("QN")) {
-                lmsg = "该工件OP"+ lx+"加工NG";
+                lmsg = formatPriorOprnoMsg(mes_msg, "加工NG");
             }else if(processMsgRet.equalsIgnoreCase("QD")) {
-                lmsg = "该工件OP"+ lx+"未加工";
+                lmsg = formatPriorOprnoMsg(mes_msg, "未加工");
             }else if(processMsgRet.equalsIgnoreCase("NF")) {
                 lmsg = "该工件已合格下线";
             }else if(processMsgRet.equalsIgnoreCase("NR")) {
@@ -60,4 +54,22 @@ public class ErrorMsg {
         }catch (Exception e){ }
         return lmsg;
     }
+
+    /** TCP 报文仅含 RS+结果码,不含具体工位号;三位数字才拼 OPxxx */
+    private static String formatPriorOprnoMsg(String mes_msg, String suffix) {
+        try {
+            if (mes_msg != null && mes_msg.length() >= 75) {
+                String block = mes_msg.substring(70, 75).trim();
+                if (block.matches("\\d{3}[A-Z]{2}")) {
+                    return "该工件OP" + block.substring(0, 3) + suffix;
+                }
+            }
+            String lx = ProtocolParam.getLx(mes_msg);
+            if (lx != null && lx.trim().matches("\\d{3}")) {
+                return "该工件OP" + lx.trim() + suffix;
+            }
+        } catch (Exception ignored) {
+        }
+        return "该工件前序工位" + suffix;
+    }
 }

+ 2 - 2
src/com/mes/util/HttpUtils.java

@@ -27,7 +27,7 @@ public class HttpUtils {
             //设置请求类型
             con.setRequestMethod(requestType);
             //设置请求需要返回的数据类型和字符集类型
-            con.setRequestProperty("Content-Type", "application/json;charset=GBK");  
+            con.setRequestProperty("Content-Type", "application/json;charset=UTF-8");  
             //允许写出
             con.setDoOutput(true);
             //允许读入
@@ -80,7 +80,7 @@ public class HttpUtils {
             //设置请求类型
             con.setRequestMethod(requestType);
             //设置请求需要返回的数据类型和字符集类型
-            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=GBK");
+            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
             //允许写出
             con.setDoOutput(true);
             //允许读入

+ 4 - 3
src/resources/config/config.properties

@@ -1,7 +1,8 @@
-mes.gw=OP80
-# mes.server_ip=127.0.0.1
+mes.gw=OP080
+#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
-mes.gwflag=A
+# 设备运行超时强制结束(分钟),默认 5
+mes.run_timeout_min=5

+ 3 - 1
src/resources/logback.xml

@@ -6,9 +6,10 @@
     <property name="LOG_HOME" value="D:/Logs"/>
     <property name="PROJECT_NAME" value="op050"/>
 
-    <!-- 控制台输出 -->
+    <!-- 控制台:Windows 用 GBK,其它系统 UTF-8(由 EncodingUtil 在启动时设置 console.charset) -->
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
         <encoder>
+            <charset>${console.charset:-UTF-8}</charset>
             <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level  %logger{36} - %msg%n</pattern>
         </encoder>
         <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
@@ -27,6 +28,7 @@
             <totalSizeCap>1GB</totalSizeCap>
         </rollingPolicy>
         <encoder>
+            <charset>UTF-8</charset>
             <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level  %logger{36} - %msg%n</pattern>
         </encoder>
     </appender>