hfy 1 week ago
parent
commit
9cb3a0a0eb

+ 48 - 9
src/com/mes/ui/WorkStationPanel.java

@@ -43,6 +43,7 @@ public class WorkStationPanel extends JPanel {
     public boolean checkQualityResult = false;
     private String currentUser = "system";
     private String lastWeldingParamContent = "";
+    private PLCUtils.WeldingParamSampler weldingParamSampler;
 
     public static List<String> hjparams = new ArrayList<>();
     
@@ -65,6 +66,7 @@ public class WorkStationPanel extends JPanel {
     private long lastPlcStatusWarnTime = 0L;
     private int plcWorkflowFailCount = 0;
     private long lastPlcWorkflowWarnTime = 0L;
+    private long nextPlcWorkflowTime = 0L;
 
     private boolean iconREDFlag = true;
 
@@ -443,6 +445,7 @@ public class WorkStationPanel extends JPanel {
         finishOkButton.setEnabled(false);
         fxLabel.setVisible(false);
         lastWeldingParamContent = "";
+        stopWeldingParamSampler();
     }
 
     public void resetScanA() {
@@ -455,7 +458,8 @@ public class WorkStationPanel extends JPanel {
             productSnField.setText("");
             fxLabel.setVisible(false);
             lastWeldingParamContent = "";
-
+            stopWeldingParamSampler();
+            PLCUtils.writeStartfalseMethod(stationConfig);
             scanButton.setEnabled(true);
             statusLabel.setText("请扫工件码");
 //        MesClient.setMenuStatus("开班点检,请先进行OKNG样件测试",-1);
@@ -573,9 +577,14 @@ public class WorkStationPanel extends JPanel {
     }
 
     private void runWorkFlowSafely() {
+        long now = System.currentTimeMillis();
+        if (now < nextPlcWorkflowTime) {
+            return;
+        }
         try {
             runWorkFlow();
             plcWorkflowFailCount = 0;
+            nextPlcWorkflowTime = 0L;
         } catch (Exception e) {
             handleWorkflowException(e);
         }
@@ -604,6 +613,7 @@ public class WorkStationPanel extends JPanel {
                 if (ret){
                     boolean ret2 = PLCUtils.writeStopMethod(stationConfig);
                     if (ret2) {
+                        startWeldingParamSampler();
                         statusLabel.setForeground(Color.GREEN);
                         statusLabel.setText("加工中,等待加工结束");
                         plcStatus = 3;
@@ -616,7 +626,7 @@ public class WorkStationPanel extends JPanel {
                     // 发送结果
                     String sn = productSnField.getText().trim();
                     if (lastWeldingParamContent == null || lastWeldingParamContent.trim().isEmpty()) {
-                        lastWeldingParamContent = PLCUtils.getWeldingParamContent(stationConfig);
+                        lastWeldingParamContent = stopWeldingParamSamplerAndGetContent();
                         log.info("PLC中读取"+lastWeldingParamContent);
                     }
                     JSONObject retObj = DataUtil.sendQuality( sn,"OK", currentUser, gw, lineSn, serverIp);
@@ -640,13 +650,6 @@ public class WorkStationPanel extends JPanel {
                         String msg = (retObj != null && retObj.containsKey("message")) ? retObj.getString("message") : "提交失败";
                         statusLabel.setText(msg);
                     }
-                }else {
-                    // 获取参数
-                    lastWeldingParamContent = PLCUtils.getWeldingParamContent(stationConfig);
-                    hjparams = new ArrayList<>();
-                    if (lastWeldingParamContent != null && !lastWeldingParamContent.trim().isEmpty()) {
-                        hjparams.add(lastWeldingParamContent);
-                    }
                 }
             }else if (plcStatus == 4){
                 // 等待加工结束信号为 false,收到后将允许下料置为 false。
@@ -676,6 +679,8 @@ public class WorkStationPanel extends JPanel {
     private void handleWorkflowException(Exception e) {
         plcWorkflowFailCount++;
         long now = System.currentTimeMillis();
+        long retryDelay = Math.min(30000L, 3000L * plcWorkflowFailCount);
+        nextPlcWorkflowTime = now + retryDelay;
         if (now - lastPlcWorkflowWarnTime >= PLC_WORKFLOW_WARN_INTERVAL_MS) {
             lastPlcWorkflowWarnTime = now;
             log.warn("PLC自动流程执行失败,工位={},流程状态={},连续失败{}次,下次继续等待", gw, plcStatus, plcWorkflowFailCount, e);
@@ -683,6 +688,40 @@ public class WorkStationPanel extends JPanel {
     }
 
 
+    private void startWeldingParamSampler() {
+        if (stationConfig == null || weldingParamSampler != null) {
+            return;
+        }
+        lastWeldingParamContent = "";
+        weldingParamSampler = PLCUtils.startWeldingParamSampler(stationConfig);
+        log.info("start welding param sampler, gw={}", gw);
+    }
+
+    private String stopWeldingParamSamplerAndGetContent() {
+        PLCUtils.WeldingParamSampler sampler = weldingParamSampler;
+        weldingParamSampler = null;
+        String content;
+        if (sampler == null) {
+            content = PLCUtils.getWeldingParamContent(stationConfig);
+        } else {
+            content = sampler.stopAndGetContent();
+        }
+        hjparams = new ArrayList<>();
+        if (content != null && !content.trim().isEmpty()) {
+            hjparams.add(content);
+        }
+        log.info("stop welding param sampler, gw={}, contentLength={}", gw, content == null ? 0 : content.length());
+        return content;
+    }
+
+    private void stopWeldingParamSampler() {
+        PLCUtils.WeldingParamSampler sampler = weldingParamSampler;
+        weldingParamSampler = null;
+        if (sampler != null) {
+            sampler.stopAndGetContent();
+        }
+    }
+
     public void setCurrentUser(String user) {
         this.currentUser = user;
     }

+ 349 - 50
src/com/mes/util/PLCUtils.java

@@ -2,6 +2,7 @@ package com.mes.util;
 
 import com.alibaba.fastjson2.JSONArray;
 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 org.slf4j.Logger;
@@ -11,14 +12,21 @@ import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 public class PLCUtils {
 
     private static final Logger log = LoggerFactory.getLogger(PLCUtils.class);
     private static final int CONNECT_TIMEOUT_MS = 2000;
     private static final int RECEIVE_TIMEOUT_MS = 3000;
+    private static final long PLC_FAILURE_WARN_INTERVAL_MS = 30000L;
+    private static final long WELDING_SAMPLE_INTERVAL_MS = 500L;
+    private static final Map<String, Object> PLC_LOCKS = new HashMap<>();
+    private static final Map<String, McPLC> PLC_CACHE = new HashMap<>();
+    private static final Map<String, Long> PLC_FAILURE_LOG_TIMES = new HashMap<>();
 
     private static McPLC defaultPlc = new McPLC(EMcSeries.QnA, "192.168.0.35", 8000);
 
@@ -108,16 +116,19 @@ public class PLCUtils {
     }
 
     public static StationStatus readStationStatus(StationConfig config) {
-        McPLC plc = createPlc(config);
-        try {
-            return new StationStatus(
-                    readBoolean(plc, config, config.getAllowStart()),
-                    readBoolean(plc, config, config.getAllowDown()),
-                    readBoolean(plc, config, config.getStartMethod()),
-                    readBoolean(plc, config, config.getStopMethod()),
-                    readBoolean(plc, config, config.getFault()));
-        } finally {
-            plc.close();
+        synchronized (getPlcLock(config)) {
+            McPLC plc = getCachedPlc(config);
+            try {
+                return new StationStatus(
+                        readBoolean(plc, config, config.getAllowStart()),
+                        readBoolean(plc, config, config.getAllowDown()),
+                        readBoolean(plc, config, config.getStartMethod()),
+                        readBoolean(plc, config, config.getStopMethod()),
+                        readBoolean(plc, config, config.getFault()));
+            } catch (RuntimeException e) {
+                discardCachedPlc(config, plc);
+                throw e;
+            }
         }
     }
 
@@ -193,21 +204,160 @@ public class PLCUtils {
             return "";
         }
 
-        McPLC plc = createPlc(config);
-        try {
+        synchronized (getPlcLock(config)) {
+            McPLC plc = getCachedPlc(config);
+            try {
+            synchronized (getPlcLock(config)) {
             if ("arc".equalsIgnoreCase(config.getWeldType())) {
                 return buildArcWeldContent(plc, config).toJSONString();
             }
             if ("spot".equalsIgnoreCase(config.getWeldType())) {
                 return buildSpotWeldContent(plc, config).toJSONString();
             }
+            }
             log.info("未配置该工位的PLC参数点位,工位={}", config.getGw());
             return "";
         } catch (Exception e) {
             log.error("读取PLC参数失败,工位=" + config.getGw(), e);
             return "";
-        } finally {
-            plc.close();
+        }
+    }
+
+    }
+
+    public static WeldingParamSampler startWeldingParamSampler(StationConfig config) {
+        WeldingParamSampler sampler = new WeldingParamSampler(config, WELDING_SAMPLE_INTERVAL_MS);
+        sampler.start();
+        return sampler;
+    }
+    public static boolean writeStartfalseMethod(StationConfig config) {
+        writeBoolean(config, config.getAllowStart(), false);
+        return true;
+    }
+    public static class WeldingParamSampler {
+        private final StationConfig config;
+        private final long sampleIntervalMs;
+        private final List<JSONObject> samples = new ArrayList<>();
+        private volatile boolean running = false;
+        private Thread thread;
+        private long startTimeMillis;
+        private long stopTimeMillis;
+
+        private WeldingParamSampler(StationConfig config, long sampleIntervalMs) {
+            this.config = config;
+            this.sampleIntervalMs = sampleIntervalMs;
+        }
+
+        public synchronized void start() {
+            if (running || config == null || isEmpty(config.getWeldType())) {
+                return;
+            }
+            running = true;
+            startTimeMillis = System.currentTimeMillis();
+            thread = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    sampleLoop();
+                }
+            }, "welding-param-sampler-" + config.getGw());
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+        public String stopAndGetContent() {
+            running = false;
+            stopTimeMillis = System.currentTimeMillis();
+            Thread localThread = thread;
+            if (localThread != null) {
+                localThread.interrupt();
+                try {
+                    localThread.join(3000L);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+            ensureAtLeastOneSample();
+            return buildSampledContent();
+        }
+
+        private void sampleLoop() {
+            while (running) {
+                try {
+                    synchronized (getPlcLock(config)) {
+                        McPLC plc = getCachedPlc(config);
+                        try {
+                            addSample(readWeldingSample(plc, config));
+                        } catch (RuntimeException e) {
+                            discardCachedPlc(config, plc);
+                            throw e;
+                        }
+                    }
+                } catch (Exception e) {
+                    log.warn("read welding sample failed, gw={}", config.getGw(), e);
+                }
+                sleepQuietly(sampleIntervalMs);
+            }
+        }
+
+        private void addSample(JSONObject sample) {
+            if (sample == null || sample.isEmpty()) {
+                return;
+            }
+            synchronized (samples) {
+                samples.add(sample);
+            }
+        }
+
+        private void ensureAtLeastOneSample() {
+            synchronized (samples) {
+                if (!samples.isEmpty()) {
+                    return;
+                }
+            }
+            try {
+                synchronized (getPlcLock(config)) {
+                    McPLC plc = getCachedPlc(config);
+                    try {
+                        addSample(readWeldingSample(plc, config));
+                    } catch (RuntimeException e) {
+                        discardCachedPlc(config, plc);
+                        throw e;
+                    }
+                }
+            } catch (Exception e) {
+                log.warn("read final welding sample failed, gw={}", config == null ? "" : config.getGw(), e);
+            }
+        }
+
+        private String buildSampledContent() {
+            if (config == null || isEmpty(config.getWeldType())) {
+                return "";
+            }
+
+            JSONArray sampleArray = new JSONArray();
+            synchronized (samples) {
+                sampleArray.addAll(samples);
+            }
+            if (sampleArray.isEmpty()) {
+                return "";
+            }
+
+            JSONObject content = baseContent(config, config.getWeldType());
+            content.put("startTime", formatTime(startTimeMillis));
+            content.put("stopTime", formatTime(stopTimeMillis == 0L ? System.currentTimeMillis() : stopTimeMillis));
+            content.put("sampleIntervalMs", sampleIntervalMs);
+            content.put("sampleCount", sampleArray.size());
+            content.put("samples", sampleArray);
+            addSpotSummary(content, sampleArray);
+            return content.toJSONString();
+        }
+
+        private void sleepQuietly(long millis) {
+            try {
+                Thread.sleep(millis);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
         }
     }
 
@@ -218,22 +368,29 @@ public class PLCUtils {
             return;
         }
 
-        McPLC plc = createPlc(config);
-        try {
+        synchronized (getPlcLock(config)) {
+            McPLC plc = getCachedPlc(config);
+            try {
             // 现场验证:192.168.0.10 的SLMP端口支持单点写,不适合连续位写。
-            plc.writeBoolean(address, value);
-        } finally {
-            plc.close();
+                plc.writeBoolean(address, value);
+            } catch (RuntimeException e) {
+                discardCachedPlc(config, plc);
+                logPlcFailure("writeBoolean", config, address, plc, e);
+                throw e;
+            }
         }
     }
 
     // 读取单个 M 点状态,例如开始加工、加工结束。
     private static boolean readBoolean(StationConfig config, String address) {
-        McPLC plc = createPlc(config);
-        try {
-            return readBoolean(plc, config, address);
-        } finally {
-            plc.close();
+        synchronized (getPlcLock(config)) {
+            McPLC plc = getCachedPlc(config);
+            try {
+                return readBoolean(plc, config, address);
+            } catch (RuntimeException e) {
+                discardCachedPlc(config, plc);
+                throw e;
+            }
         }
     }
 
@@ -243,61 +400,115 @@ public class PLCUtils {
             return false;
         }
 
-        return plc.readBoolean(address);
+        try {
+            return plc.readBoolean(address);
+        } catch (RuntimeException e) {
+            logPlcFailure("readBoolean", config, address, plc, e);
+            throw e;
+        }
     }
 
     private static JSONObject buildArcWeldContent(McPLC plc, StationConfig config) {
         JSONObject content = baseContent(config, "arc");
+
+        JSONArray samples = new JSONArray();
+        samples.add(buildArcWeldSample(plc, config));
+        content.put("samples", samples);
+        return content;
+    }
+    private static JSONObject buildSpotWeldContent(McPLC plc, StationConfig config) {
+        JSONObject content = baseContent(config, "spot");
+        JSONObject sample = buildSpotWeldSample(plc, config);
+
+        JSONArray samples = new JSONArray();
+        samples.add(sample);
+        content.put("samples", samples);
+        addSpotSummary(content, samples);
+        return content;
+    }
+
+    private static JSONObject readWeldingSample(McPLC plc, StationConfig config) {
+        if ("arc".equalsIgnoreCase(config.getWeldType())) {
+            return buildArcWeldSample(plc, config);
+        }
+        if ("spot".equalsIgnoreCase(config.getWeldType())) {
+            return buildSpotWeldSample(plc, config);
+        }
+        return new JSONObject();
+    }
+
+    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);
-
-        JSONArray samples = new JSONArray();
-        samples.add(sample);
-        content.put("samples", samples);
-        return content;
+        addScaledNumber(plc, sample, "robot2WeldTime", config.getRobot2Time(), 20, true);
+        return sample;
     }
-    private static JSONObject buildSpotWeldContent(McPLC plc, StationConfig config) {
-        List<Boolean> robot1Start = plc.readBoolean(config.getRobot1Start(), 50);
-        List<Boolean> robot1Done = plc.readBoolean(config.getRobot1Done(), 50);
-        List<Boolean> robot2Start = plc.readBoolean(config.getRobot2Start(), 50);
-        List<Boolean> robot2Done = plc.readBoolean(config.getRobot2Done(), 50);
 
-        JSONObject content = baseContent(config, "spot");
+    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, "robot2Current", config.getRobot2Current(), 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);
 
         JSONObject spot = new JSONObject();
         JSONObject robot1 = new JSONObject();
-        robot1.put("startPoints", toPointNumbers(robot1Start));
-        robot1.put("finishPoints", toPointNumbers(robot1Done));
+        robot1.put("startPoints", readPointNumbers(plc, config.getRobot1Start(), 50));
+        robot1.put("finishPoints", readPointNumbers(plc, config.getRobot1Done(), 50));
         JSONObject robot2 = new JSONObject();
-        robot2.put("startPoints", toPointNumbers(robot2Start));
-        robot2.put("finishPoints", toPointNumbers(robot2Done));
+        robot2.put("startPoints", readPointNumbers(plc, config.getRobot2Start(), 50));
+        robot2.put("finishPoints", readPointNumbers(plc, config.getRobot2Done(), 50));
         spot.put("robot1", robot1);
         spot.put("robot2", robot2);
         sample.put("spot", spot);
+        return sample;
+    }
 
-        JSONArray samples = new JSONArray();
-        samples.add(sample);
-        content.put("samples", samples);
+    private static JSONArray readPointNumbers(McPLC plc, String address, int count) {
+        if (isEmpty(address)) {
+            return new JSONArray();
+        }
+        return toPointNumbers(plc.readBoolean(address, count));
+    }
 
+    private static void addSpotSummary(JSONObject content, JSONArray samples) {
+        if (samples == null || samples.isEmpty()) {
+            return;
+        }
+        JSONObject lastSample = samples.getJSONObject(samples.size() - 1);
+        if (lastSample == null) {
+            return;
+        }
+        JSONObject spot = lastSample.getJSONObject("spot");
+        if (spot == null) {
+            return;
+        }
+        JSONObject robot1 = spot.getJSONObject("robot1");
+        JSONObject robot2 = spot.getJSONObject("robot2");
         JSONObject summary = new JSONObject();
-        summary.put("robot1StartCount", countTrue(robot1Start));
-        summary.put("robot1FinishCount", countTrue(robot1Done));
-        summary.put("robot2StartCount", countTrue(robot2Start));
-        summary.put("robot2FinishCount", countTrue(robot2Done));
+        summary.put("robot1StartCount", pointCount(robot1, "startPoints"));
+        summary.put("robot1FinishCount", pointCount(robot1, "finishPoints"));
+        summary.put("robot2StartCount", pointCount(robot2, "startPoints"));
+        summary.put("robot2FinishCount", pointCount(robot2, "finishPoints"));
         content.put("summary", summary);
-        return content;
+    }
+
+    private static int pointCount(JSONObject robot, String key) {
+        if (robot == null) {
+            return 0;
+        }
+        JSONArray points = robot.getJSONArray(key);
+        return points == null ? 0 : points.size();
     }
 
     private static JSONObject baseContent(StationConfig config, String weldingType) {
@@ -363,6 +574,13 @@ public class PLCUtils {
         return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
     }
 
+    private static String formatTime(long timeMillis) {
+        if (timeMillis <= 0L) {
+            return "";
+        }
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timeMillis));
+    }
+
     // 按 StationConfig 创建 PLC 连接;不同工位可配置不同 PLC、端口和系列。
     private static McPLC createPlc(StationConfig config) {
         if (config == null || isEmpty(config.getPlcIp())) {
@@ -372,7 +590,9 @@ public class PLCUtils {
             return plc;
         }
 
-        McPLC plc = new McPLC(parseSeries(config.getPlcSeries()), config.getPlcIp(), config.getPlcPort());
+        EMcSeries series = parseSeries(config.getPlcSeries());
+        EMcFrameType frameType = parseFrameType(config.getPlcFrame(), series.getFrameType());
+        McPLC plc = new McPLC(series, frameType, config.getPlcIp(), config.getPlcPort());
         plc.setConnectTimeout(CONNECT_TIMEOUT_MS);
         plc.setReceiveTimeout(RECEIVE_TIMEOUT_MS);
         return plc;
@@ -388,6 +608,85 @@ public class PLCUtils {
         return EMcSeries.QnA;
     }
 
+    private static EMcFrameType parseFrameType(String frameType, EMcFrameType defaultValue) {
+        if (isEmpty(frameType)) {
+            return defaultValue;
+        }
+        String normalized = frameType.trim().toUpperCase(Locale.ROOT);
+        if (!normalized.startsWith("FRAME_")) {
+            normalized = "FRAME_" + normalized;
+        }
+        try {
+            return EMcFrameType.valueOf(normalized);
+        } catch (Exception e) {
+            log.warn("invalid PLC frame type {}, use default {}", frameType, defaultValue);
+            return defaultValue;
+        }
+    }
+
+    private static void logPlcFailure(String action, StationConfig config, String address, McPLC plc, Exception e) {
+        String logKey = action + "|" + plcKey(config) + "|" + address;
+        long now = System.currentTimeMillis();
+        synchronized (PLC_FAILURE_LOG_TIMES) {
+            Long lastLogTime = PLC_FAILURE_LOG_TIMES.get(logKey);
+            if (lastLogTime != null && now - lastLogTime < PLC_FAILURE_WARN_INTERVAL_MS) {
+                return;
+            }
+            PLC_FAILURE_LOG_TIMES.put(logKey, now);
+        }
+        log.warn("{} failed, gw={}, address={}, ip={}, port={}, series={}, frame={}, connectTimeout={}ms, receiveTimeout={}ms",
+                action,
+                config == null ? "" : config.getGw(),
+                address,
+                config == null ? "" : config.getPlcIp(),
+                config == null ? "" : config.getPlcPort(),
+                plc == null ? "" : plc.getSeries(),
+                plc == null ? "" : plc.getFrameType(),
+                CONNECT_TIMEOUT_MS,
+                RECEIVE_TIMEOUT_MS,
+                e);
+    }
+
+    private static Object getPlcLock(StationConfig config) {
+        String key = plcKey(config);
+        synchronized (PLC_LOCKS) {
+            Object lock = PLC_LOCKS.get(key);
+            if (lock == null) {
+                lock = new Object();
+                PLC_LOCKS.put(key, lock);
+            }
+            return lock;
+        }
+    }
+
+    private static McPLC getCachedPlc(StationConfig config) {
+        String key = plcKey(config);
+        McPLC plc = PLC_CACHE.get(key);
+        if (plc == null) {
+            plc = createPlc(config);
+            PLC_CACHE.put(key, plc);
+        }
+        return plc;
+    }
+
+    private static void discardCachedPlc(StationConfig config, McPLC plc) {
+        String key = plcKey(config);
+        McPLC cached = PLC_CACHE.get(key);
+        if (cached == plc) {
+            PLC_CACHE.remove(key);
+        }
+        if (plc != null) {
+            plc.close();
+        }
+    }
+
+    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 int countTrue(List<Boolean> values) {
         int count = 0;
         for (Boolean value : values) {

+ 10 - 0
src/com/mes/util/StationConfig.java

@@ -28,6 +28,7 @@ public class StationConfig {
     private String plcIp;
     private int plcPort;
     private String plcSeries;
+    private String plcFrame;
     private String plcStation;
     private String weldType;
 
@@ -159,6 +160,14 @@ public class StationConfig {
         this.plcSeries = plcSeries;
     }
 
+    public String getPlcFrame() {
+        return plcFrame;
+    }
+
+    public void setPlcFrame(String plcFrame) {
+        this.plcFrame = plcFrame;
+    }
+
     public String getPlcStation() {
         return plcStation;
     }
@@ -306,6 +315,7 @@ public class StationConfig {
         config.setPlcIp(props.getProperty(prefix + "plc.ip", "192.168.0.35"));
         config.setPlcPort(parseInt(props.getProperty(prefix + "plc.port"), 8000));
         config.setPlcSeries(props.getProperty(prefix + "plc.series", "QnA"));
+        config.setPlcFrame(props.getProperty(prefix + "plc.frame", ""));
         config.setPlcStation(props.getProperty(prefix + "plc.station", ""));
         config.setWeldType(props.getProperty(prefix + "weldType", ""));
         config.setRobot1Start(props.getProperty(prefix + "robot1.start", ""));

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

@@ -98,6 +98,7 @@ mes.station.OP090A.fault=M6005
 mes.station.OP090A.plc.ip=192.168.0.10
 mes.station.OP090A.plc.port=5001
 mes.station.OP090A.plc.series=Q_L
+mes.station.OP090A.plc.frame=FRAME_3E
 mes.station.OP090A.plc.station=OP50
 mes.station.OP090A.weldType=spot
 mes.station.OP090A.robot1.start=M6100