chefz 11 hodín pred
rodič
commit
f8987cb6c1

+ 11 - 0
.project

@@ -14,4 +14,15 @@
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 	</natures>
+	<filteredResources>
+		<filter>
+			<id>1780298192967</id>
+			<name></name>
+			<type>30</type>
+			<matcher>
+				<id>org.eclipse.core.resources.regexFilterMatcher</id>
+				<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
+			</matcher>
+		</filter>
+	</filteredResources>
 </projectDescription>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 769 - 87
src/com/mes/component/WorkJPanelDevice.java


+ 205 - 11
src/com/mes/ui/DataUtil.java

@@ -11,6 +11,8 @@ import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -59,14 +61,21 @@ public class DataUtil {
             String params = "__ajax=json&oprno="+oprno+"&lineSn="+lineSn+"&sn="+sn+"&result="+ret+"&ucode="+user;
             log.info("url="+url);
             log.info("params="+params);
+            // 先落本地未提交记录,失败后可异步重试并在工作记录显示状态
+            JdbcUtils.ensurePendingSubmitRecord(oprno, sn, params);
             String result = doPost(url,params);
             log.info("result="+result);
 
-            if(result.equalsIgnoreCase("false")) {
+            if(result == null || result.equalsIgnoreCase("false")) {
                 return null;
             }else {
-                JdbcUtils.insertData(oprno, "100000", params, "MQDW", sn);
-                return JSONObject.parseObject(result);
+                JSONObject obj = JSONObject.parseObject(result);
+                if (obj != null && obj.get("result") != null
+                        && obj.get("result").toString().equalsIgnoreCase("true")) {
+                    JdbcUtils.markSubmitRecordSuccess(oprno, sn);
+                    JdbcUtils.insertData(oprno, "100000", params, "MQDW", sn);
+                }
+                return obj;
             }
         }catch (Exception e){
             log.info("e="+e.getMessage());
@@ -74,6 +83,47 @@ public class DataUtil {
         }
     }
 
+    /**
+     * 异步提交质量结果:主流程只落库排队后立即返回,HTTP 在后台线程执行,失败由定时器重试。
+     */
+    public static void submitQualityAsync(String sn, String oprno, String ret, String user) {
+        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);
+            String mes_server_ip = pro.getProperty("mes.server_ip");
+            String lineSn = pro.getProperty("mes.line_sn").trim();
+            final String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/pcresult";
+            final String params = "__ajax=json&oprno=" + oprno + "&lineSn=" + lineSn
+                    + "&sn=" + sn + "&result=" + ret + "&ucode=" + user;
+            final String station = oprno;
+            final String productSn = sn;
+            JdbcUtils.ensurePendingSubmitRecord(station, productSn, params);
+            log.info("异步提交已入队: sn={}, oprno={}", productSn, station);
+            new Thread(() -> {
+                try {
+                    String result = doPost(url, params);
+                    log.info("异步提交结果 sn={}, result={}", productSn, result);
+                    if (result == null || result.equalsIgnoreCase("false")) {
+                        return;
+                    }
+                    JSONObject obj = JSONObject.parseObject(result);
+                    if (obj != null && obj.get("result") != null
+                            && obj.get("result").toString().equalsIgnoreCase("true")) {
+                        JdbcUtils.markSubmitRecordSuccess(station, productSn);
+                        JdbcUtils.insertData(station, "100000", params, "MQDW", productSn);
+                    }
+                } catch (Exception e) {
+                    log.error("异步提交异常 sn=" + productSn + ", " + e.getMessage());
+                }
+            }, "submit-quality-" + productSn).start();
+        } catch (Exception e) {
+            log.error("submitQualityAsync入队失败: " + e.getMessage());
+        }
+    }
+
     public static JSONObject sendQuality(String sn,String oprno,String ret,String user,String ps){
         try{
             String enconding = "UTF-8";
@@ -87,14 +137,20 @@ public class DataUtil {
             String params = "__ajax=json&oprno="+oprno+"&lineSn="+lineSn+"&sn="+sn+"&result="+ret+"&ucode="+user+"&ps="+ps;
             log.info("url="+url);
             log.info("params="+params);
+            JdbcUtils.ensurePendingSubmitRecord(oprno, sn, params);
             String result = doPost(url,params);
             log.info("result="+result);
 
-            if(result.equalsIgnoreCase("false")) {
+            if(result == null || result.equalsIgnoreCase("false")) {
                 return null;
             }else {
-                JdbcUtils.insertData(oprno, "100000", params, "MQDW", sn);
-                return JSONObject.parseObject(result);
+                JSONObject obj = JSONObject.parseObject(result);
+                if (obj != null && obj.get("result") != null
+                        && obj.get("result").toString().equalsIgnoreCase("true")) {
+                    JdbcUtils.markSubmitRecordSuccess(oprno, sn);
+                    JdbcUtils.insertData(oprno, "100000", params, "MQDW", sn);
+                }
+                return obj;
             }
         }catch (Exception e){
             log.info("e="+e.getMessage());
@@ -102,6 +158,40 @@ public class DataUtil {
         }
     }
 
+    /** 定时重试本地未提交的质量结果 */
+    public static void retryPendingSubmitRecords() {
+        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);
+            String mes_server_ip = pro.getProperty("mes.server_ip");
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/pcresult";
+            List<ParamReq> pending = JdbcUtils.getPendingSubmitRecords();
+            for (ParamReq item : pending) {
+                String bw = item.getContent();
+                if (bw == null || bw.trim().isEmpty()) {
+                    continue;
+                }
+                log.info("重试提交质量结果 id={}, sn={}, bw={}", item.getId(), item.getSn(), bw);
+                String result = doPost(url, bw);
+                if (result == null || result.equalsIgnoreCase("false")) {
+                    continue;
+                }
+                JSONObject obj = JSONObject.parseObject(result);
+                if (obj != null && obj.get("result") != null
+                        && obj.get("result").toString().equalsIgnoreCase("true")) {
+                    JdbcUtils.markSubmitRecordSuccessById(item.getId());
+                    JdbcUtils.insertData(item.getOprno(), "100000", bw, "MQDW", item.getSn());
+                    log.info("重试提交成功 id={}, sn={}", item.getId(), item.getSn());
+                }
+            }
+        } catch (Exception e) {
+            log.error("retryPendingSubmitRecords异常: " + e.getMessage());
+        }
+    }
+
     public static JSONObject createOrder(String oprno,String wsn1,String user){
         try{
             String enconding = "UTF-8";
@@ -272,20 +362,32 @@ public class DataUtil {
             connection.setReadTimeout(15000);
             connection.setDoOutput(true);
             connection.setDoInput(true);
-            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
             connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
             os = connection.getOutputStream();
-            os.write(param.getBytes());
-            if (connection.getResponseCode() == 200) {
+            os.write(param.getBytes(StandardCharsets.UTF_8));
+            int responseCode = connection.getResponseCode();
+            if (responseCode == 200) {
                 is = connection.getInputStream();
-                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
                 StringBuffer sbf = new StringBuffer();
                 String temp = null;
                 while ((temp = br.readLine()) != null) {
                     sbf.append(temp);
-                    sbf.append("\r\n");
                 }
                 result = sbf.toString();
+            } else {
+                log.error("doPost HTTP {} url={}", responseCode, httpUrl);
+                InputStream errStream = connection.getErrorStream();
+                if (errStream != null) {
+                    br = new BufferedReader(new InputStreamReader(errStream, StandardCharsets.UTF_8));
+                    StringBuffer sbf = new StringBuffer();
+                    String temp;
+                    while ((temp = br.readLine()) != null) {
+                        sbf.append(temp);
+                    }
+                    log.error("doPost error body: {}", sbf.toString());
+                }
             }
         } catch (MalformedURLException e) {
             e.printStackTrace();
@@ -411,4 +513,96 @@ public class DataUtil {
 
         return  oprno;
     }
+
+    /**
+     * 绑定冷板/仓库物料码
+     */
+    public static JSONObject bindWarehouse(String sn, String wsn, String oprno, String userCode, String craft) {
+        try {
+            String mes_server_ip = MesClient.mes_server_ip;
+            String lineSn = MesClient.mes_line_sn;
+            if (mes_server_ip == null || mes_server_ip.isEmpty()) {
+                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_server_ip = pro.getProperty("mes.server_ip");
+                lineSn = pro.getProperty("mes.line_sn").trim();
+            }
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/bindWarehouse";
+            String params = "__ajax=json"
+                    + "&oprno=" + URLEncoder.encode(oprno != null ? oprno : "", "UTF-8")
+                    + "&lineSn=" + URLEncoder.encode(lineSn != null ? lineSn : "", "UTF-8")
+                    + "&ucode=" + URLEncoder.encode(userCode != null ? userCode : "", "UTF-8")
+                    + "&sn=" + URLEncoder.encode(sn != null ? sn : "", "UTF-8")
+                    + "&wsn=" + URLEncoder.encode(wsn != null ? wsn : "", "UTF-8")
+                    + "&craft=" + URLEncoder.encode(craft != null ? craft : "", "UTF-8");
+            if (MesClient.sessionid != null && !MesClient.sessionid.isEmpty()) {
+                params += "&__sid=" + URLEncoder.encode(MesClient.sessionid, "UTF-8");
+            }
+            log.info("bindWarehouse url=" + url);
+            log.info("bindWarehouse params=" + params);
+            String result = doPost(url, params);
+            log.info("bindWarehouse result=" + result);
+            if (result == null || result.trim().isEmpty() || result.equalsIgnoreCase("false")) {
+                return null;
+            }
+            JSONObject retObj = JSONObject.parseObject(result);
+            if (retObj != null && retObj.get("result") != null
+                    && retObj.get("result").toString().equalsIgnoreCase("true")) {
+                JdbcUtils.insertData(oprno, craft, params, "MBDW", sn);
+            }
+            return retObj;
+        } catch (Exception e) {
+            log.error("bindWarehouse error: " + e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 解绑冷板/仓库物料码
+     */
+    public static JSONObject unBindWarehouse(String sn, String wsn, String oprno, String userCode, String craft) {
+        try {
+            String mes_server_ip = MesClient.mes_server_ip;
+            String lineSn = MesClient.mes_line_sn;
+            if (mes_server_ip == null || mes_server_ip.isEmpty()) {
+                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_server_ip = pro.getProperty("mes.server_ip");
+                lineSn = pro.getProperty("mes.line_sn").trim();
+            }
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/unBindWarehouse";
+            String params = "__ajax=json"
+                    + "&oprno=" + URLEncoder.encode(oprno != null ? oprno : "", "UTF-8")
+                    + "&lineSn=" + URLEncoder.encode(lineSn != null ? lineSn : "", "UTF-8")
+                    + "&ucode=" + URLEncoder.encode(userCode != null ? userCode : "", "UTF-8")
+                    + "&sn=" + URLEncoder.encode(sn != null ? sn : "", "UTF-8")
+                    + "&wsn=" + URLEncoder.encode(wsn != null ? wsn : "", "UTF-8")
+                    + "&craft=" + URLEncoder.encode(craft != null ? craft : "", "UTF-8");
+            if (MesClient.sessionid != null && !MesClient.sessionid.isEmpty()) {
+                params += "&__sid=" + URLEncoder.encode(MesClient.sessionid, "UTF-8");
+            }
+            log.info("unBindWarehouse url=" + url);
+            log.info("unBindWarehouse params=" + params);
+            String result = doPost(url, params);
+            log.info("unBindWarehouse result=" + result);
+            if (result == null || result.trim().isEmpty() || result.equalsIgnoreCase("false")) {
+                return null;
+            }
+            JSONObject retObj = JSONObject.parseObject(result);
+            if (retObj != null && retObj.get("result") != null
+                    && retObj.get("result").toString().equalsIgnoreCase("true")) {
+                JdbcUtils.insertData(oprno, craft, params, "MJBW", sn);
+            }
+            return retObj;
+        } catch (Exception e) {
+            log.error("unBindWarehouse error: " + e.getMessage(), e);
+            return null;
+        }
+    }
 }

+ 69 - 9
src/com/mes/ui/MesClient.java

@@ -118,6 +118,7 @@ public class MesClient extends JFrame {
                     startHeartBeatTimer();
 
                     upParams();
+                    startSubmitRetryTimer();
 
 
                 }catch (Exception e){
@@ -128,6 +129,26 @@ public class MesClient extends JFrame {
     }
 
     public static Timer cjTimer3;
+    public static Timer submitRetryTimer;
+
+    /** 定时重试本地未提交的质量结果 */
+    public static void startSubmitRetryTimer() {
+        if (submitRetryTimer != null) {
+            submitRetryTimer.cancel();
+        }
+        submitRetryTimer = new Timer();
+        submitRetryTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                try {
+                    DataUtil.retryPendingSubmitRecords();
+                } catch (Exception e) {
+                    log.error("submit retry timer error: " + e.getMessage());
+                }
+            }
+        }, 5000, 5000);
+    }
+
     public static void upParams() {
         if(cjTimer3!=null) {
             cjTimer3.cancel();
@@ -337,10 +358,13 @@ public class MesClient extends JFrame {
         //首页
         JPanel indexPanelA = new JPanel();
         indexScrollPaneA = new JScrollPane(indexPanelA);
-        if(mes_gw.equals("OP220")){
-            indexPanelA.setLayout(new GridLayout(1,1, 5, 5));
-        }else{
-            indexPanelA.setLayout(new GridLayout(2,2, 5, 5));
+        // 根据工位动态设置布局,OP140 每行 3 个,其他默认每行 2 个
+        if (mes_gw.equals("OP220") || mes_gw.equals("OP190")) {
+            indexPanelA.setLayout(new GridLayout(1, 1, 10, 10));
+        } else if (mes_gw.equals("OP140")) {
+            indexPanelA.setLayout(new GridLayout(0, 3, 10, 10));
+        } else {
+            indexPanelA.setLayout(new GridLayout(0, 2, 10, 10));
         }
         indexPanelA.setBackground(Color.LIGHT_GRAY);
 
@@ -351,6 +375,7 @@ public class MesClient extends JFrame {
             indexPanel20A.setBackground(Color.WHITE);
             workJPanelDeviceA = new WorkJPanelDevice("A");
             workJPanelDeviceA.setBounds(10, 10, workJPanelDeviceA.workWidth, workJPanelDeviceA.workHeight);
+            indexPanel20A.setPreferredSize(new Dimension(workJPanelDeviceA.workWidth + 20, workJPanelDeviceA.workHeight + 20));
             indexPanel20A.add(workJPanelDeviceA);
             indexPanelA.add(indexPanel20A);
 
@@ -359,6 +384,7 @@ public class MesClient extends JFrame {
             indexPanel20B.setBackground(Color.WHITE);
             workJPanelDeviceB = new WorkJPanelDevice("B");
             workJPanelDeviceB.setBounds(10, 10, workJPanelDeviceB.workWidth, workJPanelDeviceB.workHeight);
+            indexPanel20B.setPreferredSize(new Dimension(workJPanelDeviceB.workWidth + 20, workJPanelDeviceB.workHeight + 20));
             indexPanel20B.add(workJPanelDeviceB);
             indexPanelA.add(indexPanel20B);
 
@@ -367,6 +393,7 @@ public class MesClient extends JFrame {
             indexPanel20C.setBackground(Color.WHITE);
             workJPanelDeviceC = new WorkJPanelDevice("C");
             workJPanelDeviceC.setBounds(10, 10, workJPanelDeviceC.workWidth, workJPanelDeviceC.workHeight);
+            indexPanel20C.setPreferredSize(new Dimension(workJPanelDeviceC.workWidth + 20, workJPanelDeviceC.workHeight + 20));
             indexPanel20C.add(workJPanelDeviceC);
             indexPanelA.add(indexPanel20C);
 
@@ -375,6 +402,7 @@ public class MesClient extends JFrame {
             indexPanel20D.setBackground(Color.WHITE);
             workJPanelDeviceD = new WorkJPanelDevice("D");
             workJPanelDeviceD.setBounds(10, 10, workJPanelDeviceD.workWidth, workJPanelDeviceD.workHeight);
+            indexPanel20D.setPreferredSize(new Dimension(workJPanelDeviceD.workWidth + 20, workJPanelDeviceD.workHeight + 20));
             indexPanel20D.add(workJPanelDeviceD);
             indexPanelA.add(indexPanel20D);
 
@@ -383,6 +411,7 @@ public class MesClient extends JFrame {
             indexPanel20E.setBackground(Color.WHITE);
             workJPanelDeviceE = new WorkJPanelDevice("E");
             workJPanelDeviceE.setBounds(10, 10, workJPanelDeviceE.workWidth, workJPanelDeviceE.workHeight);
+            indexPanel20E.setPreferredSize(new Dimension(workJPanelDeviceE.workWidth + 20, workJPanelDeviceE.workHeight + 20));
             indexPanel20E.add(workJPanelDeviceE);
             indexPanelA.add(indexPanel20E);
 
@@ -391,6 +420,7 @@ public class MesClient extends JFrame {
             indexPanel20F.setBackground(Color.WHITE);
             workJPanelDeviceF = new WorkJPanelDevice("F");
             workJPanelDeviceF.setBounds(10, 10, workJPanelDeviceF.workWidth, workJPanelDeviceF.workHeight);
+            indexPanel20F.setPreferredSize(new Dimension(workJPanelDeviceF.workWidth + 20, workJPanelDeviceF.workHeight + 20));
             indexPanel20F.add(workJPanelDeviceF);
             indexPanelA.add(indexPanel20F);
 
@@ -399,6 +429,7 @@ public class MesClient extends JFrame {
             indexPanel20G.setBackground(Color.WHITE);
             workJPanelDeviceG = new WorkJPanelDevice("G");
             workJPanelDeviceG.setBounds(10, 10, workJPanelDeviceG.workWidth, workJPanelDeviceG.workHeight);
+            indexPanel20G.setPreferredSize(new Dimension(workJPanelDeviceG.workWidth + 20, workJPanelDeviceG.workHeight + 20));
             indexPanel20G.add(workJPanelDeviceG);
             indexPanelA.add(indexPanel20G);
         }else if(mes_gw.equals("OP230")){
@@ -407,6 +438,7 @@ public class MesClient extends JFrame {
             indexPanel20A.setBackground(Color.WHITE);
             workJPanelDeviceA = new WorkJPanelDevice("A");
             workJPanelDeviceA.setBounds(10, 10, workJPanelDeviceA.workWidth, workJPanelDeviceA.workHeight);
+            indexPanel20A.setPreferredSize(new Dimension(workJPanelDeviceA.workWidth + 20, workJPanelDeviceA.workHeight + 20));
             indexPanel20A.add(workJPanelDeviceA);
             indexPanelA.add(indexPanel20A);
 
@@ -415,6 +447,7 @@ public class MesClient extends JFrame {
             indexPanel20B.setBackground(Color.WHITE);
             workJPanelDeviceB = new WorkJPanelDevice("B");
             workJPanelDeviceB.setBounds(10, 10, workJPanelDeviceB.workWidth, workJPanelDeviceB.workHeight);
+            indexPanel20B.setPreferredSize(new Dimension(workJPanelDeviceB.workWidth + 20, workJPanelDeviceB.workHeight + 20));
             indexPanel20B.add(workJPanelDeviceB);
             indexPanelA.add(indexPanel20B);
 
@@ -423,6 +456,7 @@ public class MesClient extends JFrame {
             indexPanel20C.setBackground(Color.WHITE);
             workJPanelDeviceC = new WorkJPanelDevice("C");
             workJPanelDeviceC.setBounds(10, 10, workJPanelDeviceC.workWidth, workJPanelDeviceC.workHeight);
+            indexPanel20C.setPreferredSize(new Dimension(workJPanelDeviceC.workWidth + 20, workJPanelDeviceC.workHeight + 20));
             indexPanel20C.add(workJPanelDeviceC);
             indexPanelA.add(indexPanel20C);
 
@@ -431,16 +465,25 @@ public class MesClient extends JFrame {
             indexPanel20D.setBackground(Color.WHITE);
             workJPanelDeviceD = new WorkJPanelDevice("D");
             workJPanelDeviceD.setBounds(10, 10, workJPanelDeviceD.workWidth, workJPanelDeviceD.workHeight);
+            indexPanel20D.setPreferredSize(new Dimension(workJPanelDeviceD.workWidth + 20, workJPanelDeviceD.workHeight + 20));
             indexPanel20D.add(workJPanelDeviceD);
             indexPanelA.add(indexPanel20D);
         }else if(mes_gw.equals("OP190")){
-            JPanel indexPanel20A = new JPanel();
-            indexPanel20A.setLayout(null);
+            JPanel indexPanel20A = new JPanel(new GridBagLayout());
             indexPanel20A.setBackground(Color.WHITE);
             workJPanelDeviceA = new WorkJPanelDevice("A");
-            workJPanelDeviceA.setBounds(10, 10, workJPanelDeviceA.workWidth, workJPanelDeviceA.workHeight);
-            indexPanel20A.add(workJPanelDeviceA);
-            indexPanelA.add(indexPanel20A);
+            GridBagConstraints gbc = new GridBagConstraints();
+            gbc.gridx = 0;
+            gbc.gridy = 0;
+            gbc.anchor = GridBagConstraints.CENTER;
+            gbc.insets = new Insets(16, 16, 16, 16);
+            indexPanel20A.add(workJPanelDeviceA, gbc);
+            indexPanel20A.setPreferredSize(new Dimension(
+                    workJPanelDeviceA.workWidth + 32,
+                    workJPanelDeviceA.workHeight + 32));
+            indexScrollPaneA = new JScrollPane(indexPanel20A);
+            indexScrollPaneA.setBorder(BorderFactory.createEmptyBorder());
+            indexPanelA = indexPanel20A;
         }else if(mes_gw.equals("OP210")){
             // 新增:OP200A 工位面板
             JPanel indexPanel200A = new JPanel();
@@ -448,6 +491,7 @@ public class MesClient extends JFrame {
             indexPanel200A.setBackground(Color.WHITE);
             workJPanelDeviceG = new WorkJPanelDevice("200A"); // 用特殊标识区分
             workJPanelDeviceG.setBounds(10, 10, workJPanelDeviceG.workWidth, workJPanelDeviceG.workHeight);
+            indexPanel200A.setPreferredSize(new Dimension(workJPanelDeviceG.workWidth + 20, workJPanelDeviceG.workHeight + 20));
             indexPanel200A.add(workJPanelDeviceG);
             indexPanelA.add(indexPanel200A);
 
@@ -456,6 +500,7 @@ public class MesClient extends JFrame {
             indexPanel20A.setBackground(Color.WHITE);
             workJPanelDeviceA = new WorkJPanelDevice("A");
             workJPanelDeviceA.setBounds(10, 10, workJPanelDeviceA.workWidth, workJPanelDeviceA.workHeight);
+            indexPanel20A.setPreferredSize(new Dimension(workJPanelDeviceA.workWidth + 20, workJPanelDeviceA.workHeight + 20));
             indexPanel20A.add(workJPanelDeviceA);
             indexPanelA.add(indexPanel20A);
 
@@ -464,6 +509,7 @@ public class MesClient extends JFrame {
             indexPanel20B.setBackground(Color.WHITE);
             workJPanelDeviceB = new WorkJPanelDevice("B");
             workJPanelDeviceB.setBounds(10, 10, workJPanelDeviceB.workWidth, workJPanelDeviceB.workHeight);
+            indexPanel20B.setPreferredSize(new Dimension(workJPanelDeviceB.workWidth + 20, workJPanelDeviceB.workHeight + 20));
             indexPanel20B.add(workJPanelDeviceB);
             indexPanelA.add(indexPanel20B);
 
@@ -472,6 +518,7 @@ public class MesClient extends JFrame {
             indexPanel20C.setBackground(Color.WHITE);
             workJPanelDeviceC = new WorkJPanelDevice("C");
             workJPanelDeviceC.setBounds(10, 10, workJPanelDeviceC.workWidth, workJPanelDeviceC.workHeight);
+            indexPanel20C.setPreferredSize(new Dimension(workJPanelDeviceC.workWidth + 20, workJPanelDeviceC.workHeight + 20));
             indexPanel20C.add(workJPanelDeviceC);
             indexPanelA.add(indexPanel20C);
 
@@ -480,6 +527,19 @@ public class MesClient extends JFrame {
         tabbedPane.addTab("工作面板", null, indexScrollPaneA, null);
         tabbedPane.setEnabledAt(0, true);
 
+        // 如果是 OP190,添加独立的点检和记录面板,与工作面板同级
+        if (mes_gw.equals("OP190")) {
+            JPanel checkPanel = new JPanel();
+            checkPanel.setLayout(new BorderLayout());
+            String checkUrl = "http://" + mes_server_ip + ":8980/js/a/mes/mesProcessCheckRecord/ulist?ucode=1&oprno=OP190A&lineSn=" + mes_line_sn;
+            MesWebView checkWebView = new MesWebView(checkUrl);
+            checkPanel.add(checkWebView, BorderLayout.CENTER);
+            tabbedPane.addTab("开班点检", null, checkPanel, null);
+
+            WorkRecordPanel recordPanel = new WorkRecordPanel("OP190A", mes_line_sn);
+            tabbedPane.addTab("工作记录", null, recordPanel, null);
+        }
+
         tabbedPane.addChangeListener(new ChangeListener() {
             @Override
             public void stateChanged(ChangeEvent e) {

+ 11 - 0
src/com/mes/ui/OprnoUtil.java

@@ -41,6 +41,17 @@ public class OprnoUtil {
         return des;
     }
 
+    /**
+     * 获取子工位/设备面板显示名称(工位号下方副标题)
+     */
+    public static String getStationDes(String lineSn, String oprno, String pos) {
+        oprno = formatOprno(oprno);
+        if (lineSn.equals("XT") && oprno.equals("OP200")) {
+            return "激光焊";
+        }
+        return getGwDes(lineSn, oprno);
+    }
+
     public static String formatOprno(String oprno){
         List<String> lists = new ArrayList<>();
 

+ 9 - 0
src/com/mes/ui/WorkRecordData.java

@@ -10,6 +10,7 @@ public class WorkRecordData {
     private String updateBy;    // 作业人员
     private String updateDate;  // 日期
     private String content;     // 工位结果
+    private String submitState; // 提交状态:已提交 / 未提交
 
     public String getId() {
         return id;
@@ -58,4 +59,12 @@ public class WorkRecordData {
     public void setContent(String content) {
         this.content = content;
     }
+
+    public String getSubmitState() {
+        return submitState;
+    }
+
+    public void setSubmitState(String submitState) {
+        this.submitState = submitState;
+    }
 }

+ 48 - 7
src/com/mes/ui/WorkRecordPanel.java

@@ -3,6 +3,8 @@ package com.mes.ui;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.mes.util.JdbcUtils;
+
 import javax.swing.*;
 import javax.swing.table.DefaultTableCellRenderer;
 import javax.swing.table.DefaultTableModel;
@@ -10,7 +12,9 @@ import javax.swing.table.TableColumn;
 import java.awt.*;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 工作记录面板组件
@@ -21,7 +25,7 @@ public class WorkRecordPanel extends JPanel {
     private static final Logger log = LoggerFactory.getLogger(WorkRecordPanel.class);
 
     // 表格列名
-    private static final String[] COLUMN_NAMES = {"工件码", "工位号", "工位结果", "作业人员", "日期"};
+    private static final String[] COLUMN_NAMES = {"工件码", "工位号", "工位结果", "作业人员", "日期", "状态"};
 
     // 分页配置
     private static final int PAGE_SIZE = 20;
@@ -245,6 +249,9 @@ public class WorkRecordPanel extends JPanel {
         // 日期
         column = table.getColumnModel().getColumn(4);
         column.setPreferredWidth(150);
+        // 状态
+        column = table.getColumnModel().getColumn(5);
+        column.setPreferredWidth(80);
     }
 
     /**
@@ -339,6 +346,8 @@ public class WorkRecordPanel extends JPanel {
 
         if (resp == null || !resp.isResult()) {
             String msg = resp != null ? resp.getMessage() : "请求失败";
+            // 服务端失败时仍展示本地提交记录
+            fillLocalSubmitRows(queryOprno);
             updateUIError(msg);
             return;
         }
@@ -363,16 +372,30 @@ public class WorkRecordPanel extends JPanel {
         }
         statusLabel.setText("工位:" + oprnoDisplay + " 共" + totalCount + "条");
 
-        // 填充表格数据
+        // 填充表格数据:本地未提交置顶,服务端记录标记为已提交
+        Set<String> pendingKeys = new HashSet<>();
+        if (pageNo == 1) {
+            pendingKeys.addAll(fillLocalSubmitRows(queryOprno));
+        }
+
         List<WorkRecordData> list = resp.getList();
         if (list != null && !list.isEmpty()) {
             for (WorkRecordData data : list) {
+                String key = data.getSn() + "|" + data.getOprno();
+                if (pendingKeys.contains(key)) {
+                    continue;
+                }
+                String state = data.getSubmitState();
+                if (state == null || state.isEmpty()) {
+                    state = "已提交";
+                }
                 Object[] row = {
                         data.getSn(),
                         data.getOprno(),
                         data.getContent(),
                         data.getUpdateBy(),
-                        data.getUpdateDate()
+                        data.getUpdateDate(),
+                        state
                 };
                 tableModel.addRow(row);
             }
@@ -382,16 +405,34 @@ public class WorkRecordPanel extends JPanel {
         table.repaint();
     }
 
+    /** 填充本地未提交记录;返回未提交的 sn|oprno 集合 */
+    private Set<String> fillLocalSubmitRows(String queryOprno) {
+        Set<String> pendingKeys = new HashSet<>();
+        String localOprno = isAllStation
+                ? (queryOprno == null || queryOprno.isEmpty() ? null : queryOprno)
+                : oprno;
+        String snFilter = snSearchField != null ? snSearchField.getText().trim() : "";
+        List<String[]> localRows = JdbcUtils.getSubmitRecordRows(localOprno, snFilter, 50);
+        for (String[] row : localRows) {
+            if ("未提交".equals(row[5])) {
+                pendingKeys.add(row[0] + "|" + row[1]);
+                tableModel.addRow(new Object[]{row[0], row[1], row[2], row[3], row[4], row[5]});
+            }
+        }
+        return pendingKeys;
+    }
+
     /**
      * 更新UI显示错误信息
      */
     private void updateUIError(String message) {
-        statusLabel.setText("错误: " + message);
-        pageLabel.setText("0/0");
+        statusLabel.setText("错误: " + message + "(已显示本地提交记录)");
+        pageLabel.setText(pageNo + "/" + Math.max(totalPages, 1));
         setButtonsEnabled(true);
-        btnPrevious.setEnabled(false);
+        btnPrevious.setEnabled(pageNo > 1);
         btnNext.setEnabled(false);
-        tableModel.setRowCount(0);
+        // 不在这里清空表格,保留 fillLocalSubmitRows 已填充的本地记录
+        table.repaint();
     }
 
     /**

+ 209 - 16
src/com/mes/util/JdbcUtils.java

@@ -242,27 +242,220 @@ public class JdbcUtils {
     	return ret;
     }
 
-	// 向 submit_record表 插入提交记录数据
+	// 向 submit_record表 插入提交记录数据(状态 0=未提交)
 	public static boolean insertSubmitRecord(String oprno, String sn, String bw){
-		boolean ret = false;
+		ensurePendingSubmitRecord(oprno, sn, bw);
+		return true;
+	}
+
+	/**
+	 * 确保存在一条未提交记录:已有则更新报文,没有则新增。返回记录 id,失败返回 -1。
+	 */
+	public static int ensurePendingSubmitRecord(String oprno, String sn, String bw) {
+		JdbcUtils.getConn();
+		if (conn == null || oprno == null || sn == null || bw == null) {
+			return -1;
+		}
+		String safeOprno = oprno.replace("'", "''");
+		String safeSn = sn.replace("'", "''");
+		String safeBw = bw.replace("'", "''");
 		String record_time = DateLocalUtils.getCurrentTime();
-		if(conn==null) {
-			ret = false;
-		}else {
+		Statement statement = null;
+		ResultSet rs = null;
+		try {
+			statement = conn.createStatement();
+			rs = statement.executeQuery(
+					"SELECT id FROM submit_record WHERE state = '0' AND oprno = '" + safeOprno
+							+ "' AND sn = '" + safeSn + "' ORDER BY id DESC LIMIT 1");
+			if (rs.next()) {
+				int id = rs.getInt(1);
+				statement.executeUpdate("UPDATE submit_record SET bw = '" + safeBw
+						+ "', record_time = '" + record_time + "' WHERE id = " + id);
+				return id;
+			}
+			statement.executeUpdate(
+					"INSERT INTO submit_record (oprno, sn, bw, record_time, state) VALUES('"
+							+ safeOprno + "', '" + safeSn + "', '" + safeBw + "', '" + record_time + "', '0')");
+			rs = statement.executeQuery("SELECT last_insert_rowid()");
+			if (rs.next()) {
+				return rs.getInt(1);
+			}
+		} catch (SQLException e) {
+			log.error("ensurePendingSubmitRecord失败", e);
+		} finally {
 			try {
-				Statement statement=conn.createStatement();
-				String insertSQL = "INSERT INTO submit_record (oprno, sn, bw, record_time, state)" +
-						"VALUES('" + oprno + "', '" + sn + "', '" + bw + "', '" + record_time + "', '0')";
-				statement.executeUpdate(insertSQL);
-				statement.close();
-				ret = true;
-				log.info("向submit_record表插入数据成功: " + insertSQL);
-			} catch (SQLException e) {
-				ret = false;
-				log.info("向submit_record表插入数据失败");
+				if (rs != null) rs.close();
+				if (statement != null) statement.close();
+			} catch (SQLException ignore) {
 			}
 		}
-		return ret;
+		return -1;
+	}
+
+	public static boolean markSubmitRecordSuccess(String oprno, String sn) {
+		JdbcUtils.getConn();
+		if (conn == null || oprno == null || sn == null) {
+			return false;
+		}
+		try {
+			Statement statement = conn.createStatement();
+			statement.executeUpdate("UPDATE submit_record SET state = '1' WHERE state = '0' AND oprno = '"
+					+ oprno.replace("'", "''") + "' AND sn = '" + sn.replace("'", "''") + "'");
+			statement.close();
+			return true;
+		} catch (SQLException e) {
+			log.error("markSubmitRecordSuccess失败", e);
+			return false;
+		}
+	}
+
+	public static boolean markSubmitRecordSuccessById(int id) {
+		JdbcUtils.getConn();
+		if (conn == null || id <= 0) {
+			return false;
+		}
+		try {
+			Statement statement = conn.createStatement();
+			statement.executeUpdate("UPDATE submit_record SET state = '1' WHERE id = " + id);
+			statement.close();
+			return true;
+		} catch (SQLException e) {
+			log.error("markSubmitRecordSuccessById失败", e);
+			return false;
+		}
+	}
+
+	public static boolean hasPendingSubmitRecord(String oprno, String sn) {
+		JdbcUtils.getConn();
+		if (conn == null || oprno == null || sn == null) {
+			return false;
+		}
+		Statement stmt = null;
+		ResultSet ret = null;
+		try {
+			stmt = conn.createStatement();
+			ret = stmt.executeQuery(
+					"SELECT id FROM submit_record WHERE state = '0' AND oprno = '"
+							+ oprno.replace("'", "''") + "' AND sn = '" + sn.replace("'", "''")
+							+ "' LIMIT 1");
+			return ret.next();
+		} catch (SQLException e) {
+			log.error("hasPendingSubmitRecord失败", e);
+			return false;
+		} finally {
+			try {
+				if (ret != null) ret.close();
+				if (stmt != null) stmt.close();
+			} catch (SQLException ignore) {
+			}
+		}
+	}
+
+	public static boolean hasSubmittedRecord(String oprno, String sn) {
+		JdbcUtils.getConn();
+		if (conn == null || oprno == null || sn == null) {
+			return false;
+		}
+		Statement stmt = null;
+		ResultSet ret = null;
+		try {
+			stmt = conn.createStatement();
+			ret = stmt.executeQuery(
+					"SELECT id FROM submit_record WHERE state = '1' AND oprno = '"
+							+ oprno.replace("'", "''") + "' AND sn = '" + sn.replace("'", "''")
+							+ "' ORDER BY id DESC LIMIT 1");
+			return ret.next();
+		} catch (SQLException e) {
+			log.error("hasSubmittedRecord失败", e);
+			return false;
+		} finally {
+			try {
+				if (ret != null) ret.close();
+				if (stmt != null) stmt.close();
+			} catch (SQLException ignore) {
+			}
+		}
+	}
+
+	/** 未提交的结果报文,供定时重试 */
+	public static List<ParamReq> getPendingSubmitRecords() {
+		List<ParamReq> list = new ArrayList<>();
+		JdbcUtils.getConn();
+		if (conn == null) {
+			return list;
+		}
+		Statement stmt = null;
+		ResultSet ret = null;
+		try {
+			stmt = conn.createStatement();
+			ret = stmt.executeQuery(
+					"SELECT id, oprno, sn, bw FROM submit_record WHERE state = '0' ORDER BY id ASC LIMIT 50");
+			while (ret.next()) {
+				ParamReq item = new ParamReq();
+				item.setId(ret.getInt(1));
+				item.setOprno(ret.getString(2));
+				item.setSn(ret.getString(3));
+				item.setContent(ret.getString(4));
+				list.add(item);
+			}
+		} catch (SQLException e) {
+			log.error("getPendingSubmitRecords失败", e);
+		} finally {
+			try {
+				if (ret != null) ret.close();
+				if (stmt != null) stmt.close();
+			} catch (SQLException ignore) {
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * 本地提交记录(含状态),用于工作记录页展示。
+	 * state: 0未提交 1已提交
+	 */
+	public static List<String[]> getSubmitRecordRows(String oprnoFilter, String snFilter, int limit) {
+		List<String[]> rows = new ArrayList<>();
+		JdbcUtils.getConn();
+		if (conn == null) {
+			return rows;
+		}
+		Statement stmt = null;
+		ResultSet ret = null;
+		try {
+			StringBuilder sql = new StringBuilder(
+					"SELECT sn, oprno, bw, record_time, state FROM submit_record WHERE 1=1");
+			if (oprnoFilter != null && !oprnoFilter.trim().isEmpty()) {
+				sql.append(" AND oprno = '").append(oprnoFilter.replace("'", "''")).append("'");
+			}
+			if (snFilter != null && !snFilter.trim().isEmpty()) {
+				sql.append(" AND sn LIKE '%").append(snFilter.replace("'", "''")).append("%'");
+			}
+			sql.append(" ORDER BY id DESC LIMIT ").append(limit > 0 ? limit : 100);
+			stmt = conn.createStatement();
+			ret = stmt.executeQuery(sql.toString());
+			while (ret.next()) {
+				String state = ret.getString(5);
+				String stateText = "1".equals(state) ? "已提交" : "未提交";
+				rows.add(new String[]{
+						ret.getString(1),
+						ret.getString(2),
+						"",
+						"",
+						ret.getString(4),
+						stateText
+				});
+			}
+		} catch (SQLException e) {
+			log.error("getSubmitRecordRows失败", e);
+		} finally {
+			try {
+				if (ret != null) ret.close();
+				if (stmt != null) stmt.close();
+			} catch (SQLException ignore) {
+			}
+		}
+		return rows;
 	}
 
 

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

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