hfy 2 недель назад
Родитель
Сommit
53c57f6f1a

+ 4 - 1
.classpath

@@ -10,7 +10,7 @@
 	<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/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"/>
@@ -21,4 +21,7 @@
 	<classpathentry kind="lib" path="lib/sqlite-jdbc-3.36.0.3.jar"/>
 	<classpathentry kind="lib" path="lib/commons-codec-1.15.jar"/>
 	<classpathentry kind="lib" path="lib/fastjson2-2.0.16.jar"/>
+	<classpathentry kind="lib" path="lib/iot-communication-1.5.3.jar"/>
+	<classpathentry kind="lib" path="lib/logback-classic-1.2.13.jar"/>
+	<classpathentry kind="lib" path="lib/logback-core-1.2.13.jar"/>
 </classpath>

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

@@ -2,6 +2,7 @@ package com.mes.ui;
 
 import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
+import com.mes.util.ConfigUtils;
 import com.mes.util.JdbcUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -18,6 +19,10 @@ import java.util.Properties;
 public class DataUtil {
     public static final Logger log = LoggerFactory.getLogger(DataUtil.class);
 
+    private static Properties loadConfig() throws IOException {
+        return ConfigUtils.loadProperties();
+    }
+
     //删除当前工位工件码
     public static JSONObject delCurSn(String oprno,String serverIp,String lineSn){
         try {
@@ -48,11 +53,7 @@ public class DataUtil {
 
     public static JSONObject checkQuality(String sn, 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);
+            Properties pro = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -84,11 +85,7 @@ public class DataUtil {
 
     public static JSONObject sendQuality(String sn,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);
+            Properties pro = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -169,11 +166,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 = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -195,11 +188,7 @@ public class DataUtil {
 
     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 = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String oprno = pro.getProperty("mes.gw").trim();
             String lineSn = pro.getProperty("mes.line_sn").trim();
@@ -290,11 +279,7 @@ public class DataUtil {
     public static WorkRecordResp getWorkRecordList(String oprno, String sn, int pageNo, int pageSize) {
         WorkRecordResp resp = new WorkRecordResp();
         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 = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String lineSn = pro.getProperty("mes.line_sn").trim();
             
@@ -371,11 +356,7 @@ public class DataUtil {
     public static CheckRecordResp getCheckRecordList(String oprno, int pageNo, int pageSize) {
         CheckRecordResp resp = new CheckRecordResp();
         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 = loadConfig();
             String mes_server_ip = pro.getProperty("mes.server_ip");
             String lineSn = pro.getProperty("mes.line_sn").trim();
             

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

@@ -22,7 +22,7 @@ public class LoginFarme extends JFrame {
     static JButton scanLoginButton = new JButton("扫  码  登  录");
 
     public LoginFarme(){
-        setTitle("MES系统客户端:"+"工作站1");
+        setTitle("MES系统客户端:"+"工作站");
 
         ImageIcon bg = new ImageIcon(MesClient.class.getResource("/background.png"));
         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

+ 2 - 5
src/com/mes/ui/MesClient.java

@@ -3,6 +3,7 @@ package com.mes.ui;
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.component.MesRadio;
 import com.mes.component.MesWebView;
+import com.mes.util.ConfigUtils;
 import com.mes.util.DateLocalUtils;
 import com.mes.util.HttpUtils;
 import com.mes.util.JdbcUtils;
@@ -112,11 +113,7 @@ public class MesClient extends JFrame {
 
     //读配置文件
     public 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);
+        Properties pro = ConfigUtils.loadProperties();
         mes_gw =  pro.getProperty("mes.gw");
         mes_server_ip = pro.getProperty("mes.server_ip");
         mes_line_sn = pro.getProperty("mes.line_sn");

+ 13 - 2
src/com/mes/ui/MultiStationClient.java

@@ -2,6 +2,7 @@ package com.mes.ui;
 
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.component.MesWebView;
+import com.mes.util.ConfigUtils;
 import com.mes.util.JdbcUtils;
 import com.mes.util.StationConfig;
 import org.slf4j.Logger;
@@ -16,6 +17,7 @@ import java.awt.event.WindowEvent;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
 
 import static com.mes.ui.MesClient.mes_server_ip;
 import static com.mes.ui.MesClient.resetScanA;
@@ -42,14 +44,23 @@ public class MultiStationClient extends JFrame {
 
 
     public static void main(String[] args) {
-        if (LockUtil.getInstance().isAppActive()) {
+     /*   if (LockUtil.getInstance().isAppActive()) {
             JOptionPane.showMessageDialog(null, "已有一个程序在运行,程序退出");
             return;
-        }
+        }*/
 
         EventQueue.invokeLater(() -> {
             try {
                 MesClient.readProperty();
+                Properties props = ConfigUtils.loadProperties();
+                log.info("client config={}, stations={}, proxyEnabled={}, proxyBaseUrl={}",
+                        ConfigUtils.getConfigPath(),
+                        props.getProperty("mes.stations", ""),
+                        props.getProperty("plc.proxy.enabled", "false"),
+                        props.getProperty("plc.proxy.base_url", ""));
+                System.out.println("client config=" + ConfigUtils.getConfigPath()
+                        + ", stations=" + props.getProperty("mes.stations", "")
+                        + ", proxy=" + props.getProperty("plc.proxy.base_url", ""));
                 JdbcUtils.getConn();
                 welcomeWin = new LoginFarme();
                 welcomeWin.setVisible(true);

+ 10 - 0
src/com/mes/ui/Workstation1ClientMain.java

@@ -0,0 +1,10 @@
+package com.mes.ui;
+
+import com.mes.util.ConfigUtils;
+
+public class Workstation1ClientMain {
+    public static void main(String[] args) {
+        System.setProperty(ConfigUtils.CONFIG_PROPERTY, "config/config-workstation1.properties");
+        MultiStationClient.main(args);
+    }
+}

+ 10 - 0
src/com/mes/ui/Workstation2ClientMain.java

@@ -0,0 +1,10 @@
+package com.mes.ui;
+
+import com.mes.util.ConfigUtils;
+
+public class Workstation2ClientMain {
+    public static void main(String[] args) {
+        System.setProperty(ConfigUtils.CONFIG_PROPERTY, "config/config-workstation2.properties");
+        MultiStationClient.main(args);
+    }
+}

+ 92 - 0
src/com/mes/util/ConfigUtils.java

@@ -0,0 +1,92 @@
+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 ConfigUtils {
+
+    public static final String DEFAULT_CONFIG_PATH = "config/config.properties";
+    public static final String CONFIG_PROPERTY = "mes.config";
+
+    private ConfigUtils() {
+    }
+
+    public static String getConfigPath() {
+        String configPath = System.getProperty(CONFIG_PROPERTY);
+        if (configPath == null || configPath.trim().isEmpty()) {
+            return DEFAULT_CONFIG_PATH;
+        }
+        return configPath.trim();
+    }
+
+    public static Properties loadProperties() throws IOException {
+        return loadProperties(getConfigPath());
+    }
+
+    public static Properties loadPropertiesUnchecked() {
+        try {
+            return loadProperties();
+        } catch (IOException e) {
+            throw new RuntimeException("load config failed: " + getConfigPath(), e);
+        }
+    }
+
+    public static Properties loadProperties(String configPath) throws IOException {
+        Properties props = new Properties();
+        String normalizedPath = normalizeConfigPath(configPath);
+        if (!DEFAULT_CONFIG_PATH.equals(normalizedPath)) {
+            try (InputStream defaults = openConfig(DEFAULT_CONFIG_PATH)) {
+                if (defaults != null) {
+                    props.load(new BufferedReader(new InputStreamReader(defaults, "UTF-8")));
+                }
+            }
+        }
+        try (InputStream is = openConfig(normalizedPath)) {
+            if (is == null) {
+                throw new IOException("config not found: " + normalizedPath);
+            }
+            props.load(new BufferedReader(new InputStreamReader(is, "UTF-8")));
+        }
+        return props;
+    }
+
+    private static InputStream openConfig(String configPath) throws IOException {
+        if (configPath == null || configPath.trim().isEmpty()) {
+            configPath = DEFAULT_CONFIG_PATH;
+        }
+
+        File file = new File(configPath);
+        if (file.exists() && file.isFile()) {
+            return new FileInputStream(file);
+        }
+
+        String resourcePath = configPath;
+        if (resourcePath.startsWith("classpath:")) {
+            resourcePath = resourcePath.substring("classpath:".length());
+        }
+        while (resourcePath.startsWith("/") || resourcePath.startsWith("\\")) {
+            resourcePath = resourcePath.substring(1);
+        }
+        resourcePath = resourcePath.replace('\\', '/');
+        return ClassLoader.getSystemResourceAsStream(resourcePath);
+    }
+
+    private static String normalizeConfigPath(String configPath) {
+        if (configPath == null || configPath.trim().isEmpty()) {
+            return DEFAULT_CONFIG_PATH;
+        }
+        String normalized = configPath.trim().replace('\\', '/');
+        if (normalized.startsWith("classpath:")) {
+            normalized = normalized.substring("classpath:".length());
+        }
+        while (normalized.startsWith("/")) {
+            normalized = normalized.substring(1);
+        }
+        return normalized;
+    }
+}

+ 97 - 29
src/com/mes/util/PLCUtils.java

@@ -116,6 +116,14 @@ public class PLCUtils {
     }
 
     public static StationStatus readStationStatus(StationConfig config) {
+        if (PlcProxyClient.isEnabled()) {
+            return new StationStatus(
+                    readBoolean(config, config.getAllowStart()),
+                    readBoolean(config, config.getAllowDown()),
+                    readBoolean(config, config.getStartMethod()),
+                    readBoolean(config, config.getStopMethod()),
+                    readBoolean(config, config.getFault()));
+        }
         synchronized (getPlcLock(config)) {
             McPLC plc = getCachedPlc(config);
             try {
@@ -205,6 +213,25 @@ public class PLCUtils {
             return "";
         }
 
+        if (PlcProxyClient.isEnabled()) {
+            try {
+                if ("arc".equalsIgnoreCase(type)) {
+                    return buildArcWeldContent(null, config).toJSONString();
+                }
+                if ("spot".equalsIgnoreCase(type)) {
+                    return buildSpotWeldContent(null, config).toJSONString();
+                }
+                if ("signal".equalsIgnoreCase(type)) {
+                    return buildSignalContent(null, config).toJSONString();
+                }
+                log.info("鏈厤缃宸ヤ綅鐨凱LC鍙傛暟鐐逛綅锛屽伐浣?{}", config.getGw());
+                return "";
+            } catch (Exception e) {
+                log.error("璇诲彇PLC鍙傛暟澶辫触锛屽伐浣?" + config.getGw(), e);
+                return "";
+            }
+        }
+
         synchronized (getPlcLock(config)) {
             McPLC plc = getCachedPlc(config);
             try {
@@ -287,6 +314,11 @@ public class PLCUtils {
         private void sampleLoop() {
             while (running) {
                 try {
+                    if (PlcProxyClient.isEnabled()) {
+                        addSample(readWeldingSample(null, config));
+                        sleepQuietly(sampleIntervalMs);
+                        continue;
+                    }
                     synchronized (getPlcLock(config)) {
                         McPLC plc = getCachedPlc(config);
                         try {
@@ -319,6 +351,10 @@ public class PLCUtils {
                 }
             }
             try {
+                if (PlcProxyClient.isEnabled()) {
+                    addSample(readWeldingSample(null, config));
+                    return;
+                }
                 synchronized (getPlcLock(config)) {
                     McPLC plc = getCachedPlc(config);
                     try {
@@ -373,6 +409,16 @@ public class PLCUtils {
             return;
         }
 
+        if (PlcProxyClient.isEnabled()) {
+            try {
+                PlcProxyClient.writeBoolean(config, address, value);
+                return;
+            } catch (RuntimeException e) {
+                logPlcFailure("proxyWriteBoolean", config, address, null, e);
+                throw e;
+            }
+        }
+
         synchronized (getPlcLock(config)) {
             McPLC plc = getCachedPlc(config);
             try {
@@ -388,6 +434,18 @@ public class PLCUtils {
 
     // 读取单个 M 点状态,例如开始加工、加工结束。
     private static boolean readBoolean(StationConfig config, String address) {
+        if (isEmpty(address)) {
+            log.warn("PLC璇诲彇璺宠繃锛氱偣浣嶅湴鍧€涓虹┖锛屽伐浣?{}", config == null ? "" : config.getGw());
+            return false;
+        }
+        if (PlcProxyClient.isEnabled()) {
+            try {
+                return PlcProxyClient.readBoolean(config, address);
+            } catch (RuntimeException e) {
+                logPlcFailure("proxyReadBoolean", config, address, null, e);
+                throw e;
+            }
+        }
         synchronized (getPlcLock(config)) {
             McPLC plc = getCachedPlc(config);
             try {
@@ -460,34 +518,34 @@ public class PLCUtils {
     private static JSONObject buildArcWeldSample(McPLC plc, StationConfig config) {
         JSONObject sample = new JSONObject();
         sample.put("time", currentTime());
-        addScaledNumber(plc, sample, "robot1Current", config.getRobot1Current(), 1, false);
-        addScaledNumber(plc, sample, "robot1Voltage", config.getRobot1Voltage(), 10, false);
-        addScaledNumber(plc, sample, "robot1WeldTime", config.getRobot1Time(), 20, true);
-        addScaledNumber(plc, sample, "robot2Current", config.getRobot2Current(), 1, false);
-        addScaledNumber(plc, sample, "robot2Voltage", config.getRobot2Voltage(), 10, false);
-        addScaledNumber(plc, sample, "robot2WeldTime", config.getRobot2Time(), 20, true);
+        addScaledNumber(plc, config, sample, "robot1Current", config.getRobot1Current(), 1, false);
+        addScaledNumber(plc, config, sample, "robot1Voltage", config.getRobot1Voltage(), 10, false);
+        addScaledNumber(plc, config, sample, "robot1WeldTime", config.getRobot1Time(), 20, true);
+        addScaledNumber(plc, config, sample, "robot2Current", config.getRobot2Current(), 1, false);
+        addScaledNumber(plc, config, sample, "robot2Voltage", config.getRobot2Voltage(), 10, false);
+        addScaledNumber(plc, config, sample, "robot2WeldTime", config.getRobot2Time(), 20, true);
         return sample;
     }
 
     private static JSONObject buildSpotWeldSample(McPLC plc, StationConfig config) {
         JSONObject sample = new JSONObject();
         sample.put("time", currentTime());
-        addIntNumber(plc, sample, "robot1Pressure", config.getRobot1Pressure());
-        addIntNumber(plc, sample, "robot2Pressure", config.getRobot2Pressure());
-        addScaledNumber(plc, sample, "robot1Current", config.getRobot1Current(), 100, false);
-        addScaledNumber(plc, sample, "robot1Voltage", config.getRobot1Voltage(), 10, false);
-        addScaledNumber(plc, sample, "robot1WeldTime", config.getRobot1Time(), 20, true);
-        addScaledNumber(plc, sample, "robot2Current", config.getRobot2Current(), 100, false);
-        addScaledNumber(plc, sample, "robot2Voltage", config.getRobot2Voltage(), 10, false);
-        addScaledNumber(plc, sample, "robot2WeldTime", config.getRobot2Time(), 20, true);
+        addIntNumber(plc, config, sample, "robot1Pressure", config.getRobot1Pressure());
+        addIntNumber(plc, config, sample, "robot2Pressure", config.getRobot2Pressure());
+        addScaledNumber(plc, config, sample, "robot1Current", config.getRobot1Current(), 100, false);
+        addScaledNumber(plc, config, sample, "robot1Voltage", config.getRobot1Voltage(), 10, false);
+        addScaledNumber(plc, config, sample, "robot1WeldTime", config.getRobot1Time(), 20, true);
+        addScaledNumber(plc, config, sample, "robot2Current", config.getRobot2Current(), 100, false);
+        addScaledNumber(plc, config, sample, "robot2Voltage", config.getRobot2Voltage(), 10, false);
+        addScaledNumber(plc, config, sample, "robot2WeldTime", config.getRobot2Time(), 20, true);
 
         JSONObject spot = new JSONObject();
         JSONObject robot1 = new JSONObject();
-        robot1.put("startPoints", readPointNumbers(plc, config.getRobot1Start(), 50));
-        robot1.put("finishPoints", readPointNumbers(plc, config.getRobot1Done(), 50));
+        robot1.put("startPoints", readPointNumbers(plc, config, config.getRobot1Start(), 50));
+        robot1.put("finishPoints", readPointNumbers(plc, config, config.getRobot1Done(), 50));
         JSONObject robot2 = new JSONObject();
-        robot2.put("startPoints", readPointNumbers(plc, config.getRobot2Start(), 50));
-        robot2.put("finishPoints", readPointNumbers(plc, config.getRobot2Done(), 50));
+        robot2.put("startPoints", readPointNumbers(plc, config, config.getRobot2Start(), 50));
+        robot2.put("finishPoints", readPointNumbers(plc, config, config.getRobot2Done(), 50));
         spot.put("robot1", robot1);
         spot.put("robot2", robot2);
         sample.put("spot", spot);
@@ -500,21 +558,24 @@ public class PLCUtils {
 
         JSONObject spot = new JSONObject();
         JSONObject robot1 = new JSONObject();
-        robot1.put("startPoints", readPointNumbers(plc, config.getRobot1Start(), 50));
-        robot1.put("finishPoints", readPointNumbers(plc, config.getRobot1Done(), 50));
+        robot1.put("startPoints", readPointNumbers(plc, config, config.getRobot1Start(), 50));
+        robot1.put("finishPoints", readPointNumbers(plc, config, config.getRobot1Done(), 50));
         JSONObject robot2 = new JSONObject();
-        robot2.put("startPoints", readPointNumbers(plc, config.getRobot2Start(), 50));
-        robot2.put("finishPoints", readPointNumbers(plc, config.getRobot2Done(), 50));
+        robot2.put("startPoints", readPointNumbers(plc, config, config.getRobot2Start(), 50));
+        robot2.put("finishPoints", readPointNumbers(plc, config, config.getRobot2Done(), 50));
         spot.put("robot1", robot1);
         spot.put("robot2", robot2);
         sample.put("spot", spot);
         return sample;
     }
 
-    private static JSONArray readPointNumbers(McPLC plc, String address, int count) {
+    private static JSONArray readPointNumbers(McPLC plc, StationConfig config, String address, int count) {
         if (isEmpty(address)) {
             return new JSONArray();
         }
+        if (PlcProxyClient.isEnabled()) {
+            return toPointNumbers(PlcProxyClient.readBooleans(config, address, count));
+        }
         return toPointNumbers(plc.readBoolean(address, count));
     }
 
@@ -586,7 +647,7 @@ public class PLCUtils {
 
     private static void addInt16(McPLC plc, List<String> values, String name, String address) {
         if (!isEmpty(address)) {
-            values.add(name + "=" + plc.readInt16(address));
+            values.add(name + "=" + readInt16(plc, null, address));
         }
     }
 
@@ -594,27 +655,34 @@ public class PLCUtils {
         if (isEmpty(address)) {
             return;
         }
-        short raw = plc.readInt16(address);
+        short raw = readInt16(plc, null, address);
         values.add(name + "原始值=" + raw);
         double value = multiply ? raw * factor : raw * 1.0 / factor;
         values.add(name + "=" + String.format(Locale.ROOT, "%.2f", value));
     }
 
-    private static void addIntNumber(McPLC plc, JSONObject sample, String name, String address) {
+    private static void addIntNumber(McPLC plc, StationConfig config, JSONObject sample, String name, String address) {
         if (!isEmpty(address)) {
-            sample.put(name, plc.readInt16(address));
+            sample.put(name, readInt16(plc, config, address));
         }
     }
 
-    private static void addScaledNumber(McPLC plc, JSONObject sample, String name, String address, int factor, boolean multiply) {
+    private static void addScaledNumber(McPLC plc, StationConfig config, JSONObject sample, String name, String address, int factor, boolean multiply) {
         if (isEmpty(address)) {
             return;
         }
-        short raw = plc.readInt16(address);
+        short raw = readInt16(plc, config, address);
         double value = multiply ? raw * factor : raw * 1.0 / factor;
         sample.put(name, value);
     }
 
+    private static short readInt16(McPLC plc, StationConfig config, String address) {
+        if (PlcProxyClient.isEnabled()) {
+            return PlcProxyClient.readInt16(config, address);
+        }
+        return plc.readInt16(address);
+    }
+
     private static JSONArray toPointNumbers(List<Boolean> values) {
         JSONArray points = new JSONArray();
         for (int i = 0; i < values.size(); i++) {

+ 166 - 0
src/com/mes/util/PlcProxyClient.java

@@ -0,0 +1,166 @@
+package com.mes.util;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URLEncoder;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+public class PlcProxyClient {
+
+    private static final String DEFAULT_BASE_URL = "http://127.0.0.1:18080";
+    private static final int DEFAULT_CONNECT_TIMEOUT_MS = 2000;
+    private static final int DEFAULT_READ_TIMEOUT_MS = 5000;
+
+    private static Properties cachedProps;
+
+    private PlcProxyClient() {
+    }
+
+    public static boolean isEnabled() {
+        return Boolean.parseBoolean(getProperty("plc.proxy.enabled", "false"));
+    }
+
+    public static boolean readBoolean(StationConfig config, String address) {
+        JSONObject json = request("/readBoolean", config, address, null, null);
+        return json.getBooleanValue("value");
+    }
+
+    public static List<Boolean> readBooleans(StationConfig config, String address, int count) {
+        JSONObject json = request("/readBooleans", config, address, count, null);
+        JSONArray values = json.getJSONArray("value");
+        List<Boolean> result = new ArrayList<>();
+        if (values == null) {
+            return result;
+        }
+        for (int i = 0; i < values.size(); i++) {
+            Object value = values.get(i);
+            result.add(Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value)));
+        }
+        return result;
+    }
+
+    public static short readInt16(StationConfig config, String address) {
+        JSONObject json = request("/readInt16", config, address, null, null);
+        return (short) json.getIntValue("value");
+    }
+
+    public static void writeBoolean(StationConfig config, String address, boolean value) {
+        request("/writeBoolean", config, address, null, Boolean.valueOf(value));
+    }
+
+    private static JSONObject request(String path, StationConfig config, String address, Integer count, Boolean value) {
+        HttpURLConnection connection = null;
+        try {
+            URL url = new URL(buildUrl(path, config, address, count, value));
+            connection = (HttpURLConnection) url.openConnection();
+            connection.setRequestMethod("GET");
+            connection.setConnectTimeout(parseInt(getProperty("plc.proxy.connect_timeout_ms", ""), DEFAULT_CONNECT_TIMEOUT_MS));
+            connection.setReadTimeout(parseInt(getProperty("plc.proxy.read_timeout_ms", ""), DEFAULT_READ_TIMEOUT_MS));
+            connection.setDoInput(true);
+
+            int status = connection.getResponseCode();
+            InputStream stream = status >= 200 && status < 300 ? connection.getInputStream() : connection.getErrorStream();
+            String body = readBody(stream);
+            JSONObject json = JSONObject.parseObject(body);
+            if (json == null || !json.getBooleanValue("success")) {
+                String message = json == null ? body : json.getString("message");
+                throw new RuntimeException("PLC proxy request failed: " + message);
+            }
+            return json;
+        } catch (Exception e) {
+            throw new RuntimeException("PLC proxy request error, path=" + path + ", address=" + address, e);
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    private static String buildUrl(String path, StationConfig config, String address, Integer count, Boolean value) throws Exception {
+        StringBuilder url = new StringBuilder(trimTrailingSlash(getProperty("plc.proxy.base_url", DEFAULT_BASE_URL)));
+        url.append(path).append("?plc=").append(encode(plcKey(config)));
+        url.append("&address=").append(encode(address));
+        if (config != null && !isEmpty(config.getPlcSeries())) {
+            url.append("&series=").append(encode(config.getPlcSeries()));
+        }
+        if (config != null && !isEmpty(config.getPlcFrame())) {
+            url.append("&frame=").append(encode(config.getPlcFrame()));
+        }
+        if (count != null) {
+            url.append("&count=").append(count.intValue());
+        }
+        if (value != null) {
+            url.append("&value=").append(value.booleanValue());
+        }
+        return url.toString();
+    }
+
+    private static String readBody(InputStream stream) throws Exception {
+        if (stream == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        try (BufferedReader br = new BufferedReader(new InputStreamReader(stream, "UTF-8"))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                sb.append(line);
+            }
+        }
+        return sb.toString();
+    }
+
+    private static String getProperty(String key, String defaultValue) {
+        Properties props = loadProperties();
+        return props.getProperty(key, defaultValue);
+    }
+
+    private static synchronized Properties loadProperties() {
+        if (cachedProps != null) {
+            return cachedProps;
+        }
+        Properties props = ConfigUtils.loadPropertiesUnchecked();
+        cachedProps = props;
+        return cachedProps;
+    }
+
+    private static String plcKey(StationConfig config) {
+        if (config == null || isEmpty(config.getPlcIp())) {
+            return "192.168.0.35:8000";
+        }
+        return config.getPlcIp() + ":" + config.getPlcPort();
+    }
+
+    private static String trimTrailingSlash(String value) {
+        if (value == null) {
+            return DEFAULT_BASE_URL;
+        }
+        while (value.endsWith("/")) {
+            value = value.substring(0, value.length() - 1);
+        }
+        return value;
+    }
+
+    private static String encode(String value) throws Exception {
+        return URLEncoder.encode(value == null ? "" : value, "UTF-8");
+    }
+
+    private static int parseInt(String value, int defaultValue) {
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    private static boolean isEmpty(String value) {
+        return value == null || value.trim().isEmpty();
+    }
+}

+ 323 - 0
src/com/mes/util/PlcProxyServer.java

@@ -0,0 +1,323 @@
+package com.mes.util;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.github.xingshuangs.iot.protocol.melsec.enums.EMcFrameType;
+import com.github.xingshuangs.iot.protocol.melsec.enums.EMcSeries;
+import com.github.xingshuangs.iot.protocol.melsec.service.McPLC;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executors;
+
+public class PlcProxyServer {
+
+    private static final Logger log = LoggerFactory.getLogger(PlcProxyServer.class);
+    private static final int DEFAULT_PORT = 18080;
+    private static final int DEFAULT_CONNECT_TIMEOUT_MS = 2000;
+    private static final int DEFAULT_RECEIVE_TIMEOUT_MS = 3000;
+
+    private static final Map<String, Object> PLC_LOCKS = new HashMap<>();
+    private static final Map<String, McPLC> PLC_CACHE = new HashMap<>();
+
+    public static void main(String[] args) throws Exception {
+        Properties props = ConfigUtils.loadPropertiesUnchecked();
+        PlcTarget.setProperties(props);
+        int port = parseInt(props.getProperty("plc.proxy.server.port"), DEFAULT_PORT);
+        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
+        server.createContext("/health", new HealthHandler());
+        server.createContext("/readBoolean", new ReadBooleanHandler());
+        server.createContext("/readBooleans", new ReadBooleansHandler());
+        server.createContext("/readInt16", new ReadInt16Handler());
+        server.createContext("/writeBoolean", new WriteBooleanHandler());
+        server.setExecutor(Executors.newCachedThreadPool());
+        server.start();
+        log.info("PLC proxy server started, port={}", port);
+        System.out.println("PLC proxy server started, port=" + port);
+    }
+
+    private static class HealthHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) {
+            JSONObject json = ok();
+            json.put("status", "UP");
+            writeJson(exchange, 200, json);
+        }
+    }
+
+    private static class ReadBooleanHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) {
+            handlePlc(exchange, new PlcAction() {
+                @Override
+                public Object execute(McPLC plc, Map<String, String> params) {
+                    return plc.readBoolean(required(params, "address"));
+                }
+            });
+        }
+    }
+
+    private static class ReadBooleansHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) {
+            handlePlc(exchange, new PlcAction() {
+                @Override
+                public Object execute(McPLC plc, Map<String, String> params) {
+                    int count = parseInt(params.get("count"), 1);
+                    return plc.readBoolean(required(params, "address"), count);
+                }
+            });
+        }
+    }
+
+    private static class ReadInt16Handler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) {
+            handlePlc(exchange, new PlcAction() {
+                @Override
+                public Object execute(McPLC plc, Map<String, String> params) {
+                    return plc.readInt16(required(params, "address"));
+                }
+            });
+        }
+    }
+
+    private static class WriteBooleanHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange exchange) {
+            handlePlc(exchange, new PlcAction() {
+                @Override
+                public Object execute(McPLC plc, Map<String, String> params) {
+                    boolean value = Boolean.parseBoolean(required(params, "value"));
+                    plc.writeBoolean(required(params, "address"), value);
+                    return Boolean.valueOf(value);
+                }
+            });
+        }
+    }
+
+    private static void handlePlc(HttpExchange exchange, PlcAction action) {
+        Map<String, String> params = parseQuery(exchange.getRequestURI());
+        try {
+            PlcTarget target = PlcTarget.from(params);
+            Object result;
+            synchronized (getPlcLock(target.key)) {
+                McPLC plc = getCachedPlc(target);
+                try {
+                    result = action.execute(plc, params);
+                } catch (RuntimeException e) {
+                    discardCachedPlc(target.key, plc);
+                    throw e;
+                }
+            }
+            JSONObject json = ok();
+            json.put("value", result);
+            writeJson(exchange, 200, json);
+        } catch (Exception e) {
+            log.warn("PLC proxy request failed, uri={}", exchange.getRequestURI(), e);
+            JSONObject json = new JSONObject();
+            json.put("success", false);
+            json.put("message", e.getMessage());
+            writeJson(exchange, 500, json);
+        }
+    }
+
+    private static McPLC getCachedPlc(PlcTarget target) {
+        McPLC plc = PLC_CACHE.get(target.key);
+        if (plc == null) {
+            plc = createPlc(target);
+            PLC_CACHE.put(target.key, plc);
+            log.info("PLC proxy connected, plc={}, series={}, frame={}", target.key, target.series, target.frame);
+        }
+        return plc;
+    }
+
+    private static McPLC createPlc(PlcTarget target) {
+        EMcSeries series = parseSeries(target.series);
+        EMcFrameType frameType = parseFrameType(target.frame, series.getFrameType());
+        McPLC plc = new McPLC(series, frameType, target.ip, target.port);
+        plc.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MS);
+        plc.setReceiveTimeout(DEFAULT_RECEIVE_TIMEOUT_MS);
+        return plc;
+    }
+
+    private static void discardCachedPlc(String key, McPLC plc) {
+        McPLC cached = PLC_CACHE.get(key);
+        if (cached == plc) {
+            PLC_CACHE.remove(key);
+        }
+        if (plc != null) {
+            plc.close();
+        }
+    }
+
+    private static Object getPlcLock(String key) {
+        synchronized (PLC_LOCKS) {
+            Object lock = PLC_LOCKS.get(key);
+            if (lock == null) {
+                lock = new Object();
+                PLC_LOCKS.put(key, lock);
+            }
+            return lock;
+        }
+    }
+
+    private static JSONObject ok() {
+        JSONObject json = new JSONObject();
+        json.put("success", true);
+        return json;
+    }
+
+    private static void writeJson(HttpExchange exchange, int status, JSONObject json) {
+        byte[] body = json.toJSONString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
+        try {
+            exchange.getResponseHeaders().set("Content-Type", "application/json;charset=UTF-8");
+            exchange.sendResponseHeaders(status, body.length);
+            try (OutputStream os = exchange.getResponseBody()) {
+                os.write(body);
+            }
+        } catch (Exception e) {
+            log.warn("write proxy response failed", e);
+        } finally {
+            exchange.close();
+        }
+    }
+
+    private static Map<String, String> parseQuery(URI uri) {
+        Map<String, String> params = new HashMap<>();
+        String query = uri.getRawQuery();
+        if (query == null || query.isEmpty()) {
+            return params;
+        }
+        String[] pairs = query.split("&");
+        for (String pair : pairs) {
+            int index = pair.indexOf('=');
+            String key = index >= 0 ? pair.substring(0, index) : pair;
+            String value = index >= 0 ? pair.substring(index + 1) : "";
+            params.put(decode(key), decode(value));
+        }
+        return params;
+    }
+
+    private static String required(Map<String, String> params, String key) {
+        String value = params.get(key);
+        if (value == null || value.trim().isEmpty()) {
+            throw new IllegalArgumentException("missing parameter: " + key);
+        }
+        return value;
+    }
+
+    private static String decode(String value) {
+        try {
+            return URLDecoder.decode(value, "UTF-8");
+        } catch (Exception e) {
+            return value;
+        }
+    }
+
+    private static EMcSeries parseSeries(String series) {
+        if ("Q_L".equalsIgnoreCase(series)) {
+            return EMcSeries.Q_L;
+        }
+        if ("IQ_R".equalsIgnoreCase(series)) {
+            return EMcSeries.IQ_R;
+        }
+        return EMcSeries.QnA;
+    }
+
+    private static EMcFrameType parseFrameType(String frameType, EMcFrameType defaultValue) {
+        if (frameType == null || frameType.trim().isEmpty()) {
+            return defaultValue;
+        }
+        String normalized = frameType.trim().toUpperCase(Locale.ROOT);
+        if (!normalized.startsWith("FRAME_")) {
+            normalized = "FRAME_" + normalized;
+        }
+        try {
+            return EMcFrameType.valueOf(normalized);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    private static int parseInt(String value, int defaultValue) {
+        try {
+            return Integer.parseInt(value);
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    private interface PlcAction {
+        Object execute(McPLC plc, Map<String, String> params);
+    }
+
+    private static class PlcTarget {
+        private static Properties properties = new Properties();
+        private final String key;
+        private final String ip;
+        private final int port;
+        private final String series;
+        private final String frame;
+
+        private PlcTarget(String ip, int port, String series, String frame) {
+            this.ip = ip;
+            this.port = port;
+            this.series = firstNotEmpty(series, findConfiguredValue(ip, port, "plc.series"));
+            this.frame = firstNotEmpty(frame, findConfiguredValue(ip, port, "plc.frame"));
+            this.key = ip + ":" + port;
+        }
+
+        private static void setProperties(Properties props) {
+            properties = props == null ? new Properties() : props;
+        }
+
+        private static PlcTarget from(Map<String, String> params) {
+            String plc = required(params, "plc");
+            int index = plc.lastIndexOf(':');
+            if (index <= 0 || index == plc.length() - 1) {
+                throw new IllegalArgumentException("invalid plc parameter: " + plc);
+            }
+            String ip = plc.substring(0, index);
+            int port = parseInt(plc.substring(index + 1), 0);
+            if (port <= 0) {
+                throw new IllegalArgumentException("invalid plc port: " + plc);
+            }
+            return new PlcTarget(ip, port, params.get("series"), params.get("frame"));
+        }
+
+        private static String findConfiguredValue(String ip, int port, String suffix) {
+            for (Object keyObj : properties.keySet()) {
+                String key = String.valueOf(keyObj);
+                if (!key.endsWith(".plc.ip")) {
+                    continue;
+                }
+                String prefix = key.substring(0, key.length() - ".plc.ip".length());
+                String configuredIp = properties.getProperty(prefix + ".plc.ip");
+                int configuredPort = parseInt(properties.getProperty(prefix + ".plc.port"), 0);
+                if (ip.equals(configuredIp) && port == configuredPort) {
+                    return properties.getProperty(prefix + "." + suffix);
+                }
+            }
+            return "";
+        }
+
+        private static String firstNotEmpty(String first, String second) {
+            if (first != null && !first.trim().isEmpty()) {
+                return first;
+            }
+            return second == null ? "" : second;
+        }
+    }
+}

+ 1 - 9
src/com/mes/util/StationConfig.java

@@ -1,9 +1,6 @@
 package com.mes.util;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -282,11 +279,7 @@ public class StationConfig {
 
     public static List<StationConfig> loadAllStations() throws IOException {
         List<StationConfig> stations = new ArrayList<>();
-        Properties props = new Properties();
-        String encoding = "UTF-8";
-        try (InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties")) {
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, encoding));
-            props.load(br);
+        Properties props = ConfigUtils.loadProperties();
 
             String lineSn = props.getProperty("mes.line_sn");
             String serverIp = props.getProperty("mes.server_ip");
@@ -305,7 +298,6 @@ public class StationConfig {
                     stations.add(config);
                 }
             }
-        }
         return stations;
     }
 

+ 14 - 0
src/resources/config/config-workstation1.properties

@@ -0,0 +1,14 @@
+# Workstation 1 client configuration.
+mes.line_sn=HEVXT
+mes.server_ip=192.168.9.180
+
+plc.proxy.enabled=true
+plc.proxy.base_url=http://192.168.0.66:18080
+plc.proxy.server.port=18080
+plc.proxy.connect_timeout_ms=2000
+plc.proxy.read_timeout_ms=5000
+
+mes.stations=OP050A,OP060A,OP070A,OP080A,OP090A,OP160A
+
+# Include the shared station definitions from config.properties when exporting the jar,
+# or copy the same mes.station.* entries here for a standalone external config file.

+ 14 - 0
src/resources/config/config-workstation2.properties

@@ -0,0 +1,14 @@
+# Workstation 2 client configuration.
+mes.line_sn=HEVXT
+mes.server_ip=192.168.9.180
+
+plc.proxy.enabled=true
+plc.proxy.base_url=http://192.168.0.66:18080
+plc.proxy.server.port=18080
+plc.proxy.connect_timeout_ms=2000
+plc.proxy.read_timeout_ms=5000
+
+mes.stations=OP100A,OP110A,OP120A,OP130A,OP140A,OP150A
+
+# Include the shared station definitions from config.properties when exporting the jar,
+# or copy the same mes.station.* entries here for a standalone external config file.

+ 34 - 28
src/resources/config/config.properties

@@ -1,15 +1,22 @@
-# 产线配置
+# \u4EA7\u7EBF\u914D\u7F6E
 mes.line_sn=HEVXT
 #mes.server_ip=127.0.0.1
 mes.server_ip=192.168.9.180
 
-# 工位列表(支持多个工位,逗号分隔)
+# PLC proxy. Set enabled=true on both workstation client jars after the proxy service is started.
+plc.proxy.enabled=true
+plc.proxy.base_url=http://192.168.0.66:18080
+plc.proxy.server.port=18080
+plc.proxy.connect_timeout_ms=2000
+plc.proxy.read_timeout_ms=5000
+
+# \u5DE5\u4F4D\u5217\u8868\uFF08\u652F\u6301\u591A\u4E2A\u5DE5\u4F4D\uFF0C\u9017\u53F7\u5206\u9694\uFF09
 #mes.stations=OP050A,OP060A,OP070A,OP080A,OP090A,OP160A
-# 工作站2切换列表(需要运行工作站2时,将上一行 mes.stations 注释,取消下一行注释)
+# \u5DE5\u4F5C\u7AD92\u5207\u6362\u5217\u8868\uFF08\u9700\u8981\u8FD0\u884C\u5DE5\u4F5C\u7AD92\u65F6\uFF0C\u5C06\u4E0A\u4E00\u884C mes.stations \u6CE8\u91CA\uFF0C\u53D6\u6D88\u4E0B\u4E00\u884C\u6CE8\u91CA\uFF09
 mes.stations=OP100A,OP110A,OP120A,OP130A,OP140A,OP150A
 
-# 工作站1工位配置
-mes.station.OP050A.name=左右吊耳组件弧焊
+# \u5DE5\u4F5C\u7AD91\u5DE5\u4F4D\u914D\u7F6E
+mes.station.OP050A.name=\u5DE6\u53F3\u540A\u8033\u7EC4\u4EF6\u5F27\u710A
 mes.station.OP050A.allowStart=M6661
 mes.station.OP050A.allowDown=M6662
 mes.station.OP050A.startMethod=M6663
@@ -25,7 +32,7 @@ mes.station.OP050A.robot1.voltage=D7042
 mes.station.OP050A.robot2.current=D7031
 mes.station.OP050A.robot2.voltage=D7032
 
-mes.station.OP060A.name=左右吊耳组件点焊
+mes.station.OP060A.name=\u5DE6\u53F3\u540A\u8033\u7EC4\u4EF6\u70B9\u710A
 mes.station.OP060A.allowStart=M6641
 mes.station.OP060A.allowDown=M6642
 mes.station.OP060A.startMethod=M6643
@@ -47,7 +54,7 @@ mes.station.OP060A.robot2.current=D7101
 mes.station.OP060A.robot1.time=D7092
 mes.station.OP060A.robot2.time=D7102
 
-mes.station.OP070A.name=电池壳分总成1点焊
+mes.station.OP070A.name=\u7535\u6C60\u58F3\u5206\u603B\u62101\u70B9\u710A
 mes.station.OP070A.allowStart=M6621
 mes.station.OP070A.allowDown=M6622
 mes.station.OP070A.startMethod=M6623
@@ -69,7 +76,7 @@ mes.station.OP070A.robot2.current=D7081
 mes.station.OP070A.robot1.time=D7072
 mes.station.OP070A.robot2.time=D7082
 
-mes.station.OP080A.name=电池壳分总成1点焊补焊
+mes.station.OP080A.name=\u7535\u6C60\u58F3\u5206\u603B\u62101\u70B9\u710A\u8865\u710A
 mes.station.OP080A.allowStart=M6601
 mes.station.OP080A.allowDown=M6602
 mes.station.OP080A.startMethod=M6603
@@ -91,7 +98,7 @@ mes.station.OP080A.robot2.current=D7061
 mes.station.OP080A.robot1.time=D7052
 mes.station.OP080A.robot2.time=D7062
 
-mes.station.OP090A.name=电池壳分总成2点焊
+mes.station.OP090A.name=\u7535\u6C60\u58F3\u5206\u603B\u62102\u70B9\u710A
 mes.station.OP090A.allowStart=M6001
 mes.station.OP090A.allowDown=M6002
 mes.station.OP090A.startMethod=M6003
@@ -113,7 +120,7 @@ mes.station.OP090A.robot2.current=D6012
 mes.station.OP090A.robot1.time=D6003
 mes.station.OP090A.robot2.time=D6013
 
-mes.station.OP160A.name=电池壳分总成8弧焊
+mes.station.OP160A.name=\u7535\u6C60\u58F3\u5206\u603B\u62108\u5F27\u710A
 mes.station.OP160A.allowStart=M6681
 mes.station.OP160A.allowDown=M6682
 mes.station.OP160A.startMethod=M6683
@@ -129,17 +136,16 @@ mes.station.OP160A.robot1.voltage=D7022
 mes.station.OP160A.robot2.current=D7011
 mes.station.OP160A.robot2.voltage=D7012
 
-# 工作站2工位配置
-# 对应关系:
-#   OP100A -> PLC OP60  (FX5U_CPU 192.168.0.10,1号站2号工位)
-#   OP110A -> PLC OP70  (Q26UDV_CPU 192.168.0.35,1号站2号工位)
-#   OP120A -> PLC OP80  (Q26UDV_CPU 192.168.0.35,2号站2号工位)
-#   OP130A -> PLC OP90  (Q26UDV_CPU 192.168.0.35,3号站2号工位)
-#   OP140A -> PLC OP100 (Q26UDV_CPU 192.168.0.35,4号站2号工位)
-#   OP150A -> PLC OP110 (Q26UDV_CPU 192.168.0.35,5号站2号工位)
-# 同一个站的 1号/2号工位除允许启动、允许下料、开始加工、加工结束、故障外,其余焊点和参数点共用。
-
-mes.station.OP100A.name=FX5U 1号站2号工位焊点信号
+ #\u5DE5\u4F5C\u7AD92\u5DE5\u4F4D\u914D\u7F6E
+ #\u5BF9\u5E94\u5173\u7CFB\uFF1A
+ #  OP100A -> PLC OP60  \uFF08FX5U_CPU 192.168.0.10\uFF0C1\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #  OP110A -> PLC OP70  \uFF08Q26UDV_CPU 192.168.0.35\uFF0C1\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #  OP120A -> PLC OP80  \uFF08Q26UDV_CPU 192.168.0.35\uFF0C2\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #  OP130A -> PLC OP90  \uFF08Q26UDV_CPU 192.168.0.35\uFF0C3\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #  OP140A -> PLC OP100 \uFF08Q26UDV_CPU 192.168.0.35\uFF0C4\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #  OP150A -> PLC OP110 \uFF08Q26UDV_CPU 192.168.0.35\uFF0C5\u53F7\u7AD92\u53F7\u5DE5\u4F4D\uFF09
+ #\u540C\u4E00\u4E2A\u7AD9\u7684 1\u53F7/2\u53F7\u5DE5\u4F4D\u9664\u5141\u8BB8\u542F\u52A8\u3001\u5141\u8BB8\u4E0B\u6599\u3001\u5F00\u59CB\u52A0\u5DE5\u3001\u52A0\u5DE5\u7ED3\u675F\u3001\u6545\u969C\u5916\uFF0C\u5176\u4F59\u710A\u70B9\u548C\u53C2\u6570\u70B9\u5171\u7528\u3002
+mes.station.OP100A.name=FX5U 1\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u710A\u70B9\u4FE1\u53F7
 mes.station.OP100A.allowStart=M6011
 mes.station.OP100A.allowDown=M6012
 mes.station.OP100A.startMethod=M6013
@@ -161,7 +167,7 @@ mes.station.OP100A.robot2.current=D6012
 mes.station.OP100A.robot1.time=D6003
 mes.station.OP100A.robot2.time=D6013
 
-mes.station.OP110A.name=1号站2号工位焊点信号
+mes.station.OP110A.name=1\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u710A\u70B9\u4FE1\u53F7
 mes.station.OP110A.allowStart=M6611
 mes.station.OP110A.allowDown=M6612
 mes.station.OP110A.startMethod=M6613
@@ -183,7 +189,7 @@ mes.station.OP110A.robot2.current=D7061
 mes.station.OP110A.robot1.time=D7052
 mes.station.OP110A.robot2.time=D7062
 
-mes.station.OP120A.name=2号站2号工位焊点信号
+mes.station.OP120A.name=2\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u710A\u70B9\u4FE1\u53F7
 mes.station.OP120A.allowStart=M6631
 mes.station.OP120A.allowDown=M6632
 mes.station.OP120A.startMethod=M6633
@@ -205,7 +211,7 @@ mes.station.OP120A.robot2.current=D7081
 mes.station.OP120A.robot1.time=D7072
 mes.station.OP120A.robot2.time=D7082
 
-mes.station.OP130A.name=3号站2号工位焊点信号
+mes.station.OP130A.name=3\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u710A\u70B9\u4FE1\u53F7
 mes.station.OP130A.allowStart=M6651
 mes.station.OP130A.allowDown=M6652
 mes.station.OP130A.startMethod=M6653
@@ -227,7 +233,7 @@ mes.station.OP130A.robot2.current=D7101
 mes.station.OP130A.robot1.time=D7092
 mes.station.OP130A.robot2.time=D7102
 
-mes.station.OP140A.name=4号站2号工位基础信号
+mes.station.OP140A.name=4\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u57FA\u7840\u4FE1\u53F7
 mes.station.OP140A.allowStart=M6671
 mes.station.OP140A.allowDown=M6672
 mes.station.OP140A.startMethod=M6673
@@ -243,7 +249,7 @@ mes.station.OP140A.robot1.voltage=D7042
 mes.station.OP140A.robot2.current=D7031
 mes.station.OP140A.robot2.voltage=D7032
 
-mes.station.OP150A.name=5号站2号工位基础信号
+mes.station.OP150A.name=5\u53F7\u7AD92\u53F7\u5DE5\u4F4D\u57FA\u7840\u4FE1\u53F7
 mes.station.OP150A.allowStart=M6691
 mes.station.OP150A.allowDown=M6692
 mes.station.OP150A.startMethod=M6693
@@ -259,6 +265,6 @@ mes.station.OP150A.robot1.voltage=D7022
 mes.station.OP150A.robot2.current=D7011
 mes.station.OP150A.robot2.voltage=D7012
 
-# 单工位配置(保留原配置以兼容旧代码)
+# \u5355\u5DE5\u4F4D\u914D\u7F6E\uFF08\u4FDD\u7559\u539F\u914D\u7F6E\u4EE5\u517C\u5BB9\u65E7\u4EE3\u7801\uFF09
 mes.gw=OP050A
-mes.gw_des=工位1-上料
+mes.gw_des=\u5DE5\u4F4D1-\u4E0A\u6599