wangxichen 2 недель назад
Родитель
Сommit
25a03e1762

BIN
lib/RemoteComm_x64.dll


BIN
lib/RemoteComm_x86.dll


BIN
lib/jna-5.13.0.jar


+ 341 - 0
src/com/mes/controller/LaserController.java

@@ -0,0 +1,341 @@
+package com.mes.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.mes.device.LaserDevice;
+import com.mes.ui.MesClient;
+import com.mes.util.DateLocalUtils;
+import com.mes.util.JdbcUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 激光切割设备控制器
+ * 参考OP60焊接PlcUtil的模式
+ */
+public class LaserController {
+
+    private static final Logger log = LoggerFactory.getLogger(LaserController.class);
+
+    private LaserDevice device;
+
+    // 状态机 0=未开始 1=已扫码等待启动 2=设备运行中 3=设备运行结束
+    public static Integer tjFlag = 0;
+
+    // 当前工件号
+    public static String curSn = "";
+
+    // 参数采集缓存(60条批量存储)
+    public static List<String> hjparams = new ArrayList<>();
+
+    // 上传失败标志
+    public static Integer tjStatus = 0; // 1=提交失败
+
+    // 开始时间
+    private long startTime = 0;
+
+    // 宏变量地址配置
+    private static final int MACRO_MES_ENABLE = 3100;  // MES允许启动
+    private static final int MACRO_SPEED = 33563;      // 进给速度
+    private static final int MACRO_TIME = 33868;       // 加工时间
+    private static final int MACRO_COUNT = 33870;      // 零件计数
+
+    // PLC变量地址配置
+    private static final String PLC_RUNNING = "42.0";   // 运行中 MO42.0
+    private static final String PLC_STOPPED = "42.2";   // 停止 MO42.2
+    private static final String PLC_READY = "42.3";     // 就绪 MO42.3
+    private static final int PLC_TYPE_MO = 1;           // MO类型
+
+    public LaserController(LaserDevice device) {
+        this.device = device;
+    }
+
+    /**
+     * 扫码处理(参考OP60)
+     */
+    public boolean onScanCode(String sn, String user) {
+        try {
+            // 检查状态
+            if(tjFlag != 0) {
+                log.warn("上一个工件未完成,不能扫码");
+                MesClient.showError("上一个工件未完成");
+                return false;
+            }
+
+            // 检查设备连接
+            if(!device.isConnected()) {
+                log.error("设备未连接");
+                MesClient.showError("设备未连接");
+                return false;
+            }
+
+            log.info("扫码: {}", sn);
+
+            // 1. MES质检(这里需要调用MES接口)
+            // TODO: 调用checkQuality接口
+
+            // 2. 开始加工(通知MES)
+            // TODO: 调用startWork接口
+
+            // 3. 写MES允许启动信号
+            boolean writeResult = device.writeMacro(MACRO_MES_ENABLE, 1.0);
+            if(!writeResult) {
+                log.error("写入MES允许信号失败");
+                MesClient.showError("写入MES允许信号失败");
+                return false;
+            }
+
+            // 4. 切换状态
+            tjFlag = 1;
+            curSn = sn;
+            startTime = System.currentTimeMillis();
+            log.info("扫码成功,等待操作员启动设备");
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("扫码处理异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 状态检测(1秒调用1次,参考OP60的getPlcParam)
+     */
+    public void checkStatus() {
+        try {
+            if(!device.isConnected()) {
+                return;
+            }
+
+            // 根据状态执行不同逻辑
+            if(tjFlag == 1) {
+                checkStart();  // 检测启动
+            } else if(tjFlag == 2) {
+                checkFinish(); // 检测完成
+            }
+
+        } catch (Exception e) {
+            log.error("状态检测异常", e);
+        }
+    }
+
+    /**
+     * 检测设备是否启动(参考OP60的getStatusA)
+     */
+    private void checkStart() {
+        try {
+            // 读取运行状态 MO42.0
+            long running = device.readPlc(PLC_RUNNING, PLC_TYPE_MO);
+
+            if(running == 1) {
+                // 检测到设备启动
+                tjFlag = 2;
+                log.info("检测到设备启动,开始采集参数");
+                MesClient.updateStatus("设备运行中");
+
+                // 记录实际开始时间
+                startTime = System.currentTimeMillis();
+            }
+
+        } catch (Exception e) {
+            log.error("检测启动异常", e);
+        }
+    }
+
+    /**
+     * 检测设备是否完成(参考OP60的getStatusA)
+     */
+    private void checkFinish() {
+        try {
+            // 读取停止和就绪状态
+            long stopped = device.readPlc(PLC_STOPPED, PLC_TYPE_MO);
+            long ready = device.readPlc(PLC_READY, PLC_TYPE_MO);
+
+            if(stopped == 1 && ready == 1) {
+                // 设备完成
+                tjFlag = 3;
+                log.info("检测到设备完成");
+                MesClient.updateStatus("设备运行结束,提交结果中");
+
+                // 保存剩余参数
+                if(hjparams.size() > 0) {
+                    saveParams();
+                }
+
+                // 上传结果到MES
+                boolean sendret = sendQuality(curSn, "OK");
+
+                if(!sendret) {
+                    // 上传失败
+                    tjStatus = 1;
+                    log.error("结果上传MES失败");
+                    MesClient.updateStatus("结果上传MES失败");
+                } else {
+                    // 上传成功,复位
+                    log.info("结果上传成功,复位状态");
+                    resetStatus();
+                    MesClient.updateStatus("提交成功");
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("检测完成异常", e);
+        }
+    }
+
+    /**
+     * 参数采集(1秒调用1次,参考OP60的getPlcParams)
+     */
+    public void collectParams() {
+        try {
+            if(!device.isConnected()) {
+                return;
+            }
+
+            // 只有运行中才采集
+            if(tjFlag != 2) {
+                return;
+            }
+
+            // 读取系统宏变量
+            double speed = device.readMacro(MACRO_SPEED);   // 进给速度
+            double time = device.readMacro(MACRO_TIME);     // 加工时间
+            double count = device.readMacro(MACRO_COUNT);   // 零件计数
+
+            // 拼接参数字符串(参考OP60的格式)
+            String timestamp = DateLocalUtils.getCurrentTime();
+            String record = speed + "|" + time + "|" + count + "|" + timestamp;
+            hjparams.add(record);
+
+            log.debug("采集参数: {}", record);
+
+            // 满60条存储(参考OP60)
+            if(hjparams.size() == 60) {
+                saveParams();
+            }
+
+        } catch (Exception e) {
+            log.error("参数采集异常", e);
+        }
+    }
+
+    /**
+     * 保存参数到SQLite(参考OP60)
+     */
+    private void saveParams() {
+        try {
+            if(hjparams.size() == 0) {
+                return;
+            }
+
+            // 检查工件码是否为空
+            if(curSn == null || curSn.trim().isEmpty()) {
+                log.warn("工件码为空,跳过保存数据");
+                hjparams.clear();
+                return;
+            }
+
+            String oprno = MesClient.mes_gw;
+            String lineSn = MesClient.mes_line_sn;
+            String paramsJson = JSON.toJSONString(hjparams);
+
+            JdbcUtils.insertLaserData(oprno, lineSn, curSn, paramsJson);
+
+            log.info("保存参数: sn={}, count={}", curSn, hjparams.size());
+            hjparams.clear();
+
+        } catch (Exception e) {
+            log.error("保存参数异常", e);
+        }
+    }
+
+    /**
+     * 上传结果到MES(参考OP60的sendQuality)
+     */
+    private boolean sendQuality(String sn, String result) {
+        try {
+            // TODO: 调用MES接口上传结果
+            // 参考OP60: DataUtil.sendQuality(nettyClient, sn, result, user, oprno);
+            // 实际项目中需要实现这个接口
+
+            log.info("上传结果: sn={}, result={}", sn, result);
+
+            // 暂时返回true,等待实际MES接口对接
+            return true;
+
+        } catch (Exception e) {
+            log.error("上传结果异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 更新UI状态(静态方法,供MesClient调用)
+     */
+    public static void updateStatusLabel(String text) {
+        try {
+            if(MesClient.fxlabel != null) {
+                MesClient.fxlabel.setText(text);
+            }
+        } catch (Exception e) {
+            log.error("更新状态标签异常", e);
+        }
+    }
+
+    /**
+     * 复位状态(完成后调用)
+     */
+    private void resetStatus() {
+        try {
+            // 1. 复位MES允许信号(激光切割需要,OP60不需要)
+            device.writeMacro(MACRO_MES_ENABLE, 0.0);
+
+            // 2. 清空状态
+            tjFlag = 0;
+            curSn = "";
+            hjparams.clear();
+            tjStatus = 0;
+            startTime = 0;
+
+            log.info("状态已复位");
+
+        } catch (Exception e) {
+            log.error("复位状态异常", e);
+        }
+    }
+
+    /**
+     * 手动复位(上传失败时使用)
+     */
+    public void manualReset() {
+        log.warn("手动复位状态");
+        resetStatus();
+    }
+
+    /**
+     * 获取当前状态
+     */
+    public int getStatus() {
+        return tjFlag;
+    }
+
+    /**
+     * 获取当前工件号
+     */
+    public String getCurrentSn() {
+        return curSn;
+    }
+
+    /**
+     * 获取运行时长(秒)
+     */
+    public long getRunningTime() {
+        if(startTime == 0) {
+            return 0;
+        }
+        return (System.currentTimeMillis() - startTime) / 1000;
+    }
+}

+ 454 - 0
src/com/mes/controller/LaserControllerDual.java

@@ -0,0 +1,454 @@
+package com.mes.controller;
+
+import com.alibaba.fastjson2.JSON;
+import com.mes.device.LaserDevice;
+import com.mes.ui.MesClient;
+import com.mes.util.DateLocalUtils;
+import com.mes.util.JdbcUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 激光切割双面控制器
+ * 参考OP60双面逻辑
+ */
+public class LaserControllerDual {
+
+    private static final Logger log = LoggerFactory.getLogger(LaserControllerDual.class);
+
+    private LaserDevice device;
+
+    // A面状态机 0=未开始 1=已扫码等待启动 2=设备运行中 3=设备运行结束
+    public static Integer tjFlaga = 0;
+    public static String curSna = "";
+    public static List<String> hjparamsA = new ArrayList<>();
+    public static Integer tjStatusa = 0; // 1=提交失败
+    public static Boolean mesQualityFlagA = false; // true=MES质检通过可工作
+
+    // B面状态机
+    public static Integer tjFlagb = 0;
+    public static String curSnb = "";
+    public static List<String> hjparamsB = new ArrayList<>();
+    public static Integer tjStatusb = 0;
+    public static Boolean mesQualityFlagB = false;
+
+    public LaserControllerDual(LaserDevice device) {
+        this.device = device;
+    }
+
+    /**
+     * 状态检测(1秒调用1次,参考OP60的getPlcParam)
+     */
+    public void checkStatus() {
+        try {
+            if(!device.isConnected()) {
+                return;
+            }
+
+            // A面状态检测
+            checkStatusA();
+            // B面状态检测
+            checkStatusB();
+
+        } catch (Exception e) {
+            log.error("状态检测异常", e);
+        }
+    }
+
+    /**
+     * A面状态检测(参考OP60的getStatusA)
+     */
+    private void checkStatusA() {
+        try {
+            // 读取A面加工中状态 #1591
+            int working = device.readWorking("A");
+
+            if(tjFlaga == 1 && working == 1) {
+                // 状态切换:1(等待启动)→ 2(运行中)
+                tjFlaga = 2;
+                log.info("A面检测到设备启动,开始采集参数");
+                MesClient.pxstatus1.setText("A面运行中");
+
+                // 写#1590=0(禁止启动)
+                device.writeMesEnable(0);
+                log.info("写入#1590=0(禁止启动)");
+
+            } else if(tjFlaga == 2 && working == 1) {
+                // 运行中,检测是否完成
+                int finish = device.readFinish("A");
+                if(finish == 1) {
+                    // 状态切换:2(运行中)→ 3(完成)
+                    tjFlaga = 3;
+                    log.info("A面检测到设备完成");
+                    MesClient.pxstatus1.setText("A面完成,提交中");
+
+                    // 保存剩余参数
+                    if(hjparamsA.size() > 0) {
+                        saveParams("A");
+                    }
+
+                    // 上传结果到MES
+                    boolean sendret = sendQuality(curSna, "OK", "A");
+                    if(!sendret) {
+                        tjStatusa = 1;
+                        log.error("A面结果上传MES失败");
+                        MesClient.pxstatus1.setText("A面上传失败");
+                    } else {
+                        log.info("A面结果上传成功,复位状态");
+                        resetStatusA();
+                        MesClient.pxstatus1.setText("A面提交成功");
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("A面状态检测异常", e);
+        }
+    }
+
+    /**
+     * B面状态检测(参考OP60的getStatusB)
+     */
+    private void checkStatusB() {
+        try {
+            // 读取B面加工中状态 #1592
+            int working = device.readWorking("B");
+
+            if(tjFlagb == 1 && working == 1) {
+                // 状态切换:1(等待启动)→ 2(运行中)
+                tjFlagb = 2;
+                log.info("B面检测到设备启动,开始采集参数");
+                MesClient.pxstatus2.setText("B面运行中");
+
+                // 写#1590=0(禁止启动)
+                device.writeMesEnable(0);
+                log.info("写入#1590=0(禁止启动)");
+
+            } else if(tjFlagb == 2 && working == 1) {
+                // 运行中,检测是否完成
+                int finish = device.readFinish("B");
+                if(finish == 1) {
+                    // 状态切换:2(运行中)→ 3(完成)
+                    tjFlagb = 3;
+                    log.info("B面检测到设备完成");
+                    MesClient.pxstatus2.setText("B面完成,提交中");
+
+                    // 保存剩余参数
+                    if(hjparamsB.size() > 0) {
+                        saveParams("B");
+                    }
+
+                    // 上传结果到MES
+                    boolean sendret = sendQuality(curSnb, "OK", "B");
+                    if(!sendret) {
+                        tjStatusb = 1;
+                        log.error("B面结果上传MES失败");
+                        MesClient.pxstatus2.setText("B面上传失败");
+                    } else {
+                        log.info("B面结果上传成功,复位状态");
+                        resetStatusB();
+                        MesClient.pxstatus2.setText("B面提交成功");
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("B面状态检测异常", e);
+        }
+    }
+
+    /**
+     * 参数采集(1秒调用1次,参考OP60的getPlcParams)
+     */
+    public void collectParams() {
+        try {
+            if(!device.isConnected()) {
+                return;
+            }
+
+            // A面采集
+            if(tjFlaga == 2) {
+                collectParamsA();
+            }
+
+            // B面采集
+            if(tjFlagb == 2) {
+                collectParamsB();
+            }
+
+        } catch (Exception e) {
+            log.error("参数采集异常", e);
+        }
+    }
+
+    /**
+     * A面参数采集
+     */
+    private void collectParamsA() {
+        try {
+            // 读取参数
+            double speed = device.readSpeed();           // #33563 进给速度
+            double time = device.readTime("A");          // #1595 加工时间
+            int count = device.readCount("A");           // #1597 零件计数
+
+            // 拼接参数字符串
+            String timestamp = DateLocalUtils.getCurrentTime();
+            String record = speed + "|" + time + "|" + count + "|" + timestamp;
+            hjparamsA.add(record);
+
+            log.debug("A面采集参数: {}", record);
+
+            // 更新UI显示
+            MesClient.param1.setText(String.format("%.1f", speed));
+            MesClient.param2.setText(String.format("%.0f", time));
+            MesClient.param3.setText(String.valueOf(count));
+
+            // 满60条存储
+            if(hjparamsA.size() >= 60) {
+                saveParams("A");
+            }
+
+        } catch (Exception e) {
+            log.error("A面参数采集异常", e);
+        }
+    }
+
+    /**
+     * B面参数采集
+     */
+    private void collectParamsB() {
+        try {
+            // 读取参数
+            double speed = device.readSpeed();           // #33563 进给速度
+            double time = device.readTime("B");          // #1596 加工时间
+            int count = device.readCount("B");           // #1598 零件计数
+
+            // 拼接参数字符串
+            String timestamp = DateLocalUtils.getCurrentTime();
+            String record = speed + "|" + time + "|" + count + "|" + timestamp;
+            hjparamsB.add(record);
+
+            log.debug("B面采集参数: {}", record);
+
+            // 更新UI显示
+            MesClient.param21.setText(String.format("%.1f", speed));
+            MesClient.param22.setText(String.format("%.0f", time));
+            MesClient.param23.setText(String.valueOf(count));
+
+            // 满60条存储
+            if(hjparamsB.size() >= 60) {
+                saveParams("B");
+            }
+
+        } catch (Exception e) {
+            log.error("B面参数采集异常", e);
+        }
+    }
+
+    /**
+     * 保存参数到SQLite
+     */
+    private void saveParams(String side) {
+        try {
+            List<String> params = side.equals("A") ? hjparamsA : hjparamsB;
+            String sn = side.equals("A") ? curSna : curSnb;
+
+            if(params.size() == 0) {
+                return;
+            }
+
+            if(sn == null || sn.trim().isEmpty()) {
+                log.warn("{}面工件码为空,跳过保存数据", side);
+                params.clear();
+                return;
+            }
+
+            String oprno = MesClient.mes_gw;
+            String lineSn = MesClient.mes_line_sn;
+            String paramsJson = JSON.toJSONString(params);
+
+            JdbcUtils.insertLaserData(oprno, lineSn, sn, paramsJson);
+
+            log.info("{}面保存参数: sn={}, count={}", side, sn, params.size());
+            params.clear();
+
+        } catch (Exception e) {
+            log.error("{}面保存参数异常", side, e);
+        }
+    }
+
+    /**
+     * 上传结果到MES
+     */
+    private boolean sendQuality(String sn, String result, String side) {
+        try {
+            // 调用MES接口
+            String user = MesClient.user_menu.getText();
+            String user20 = MesClient.getBarcode(user);
+
+            boolean ret = com.mes.ui.DataUtil.sendQualityNew(
+                MesClient.nettyClient,
+                sn,
+                result,
+                user20
+            );
+
+            log.info("{}面上传结果: sn={}, result={}, ret={}", side, sn, result, ret);
+            return ret;
+
+        } catch (Exception e) {
+            log.error("{}面上传结果异常", side, e);
+            return false;
+        }
+    }
+
+    /**
+     * 复位A面状态
+     */
+    private void resetStatusA() {
+        tjFlaga = 0;
+        curSna = "";
+        hjparamsA.clear();
+        tjStatusa = 0;
+        mesQualityFlagA = false;
+
+        MesClient.product_sn.setText("");
+        MesClient.param1.setText("");
+        MesClient.param2.setText("");
+        MesClient.param3.setText("");
+        MesClient.pxstatus1.setText("A");
+
+        log.info("A面状态已复位");
+    }
+
+    /**
+     * 复位B面状态
+     */
+    private void resetStatusB() {
+        tjFlagb = 0;
+        curSnb = "";
+        hjparamsB.clear();
+        tjStatusb = 0;
+        mesQualityFlagB = false;
+
+        MesClient.product_sn2.setText("");
+        MesClient.param21.setText("");
+        MesClient.param22.setText("");
+        MesClient.param23.setText("");
+        MesClient.pxstatus2.setText("B");
+
+        log.info("B面状态已复位");
+    }
+
+    /**
+     * 扫码处理(A面)
+     */
+    public boolean onScanCodeA(String sn, String user) {
+        try {
+            // 检查状态
+            if(tjFlaga != 0) {
+                log.warn("A面上一个工件未完成");
+                return false;
+            }
+
+            log.info("A面扫码: {}", sn);
+            curSna = sn;
+            tjFlaga = 1;
+
+            // 等待MES质检回复后再写#1590=1
+            // 这里先标记状态,等DataUtil.checkQuality回调后再写
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("A面扫码处理异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 扫码处理(B面)
+     */
+    public boolean onScanCodeB(String sn, String user) {
+        try {
+            // 检查状态
+            if(tjFlagb != 0) {
+                log.warn("B面上一个工件未完成");
+                return false;
+            }
+
+            log.info("B面扫码: {}", sn);
+            curSnb = sn;
+            tjFlagb = 1;
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("B面扫码处理异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * MES质检通过回调(A面)
+     */
+    public void onMesQualityPassA() {
+        try {
+            mesQualityFlagA = true;
+
+            // 写#1590=1(允许启动)
+            boolean ret = device.writeMesEnable(1);
+            if(ret) {
+                log.info("A面MES质检通过,写入#1590=1(允许启动)");
+                MesClient.pxstatus1.setText("A面等待启动");
+            } else {
+                log.error("A面写入#1590=1失败");
+                MesClient.pxstatus1.setText("A面写信号失败");
+            }
+
+        } catch (Exception e) {
+            log.error("A面MES质检回调异常", e);
+        }
+    }
+
+    /**
+     * MES质检通过回调(B面)
+     */
+    public void onMesQualityPassB() {
+        try {
+            mesQualityFlagB = true;
+
+            // 写#1590=1(允许启动)
+            boolean ret = device.writeMesEnable(1);
+            if(ret) {
+                log.info("B面MES质检通过,写入#1590=1(允许启动)");
+                MesClient.pxstatus2.setText("B面等待启动");
+            } else {
+                log.error("B面写入#1590=1失败");
+                MesClient.pxstatus2.setText("B面写信号失败");
+            }
+
+        } catch (Exception e) {
+            log.error("B面MES质检回调异常", e);
+        }
+    }
+
+    /**
+     * 手动复位A面
+     */
+    public void manualResetA() {
+        log.warn("手动复位A面状态");
+        resetStatusA();
+    }
+
+    /**
+     * 手动复位B面
+     */
+    public void manualResetB() {
+        log.warn("手动复位B面状态");
+        resetStatusB();
+    }
+}

+ 298 - 0
src/com/mes/device/LaserDevice.java

@@ -0,0 +1,298 @@
+package com.mes.device;
+
+import com.mes.device.RemoteCommLibrary;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 激光切割设备通讯类
+ * 封装RemoteComm.dll的调用
+ */
+public class LaserDevice {
+
+    private static final Logger log = LoggerFactory.getLogger(LaserDevice.class);
+
+    private int handle = -1;
+    private String deviceIp = "";
+    private RemoteCommLibrary rmi = RemoteCommLibrary.INSTANCE;
+
+    /**
+     * 连接设备
+     * @param ip 设备IP地址
+     * @return 是否连接成功
+     */
+    public boolean connect(String ip) {
+        try {
+            if(handle >= 0) {
+                log.warn("设备已连接,先断开旧连接");
+                disconnect();
+            }
+
+            log.info("正在连接设备: {}", ip);
+            handle = rmi.remote_new_connect(ip);
+
+            if(handle >= 0) {
+                deviceIp = ip;
+                log.info("设备连接成功, handle={}", handle);
+                return true;
+            } else {
+                log.error("设备连接失败, 错误码={}", handle);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("连接设备异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 断开连接
+     */
+    public void disconnect() {
+        try {
+            if(handle >= 0) {
+                rmi.remote_close(handle);
+                log.info("设备连接已断开");
+                handle = -1;
+                deviceIp = "";
+            }
+        } catch (Exception e) {
+            log.error("断开连接异常", e);
+        }
+    }
+
+    /**
+     * 检查连接状态
+     * @return 是否已连接
+     */
+    public boolean isConnected() {
+        try {
+            if(handle < 0) {
+                return false;
+            }
+            int status = rmi.remote_connect_status(handle);
+            return status == 0;
+        } catch (Exception e) {
+            log.error("检查连接状态异常", e);
+            return false;
+        }
+    }
+
+    /**
+     * 读取宏变量
+     * @param addr 宏变量地址
+     * @return 宏变量值,失败返回0.0
+     */
+    public double readMacro(int addr) {
+        try {
+            if(handle < 0) {
+                log.warn("设备未连接,无法读取宏变量#{}", addr);
+                return 0.0;
+            }
+
+            double[] value = new double[1];
+            int ret = rmi.remote_read_macro_p(handle, addr, value);
+
+            if(ret == 0) {
+                return value[0];
+            } else {
+                log.warn("读取宏变量#{}失败, 错误码={}", addr, ret);
+                return 0.0;
+            }
+        } catch (Exception e) {
+            log.error("读取宏变量#{}异常", addr, e);
+            return 0.0;
+        }
+    }
+
+    /**
+     * 写入宏变量
+     * @param addr 宏变量地址
+     * @param value 要写入的值
+     * @return 是否成功
+     */
+    public boolean writeMacro(int addr, double value) {
+        try {
+            if(handle < 0) {
+                log.warn("设备未连接,无法写入宏变量#{}", addr);
+                return false;
+            }
+
+            int ret = rmi.remote_write_macro(handle, addr, value);
+
+            if(ret == 0) {
+                log.info("写入宏变量#{}={}", addr, value);
+                return true;
+            } else {
+                log.warn("写入宏变量#{}={}失败, 错误码={}", addr, value, ret);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("写入宏变量#{}={}异常", addr, value, e);
+            return false;
+        }
+    }
+
+    /**
+     * 读取PLC变量
+     * @param addr PLC地址,如"42.0"
+     * @param type 变量类型 0=MI, 1=MO
+     * @return PLC变量值,失败返回0
+     */
+    public long readPlc(String addr, int type) {
+        try {
+            if(handle < 0) {
+                log.warn("设备未连接,无法读取PLC变量{}", addr);
+                return 0;
+            }
+
+            long[] value = new long[1];
+            int ret = rmi.remote_read_plc_variable_p_2(handle, addr, type, value);
+
+            if(ret == 0) {
+                return value[0];
+            } else {
+                log.warn("读取PLC变量{}失败, 错误码={}", addr, ret);
+                return 0;
+            }
+        } catch (Exception e) {
+            log.error("读取PLC变量{}异常", addr, e);
+            return 0;
+        }
+    }
+
+    /**
+     * 写入PLC变量
+     * @param addr PLC地址
+     * @param type 变量类型 0=MI, 1=MO
+     * @param value 要写入的值
+     * @return 是否成功
+     */
+    public boolean writePlc(String addr, int type, long value) {
+        try {
+            if(handle < 0) {
+                log.warn("设备未连接,无法写入PLC变量{}", addr);
+                return false;
+            }
+
+            int ret = rmi.remote_write_plc_variable_2(handle, addr, type, value);
+
+            if(ret == 0) {
+                log.info("写入PLC变量{}={}", addr, value);
+                return true;
+            } else {
+                log.warn("写入PLC变量{}={}失败, 错误码={}", addr, value, ret);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("写入PLC变量{}={}异常", addr, value, e);
+            return false;
+        }
+    }
+
+    /**
+     * 批量读取宏变量(提高效率)
+     * @param startAddr 起始地址
+     * @param endAddr 结束地址
+     * @return 宏变量值数组,失败返回null
+     */
+    public double[] readMacroRange(int startAddr, int endAddr) {
+        try {
+            if(handle < 0) {
+                log.warn("设备未连接,无法读取宏变量范围");
+                return null;
+            }
+
+            int count = endAddr - startAddr + 1;
+            if(count > 100) {
+                log.warn("批量读取宏变量数量超过100个限制");
+                return null;
+            }
+
+            double[] values = new double[count];
+            int ret = rmi.remote_get_macro_range(handle, startAddr, endAddr, values);
+
+            if(ret == 0) {
+                return values;
+            } else {
+                log.warn("批量读取宏变量#{}~#{}失败, 错误码={}", startAddr, endAddr, ret);
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("批量读取宏变量异常", e);
+            return null;
+        }
+    }
+
+    /**
+     * 获取设备IP
+     */
+    public String getDeviceIp() {
+        return deviceIp;
+    }
+
+    /**
+     * 获取句柄
+     */
+    public int getHandle() {
+        return handle;
+    }
+
+    // ==================== 业务封装方法 ====================
+
+    /**
+     * 写入MES允许启动信号(#1590)
+     * @param enable 0=禁止启动,1=允许启动
+     */
+    public boolean writeMesEnable(int enable) {
+        return writeMacro(1590, enable);
+    }
+
+    /**
+     * 读取加工中状态
+     * @param side "A"或"B"
+     * @return 0=未加工,1=加工中
+     */
+    public int readWorking(String side) {
+        int addr = side.equals("A") ? 1591 : 1592;
+        return (int) readMacro(addr);
+    }
+
+    /**
+     * 读取加工完成状态
+     * @param side "A"或"B"
+     * @return 1=完成
+     */
+    public int readFinish(String side) {
+        int addr = side.equals("A") ? 1593 : 1594;
+        return (int) readMacro(addr);
+    }
+
+    /**
+     * 读取加工时间
+     * @param side "A"或"B"
+     * @return 加工时间(秒)
+     */
+    public double readTime(String side) {
+        int addr = side.equals("A") ? 1595 : 1596;
+        return readMacro(addr);
+    }
+
+    /**
+     * 读取零件计数
+     * @param side "A"或"B"
+     * @return 零件数量
+     */
+    public int readCount(String side) {
+        int addr = side.equals("A") ? 1597 : 1598;
+        return (int) readMacro(addr);
+    }
+
+    /**
+     * 读取进给速度(通用)
+     * @return 进给速度(mm/min)
+     */
+    public double readSpeed() {
+        return readMacro(33563);
+    }
+}

+ 196 - 0
src/com/mes/device/RemoteCommLibrary.java

@@ -0,0 +1,196 @@
+package com.mes.device;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.ptr.DoubleByReference;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+
+/**
+ * 铼钠克数控系统远程通讯库 JNA 接口
+ * RemoteComm.dll v2.6.2.1
+ */
+public interface RemoteCommLibrary extends Library {
+
+    // 只加载DLL文件名,不带路径
+    // 运行时通过 -Djava.library.path=lib 指定DLL所在目录
+    String DLL_NAME = System.getProperty("os.arch").contains("64")
+        ? "RemoteComm_x64"
+        : "RemoteComm_x86";
+
+    RemoteCommLibrary INSTANCE = Native.load(DLL_NAME, RemoteCommLibrary.class);
+
+    // ========== 连接管理 ==========
+
+    /**
+     * 创建TCP连接
+     * @param ip 控制器IP地址
+     * @return 句柄 >= 0: 成功, -1: 超过最大连接数, -3: 连接失败, -4: ftp模块连接失败
+     */
+    int remote_new_connect(String ip);
+
+    /**
+     * 关闭连接
+     * @param nHandle 句柄
+     */
+    void remote_close(int nHandle);
+
+    /**
+     * 连接状态
+     * @param nHandle 句柄
+     * @return 0: 已连接, -1: 连接断开, -2: 未建立连接
+     */
+    int remote_connect_status(int nHandle);
+
+    // ========== 宏变量读写 ==========
+
+    /**
+     * 读#宏变量
+     * @param nHandle 句柄
+     * @param nMacro 宏变量号
+     * @param val 读取的数据存放位置
+     * @return 0: 成功, -2: 未建立连接, -5: 宏变量号错误, -6: 发送失败, -7: 接收失败
+     */
+    int remote_read_macro_p(int nHandle, int nMacro, double[] val);
+
+    /**
+     * 写#宏变量
+     * @param nHandle 句柄
+     * @param nMacro 宏变量号
+     * @param val 要写入的值
+     * @return 0: 成功, -2: 未建立连接, -5: 宏变量号错误, -6: 发送失败, -7: 接收失败
+     */
+    int remote_write_macro(int nHandle, int nMacro, double val);
+
+    /**
+     * 批量读取宏变量
+     * @param nHandle 句柄
+     * @param startMacro 起始宏变量号
+     * @param endMacro 结束宏变量号
+     * @param values 读取的数据存放位置
+     * @return 0: 成功, 其他: 失败
+     */
+    int remote_get_macro_range(int nHandle, int startMacro, int endMacro, double[] values);
+
+    // ========== PLC变量读写 ==========
+
+    /**
+     * 读PLC变量
+     * @param nHandle 句柄
+     * @param PLCAdress PLC变量地址 如 "204.7"
+     * @param type 变量类型 MI=0, MO=1
+     * @param val 存储变量
+     * @return 0: 成功, -2: 未建立连接, -6: 发送失败, -7: 接收失败, -11: PLC变量名错误
+     */
+    int remote_read_plc_variable_p_2(int nHandle, String PLCAdress, int type, long[] val);
+
+    /**
+     * 写PLC变量
+     * @param nHandle 句柄
+     * @param PLCAdress PLC变量地址
+     * @param type 变量类型 MI=0, MO=1
+     * @param val 目标值
+     * @return 0: 成功, -2: 未建立连接, -6: 发送失败, -7: 接收失败, -11: PLC变量名错误
+     */
+    int remote_write_plc_variable_2(int nHandle, String PLCAdress, int type, long val);
+
+    // ========== 文件操作 ==========
+
+    /**
+     * 远程打开控制器端文件
+     * @param nHandle 句柄
+     * @param nChannel 通道号 1=通道一, 2=通道二
+     * @param chFileName 文件名 如 "/NC1/filename"
+     * @return 0: 成功, -1: 打开失败, -2: 未建立连接, -6: 发送失败, -7: 接收失败, -8: 通道错误
+     */
+    int remote_open_file(int nHandle, int nChannel, String chFileName);
+
+    /**
+     * 上传文件到控制器
+     * @param nHandle 句柄
+     * @param chFileName 本地文件名(含路径)
+     * @param nPath 路径编号 1001=NC1, 1002=NC2, ...
+     * @return 0: 成功, -1: 传输失败, -2: 未建立连接, -4: ftp连接失败, -10: 文件打开失败
+     */
+    int remote_upload_file(int nHandle, String chFileName, int nPath);
+
+    /**
+     * 从控制器下载文件
+     * @param nHandle 句柄
+     * @param nPath 路径编号
+     * @param chFileName 文件名
+     * @param outputDir 输出目录
+     * @return 0: 成功, -1: 传输失败, -2: 未建立连接, -4: ftp连接失败, -10: 文件打开失败
+     */
+    int remote_download_file(int nHandle, int nPath, String chFileName, String outputDir);
+
+    /**
+     * 删除控制器端文件
+     * @param nHandle 句柄
+     * @param chFileName 文件名
+     * @param nPath 路径编号
+     * @return 0: 成功, -1: 传输失败, -2: 未建立连接
+     */
+    int remote_delete_file(int nHandle, String chFileName, int nPath);
+
+    // ========== 坐标获取 ==========
+
+    /**
+     * 获取各轴绝对坐标
+     * @param nHandle 句柄
+     * @param coordType 选择轴 (X,Y,Z,A,B,C,U,V,W) 111111111 按顺序 0=不获取, 1=获取
+     * @param value 获取到的坐标数组
+     * @return 0: 成功, -1: 获取失败, -2: 无连接, -23: 轴名称错误
+     */
+    int remote_get_AbsolutCoordinate(int nHandle, int coordType, double[] value);
+
+    /**
+     * 获取各轴相对坐标
+     */
+    int remote_get_RelativelyCoordinate(int nHandle, int coordType, double[] value);
+
+    /**
+     * 获取各轴机械坐标
+     */
+    int remote_get_MachineCoordinate(int nHandle, int coordType, double[] value);
+
+    // ========== 加工信息 ==========
+
+    /**
+     * 获取主轴实际转速
+     */
+    int remote_get_S_CurrentSpeed(int nHandle, int channel, double[] value);
+
+    /**
+     * 获取实际进给速度
+     */
+    int remote_get_F_CurrentSpeed(int nHandle, int channel, double[] value);
+
+    // ========== PLC操作 ==========
+
+    /**
+     * 切换模式
+     * @param ModeType 1=Codeless/无代码, 2=MEM/自动, 4=Handle/手轮, 5=Jog, 6=Home/原点复归, 8=MDI
+     */
+    int remote_switch_mode(int nHandle, int ModeType);
+
+    /**
+     * 启动程序
+     */
+    int remote_cycle_start(int nHandle, int channel);
+
+    /**
+     * 程序暂停
+     */
+    int remote_cycle_pause(int nHandle, int channel);
+
+    /**
+     * 程序停止
+     */
+    int remote_cycle_stop(int nHandle, int channel);
+
+    /**
+     * 复位
+     */
+    int remote_reset(int nHandle, int channel);
+}

+ 643 - 0
src/com/mes/device/RmiTestDemo.java

@@ -0,0 +1,643 @@
+package com.mes.device;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 铼钠克数控系统通讯测试工具
+ * 测试 RemoteComm.dll 基本功能
+ */
+public class RmiTestDemo extends JFrame {
+
+    private JTextField ipField;
+    private JButton connectBtn;
+    private JButton disconnectBtn;
+    private JLabel statusLabel;
+
+    private JTextField macroAddrField;
+    private JTextField macroValueField;
+    private JButton readMacroBtn;
+    private JButton writeMacroBtn;
+
+    private JTextField plcAddrField;
+    private JTextField plcValueField;
+    private JComboBox<String> plcTypeCombo;
+    private JButton readPlcBtn;
+    private JButton writePlcBtn;
+
+    private JTextArea logArea;
+
+    // 信号监控相关
+    private JLabel light3100, light3200, lightRun, lightStop, lightReady;
+    private JButton btn3100, btn3200;
+    private JLabel valueSpeed, valueTime, valueCount;
+    private java.util.Timer monitorTimer;
+
+    private int handle = -1;
+    private RemoteCommLibrary rmi = RemoteCommLibrary.INSTANCE;
+
+    public RmiTestDemo() {
+        setTitle("铼钠克数控系统通讯测试");
+        setSize(1000, 800);  // 增加窗口高度 700→800
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        setLocationRelativeTo(null);
+
+        initUI();
+    }
+
+    private void initUI() {
+        JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+        mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+        // 连接区域
+        JPanel connectPanel = createConnectPanel();
+        mainPanel.add(connectPanel, BorderLayout.NORTH);
+
+        // 使用标签页
+        JTabbedPane tabbedPane = new JTabbedPane();
+
+        // 标签页1: 手动测试
+        JPanel manualPanel = new JPanel(new GridLayout(2, 1, 10, 10));
+        manualPanel.add(createMacroPanel());
+        manualPanel.add(createPlcPanel());
+        tabbedPane.addTab("手动测试", manualPanel);
+
+        // 标签页2: 信号监控
+        JPanel monitorPanel = createMonitorPanel();
+        tabbedPane.addTab("信号监控", monitorPanel);
+
+        mainPanel.add(tabbedPane, BorderLayout.CENTER);
+
+        // 日志区域
+        JPanel logPanel = createLogPanel();
+        mainPanel.add(logPanel, BorderLayout.SOUTH);
+
+        add(mainPanel);
+    }
+
+    // 连接面板
+    private JPanel createConnectPanel() {
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        panel.setBorder(BorderFactory.createTitledBorder("连接设置"));
+
+        panel.add(new JLabel("设备IP:"));
+        ipField = new JTextField("192.168.2.199", 15);
+        panel.add(ipField);
+
+        connectBtn = new JButton("连接");
+        connectBtn.addActionListener(e -> doConnect());
+        panel.add(connectBtn);
+
+        disconnectBtn = new JButton("断开");
+        disconnectBtn.setEnabled(false);
+        disconnectBtn.addActionListener(e -> doDisconnect());
+        panel.add(disconnectBtn);
+
+        statusLabel = new JLabel("未连接");
+        statusLabel.setForeground(Color.RED);
+        panel.add(statusLabel);
+
+        return panel;
+    }
+
+    // 宏变量面板
+    private JPanel createMacroPanel() {
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        panel.setBorder(BorderFactory.createTitledBorder("宏变量操作 (#号)"));
+
+        panel.add(new JLabel("地址:"));
+        macroAddrField = new JTextField("2113", 8);
+        panel.add(macroAddrField);
+
+        panel.add(new JLabel("值:"));
+        macroValueField = new JTextField("0.0", 12);
+        panel.add(macroValueField);
+
+        readMacroBtn = new JButton("读取");
+        readMacroBtn.setEnabled(false);
+        readMacroBtn.addActionListener(e -> doReadMacro());
+        panel.add(readMacroBtn);
+
+        writeMacroBtn = new JButton("写入");
+        writeMacroBtn.setEnabled(false);
+        writeMacroBtn.addActionListener(e -> doWriteMacro());
+        panel.add(writeMacroBtn);
+
+        return panel;
+    }
+
+    // PLC变量面板
+    private JPanel createPlcPanel() {
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        panel.setBorder(BorderFactory.createTitledBorder("PLC变量操作"));
+
+        panel.add(new JLabel("地址:"));
+        plcAddrField = new JTextField("204.7", 8);
+        panel.add(plcAddrField);
+
+        panel.add(new JLabel("类型:"));
+        plcTypeCombo = new JComboBox<>(new String[]{"MI(0)", "MO(1)"});
+        plcTypeCombo.setSelectedIndex(0);
+        panel.add(plcTypeCombo);
+
+        panel.add(new JLabel("值:"));
+        plcValueField = new JTextField("0", 12);
+        panel.add(plcValueField);
+
+        readPlcBtn = new JButton("读取");
+        readPlcBtn.setEnabled(false);
+        readPlcBtn.addActionListener(e -> doReadPlc());
+        panel.add(readPlcBtn);
+
+        writePlcBtn = new JButton("写入");
+        writePlcBtn.setEnabled(false);
+        writePlcBtn.addActionListener(e -> doWritePlc());
+        panel.add(writePlcBtn);
+
+        return panel;
+    }
+
+    // 日志面板
+    private JPanel createLogPanel() {
+        JPanel panel = new JPanel(new BorderLayout());
+        panel.setBorder(BorderFactory.createTitledBorder("日志"));
+
+        logArea = new JTextArea(10, 60);
+        logArea.setEditable(false);
+        logArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
+        JScrollPane scrollPane = new JScrollPane(logArea);
+        panel.add(scrollPane, BorderLayout.CENTER);
+
+        JButton clearBtn = new JButton("清空日志");
+        clearBtn.addActionListener(e -> logArea.setText(""));
+        panel.add(clearBtn, BorderLayout.SOUTH);
+
+        return panel;
+    }
+
+    // 连接设备
+    private void doConnect() {
+        String ip = ipField.getText().trim();
+        if (ip.isEmpty()) {
+            log("错误: IP地址不能为空");
+            return;
+        }
+
+        log("正在连接 " + ip + " ...");
+
+        new Thread(() -> {
+            try {
+                handle = rmi.remote_new_connect(ip);
+
+                SwingUtilities.invokeLater(() -> {
+                    if (handle >= 0) {
+                        log("连接成功! 句柄=" + handle);
+                        statusLabel.setText("已连接 (句柄:" + handle + ")");
+                        statusLabel.setForeground(new Color(0, 150, 0));
+                        connectBtn.setEnabled(false);
+                        disconnectBtn.setEnabled(true);
+                        enableFunctionButtons(true);
+                    } else {
+                        String errMsg = getErrorMessage(handle);
+                        log("连接失败: " + errMsg);
+                        statusLabel.setText("连接失败");
+                        statusLabel.setForeground(Color.RED);
+                    }
+                });
+            } catch (Exception e) {
+                SwingUtilities.invokeLater(() -> {
+                    log("连接异常: " + e.getMessage());
+                    e.printStackTrace();
+                });
+            }
+        }).start();
+    }
+
+    // 断开连接
+    private void doDisconnect() {
+        if (handle < 0) {
+            log("未连接设备");
+            return;
+        }
+
+        log("正在断开连接...");
+
+        new Thread(() -> {
+            try {
+                rmi.remote_close(handle);
+
+                SwingUtilities.invokeLater(() -> {
+                    log("已断开连接");
+                    handle = -1;
+                    statusLabel.setText("未连接");
+                    statusLabel.setForeground(Color.RED);
+                    connectBtn.setEnabled(true);
+                    disconnectBtn.setEnabled(false);
+                    enableFunctionButtons(false);
+                });
+            } catch (Exception e) {
+                SwingUtilities.invokeLater(() -> log("断开异常: " + e.getMessage()));
+            }
+        }).start();
+    }
+
+    // 读取宏变量
+    private void doReadMacro() {
+        if (handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        try {
+            int addr = Integer.parseInt(macroAddrField.getText().trim());
+            double[] value = new double[1];
+
+            log("读取宏变量 #" + addr + " ...");
+
+            int ret = rmi.remote_read_macro_p(handle, addr, value);
+
+            if (ret == 0) {
+                macroValueField.setText(String.valueOf(value[0]));
+                log("读取成功: #" + addr + " = " + value[0]);
+            } else {
+                log("读取失败: " + getErrorMessage(ret));
+            }
+        } catch (NumberFormatException e) {
+            log("错误: 宏变量地址格式错误");
+        } catch (Exception e) {
+            log("读取异常: " + e.getMessage());
+        }
+    }
+
+    // 写入宏变量
+    private void doWriteMacro() {
+        if (handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        try {
+            int addr = Integer.parseInt(macroAddrField.getText().trim());
+            double value = Double.parseDouble(macroValueField.getText().trim());
+
+            log("写入宏变量 #" + addr + " = " + value + " ...");
+
+            int ret = rmi.remote_write_macro(handle, addr, value);
+
+            if (ret == 0) {
+                log("写入成功: #" + addr + " = " + value);
+            } else {
+                log("写入失败: " + getErrorMessage(ret));
+            }
+        } catch (NumberFormatException e) {
+            log("错误: 地址或值格式错误");
+        } catch (Exception e) {
+            log("写入异常: " + e.getMessage());
+        }
+    }
+
+    // 读取PLC变量
+    private void doReadPlc() {
+        if (handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        try {
+            String addr = plcAddrField.getText().trim();
+            int type = plcTypeCombo.getSelectedIndex(); // 0=MI, 1=MO
+            long[] value = new long[1];
+
+            log("读取PLC变量 " + addr + " (类型=" + type + ") ...");
+
+            int ret = rmi.remote_read_plc_variable_p_2(handle, addr, type, value);
+
+            if (ret == 0) {
+                plcValueField.setText(String.valueOf(value[0]));
+                log("读取成功: " + addr + " = " + value[0]);
+            } else {
+                log("读取失败: " + getErrorMessage(ret));
+            }
+        } catch (Exception e) {
+            log("读取异常: " + e.getMessage());
+        }
+    }
+
+    // 写入PLC变量
+    private void doWritePlc() {
+        if (handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        try {
+            String addr = plcAddrField.getText().trim();
+            int type = plcTypeCombo.getSelectedIndex();
+            long value = Long.parseLong(plcValueField.getText().trim());
+
+            log("写入PLC变量 " + addr + " = " + value + " (类型=" + type + ") ...");
+
+            int ret = rmi.remote_write_plc_variable_2(handle, addr, type, value);
+
+            if (ret == 0) {
+                log("写入成功: " + addr + " = " + value);
+            } else {
+                log("写入失败: " + getErrorMessage(ret));
+            }
+        } catch (NumberFormatException e) {
+            log("错误: 值格式错误");
+        } catch (Exception e) {
+            log("写入异常: " + e.getMessage());
+        }
+    }
+
+    // 启用/禁用功能按钮
+    private void enableFunctionButtons(boolean enabled) {
+        readMacroBtn.setEnabled(enabled);
+        writeMacroBtn.setEnabled(enabled);
+        readPlcBtn.setEnabled(enabled);
+        writePlcBtn.setEnabled(enabled);
+    }
+
+    // 错误信息映射
+    private String getErrorMessage(int code) {
+        switch (code) {
+            case 0: return "成功";
+            case -1: return "失败/超过最大连接数";
+            case -2: return "未建立连接";
+            case -3: return "连接失败";
+            case -4: return "FTP连接失败";
+            case -5: return "宏变量地址错误";
+            case -6: return "发送失败";
+            case -7: return "接收失败";
+            case -8: return "通道错误";
+            case -11: return "PLC变量错误";
+            default: return "未知错误(" + code + ")";
+        }
+    }
+
+    // 日志输出
+    private void log(String msg) {
+        String timestamp = new SimpleDateFormat("HH:mm:ss").format(new Date());
+        logArea.append("[" + timestamp + "] " + msg + "\n");
+        logArea.setCaretPosition(logArea.getDocument().getLength());
+    }
+
+    // 创建信号监控面板
+    private JPanel createMonitorPanel() {
+        JPanel panel = new JPanel(new BorderLayout(10, 10));
+        panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+        // 控制面板 - 使用BoxLayout垂直排列
+        JPanel centerPanel = new JPanel();
+        centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
+
+        // 控制信号
+        centerPanel.add(createControlSignalPanel());
+        // 状态信号
+        centerPanel.add(createStatusSignalPanel());
+        // 参数显示
+        centerPanel.add(createValuePanel());
+
+        JScrollPane scrollPane = new JScrollPane(centerPanel);
+        panel.add(scrollPane, BorderLayout.CENTER);
+
+        // 底部按钮
+        JPanel btnPanel = new JPanel(new FlowLayout());
+        JButton startMonitorBtn = new JButton("启动监控");
+        JButton stopMonitorBtn = new JButton("停止监控");
+
+        startMonitorBtn.addActionListener(e -> startMonitor());
+        stopMonitorBtn.addActionListener(e -> stopMonitor());
+
+        btnPanel.add(startMonitorBtn);
+        btnPanel.add(stopMonitorBtn);
+        panel.add(btnPanel, BorderLayout.SOUTH);
+
+        return panel;
+    }
+
+    // 控制信号面板
+    private JPanel createControlSignalPanel() {
+        JPanel panel = new JPanel(new GridLayout(2, 1, 10, 10));  // 间距5→10
+        panel.setBorder(BorderFactory.createTitledBorder("控制信号(可写)"));
+
+        // #3100
+        JPanel row1 = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 8));  // 增加垂直间距
+        JLabel name1 = new JLabel("#3100 MES允许启动");
+        name1.setPreferredSize(new Dimension(200, 30));  // 高度25→30
+        light3100 = createLight(Color.GRAY);
+        btn3100 = new JButton("切换");
+        btn3100.addActionListener(e -> toggleMacro(3100, light3100));
+        row1.add(name1);
+        row1.add(light3100);
+        row1.add(btn3100);
+        panel.add(row1);
+
+        // #3200
+        JPanel row2 = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 8));  // 增加垂直间距
+        JLabel name2 = new JLabel("#3200 MES屏蔽");
+        name2.setPreferredSize(new Dimension(200, 30));  // 高度25→30
+        light3200 = createLight(Color.GRAY);
+        btn3200 = new JButton("切换");
+        btn3200.addActionListener(e -> toggleMacro(3200, light3200));
+        row2.add(name2);
+        row2.add(light3200);
+        row2.add(btn3200);
+        panel.add(row2);
+
+        return panel;
+    }
+
+    // 状态信号面板
+    private JPanel createStatusSignalPanel() {
+        JPanel panel = new JPanel(new GridLayout(3, 1, 10, 10));  // 间距5→10
+        panel.setBorder(BorderFactory.createTitledBorder("状态信号(只读)"));
+
+        panel.add(createStatusRow("MO42.0 运行中", lightRun = createLight(Color.GRAY)));
+        panel.add(createStatusRow("MO42.2 停止", lightStop = createLight(Color.GRAY)));
+        panel.add(createStatusRow("MO42.3 就绪", lightReady = createLight(Color.GRAY)));
+
+        return panel;
+    }
+
+    // 参数显示面板
+    private JPanel createValuePanel() {
+        JPanel panel = new JPanel(new GridLayout(3, 1, 10, 10));  // 间距5→10
+        panel.setBorder(BorderFactory.createTitledBorder("参数显示"));
+
+        panel.add(createValueRow("#33563 进给速度", valueSpeed = createValueLabel(), "mm/min"));
+        panel.add(createValueRow("#33868 加工时间", valueTime = createValueLabel(), "秒"));
+        panel.add(createValueRow("#33870 零件计数", valueCount = createValueLabel(), "个"));
+
+        return panel;
+    }
+
+    // 创建指示灯
+    private JLabel createLight(Color color) {
+        JLabel light = new JLabel("●");
+        light.setFont(new Font("Arial", Font.BOLD, 24));
+        light.setForeground(color);
+        return light;
+    }
+
+    // 创建数值标签
+    private JLabel createValueLabel() {
+        JLabel label = new JLabel("0.0");
+        label.setFont(new Font("Arial", Font.BOLD, 14));
+        label.setForeground(new Color(0, 100, 200));
+        return label;
+    }
+
+    // 创建状态行
+    private JPanel createStatusRow(String name, JLabel light) {
+        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 8));  // 增加垂直间距
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setPreferredSize(new Dimension(200, 30));  // 高度25→30
+        row.add(nameLabel);
+        row.add(light);
+        return row;
+    }
+
+    // 创建数值行
+    private JPanel createValueRow(String name, JLabel valueLabel, String unit) {
+        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 8));  // 增加垂直间距
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setPreferredSize(new Dimension(200, 30));  // 高度25→30
+        JLabel unitLabel = new JLabel(unit);
+        unitLabel.setForeground(Color.GRAY);
+        row.add(nameLabel);
+        row.add(valueLabel);
+        row.add(unitLabel);
+        return row;
+    }
+
+    // 切换宏变量
+    private void toggleMacro(int addr, JLabel light) {
+        if(handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        try {
+            // 读取当前值
+            double[] current = new double[1];
+            int ret = rmi.remote_read_macro_p(handle, addr, current);
+            if(ret != 0) {
+                log("读取#" + addr + "失败: " + getErrorMessage(ret));
+                return;
+            }
+
+            // 切换值
+            double newValue = (current[0] == 0) ? 1.0 : 0.0;
+            ret = rmi.remote_write_macro(handle, addr, newValue);
+
+            if(ret == 0) {
+                updateLight(light, newValue == 1.0);
+                log("切换#" + addr + " = " + newValue);
+            } else {
+                log("写入#" + addr + "失败: " + getErrorMessage(ret));
+            }
+
+        } catch (Exception e) {
+            log("切换#" + addr + "异常: " + e.getMessage());
+        }
+    }
+
+    // 启动监控
+    private void startMonitor() {
+        if(handle < 0) {
+            log("请先连接设备");
+            return;
+        }
+
+        if(monitorTimer != null) {
+            log("监控已在运行");
+            return;
+        }
+
+        monitorTimer = new java.util.Timer();
+        monitorTimer.schedule(new java.util.TimerTask() {
+            @Override
+            public void run() {
+                updateMonitor();
+            }
+        }, 0, 500);  // 500ms刷新
+
+        log("启动信号监控");
+    }
+
+    // 停止监控
+    private void stopMonitor() {
+        if(monitorTimer != null) {
+            monitorTimer.cancel();
+            monitorTimer = null;
+            log("停止信号监控");
+        }
+    }
+
+    // 更新监控数据
+    private void updateMonitor() {
+        if(handle < 0) return;
+
+        try {
+            // 读取控制信号
+            double[] val3100 = new double[1];
+            double[] val3200 = new double[1];
+            rmi.remote_read_macro_p(handle, 3100, val3100);
+            rmi.remote_read_macro_p(handle, 3200, val3200);
+
+            // 读取状态信号
+            long[] running = new long[1];
+            long[] stopped = new long[1];
+            long[] ready = new long[1];
+            rmi.remote_read_plc_variable_p_2(handle, "42.0", 1, running);
+            rmi.remote_read_plc_variable_p_2(handle, "42.2", 1, stopped);
+            rmi.remote_read_plc_variable_p_2(handle, "42.3", 1, ready);
+
+            // 读取参数
+            double[] speed = new double[1];
+            double[] time = new double[1];
+            double[] count = new double[1];
+            rmi.remote_read_macro_p(handle, 33563, speed);
+            rmi.remote_read_macro_p(handle, 33868, time);
+            rmi.remote_read_macro_p(handle, 33870, count);
+
+            // 更新UI
+            SwingUtilities.invokeLater(() -> {
+                updateLight(light3100, val3100[0] == 1.0);
+                updateLight(light3200, val3200[0] == 1.0);
+                updateLight(lightRun, running[0] == 1);
+                updateLight(lightStop, stopped[0] == 1);
+                updateLight(lightReady, ready[0] == 1);
+
+                valueSpeed.setText(String.format("%.2f", speed[0]));
+                valueTime.setText(String.format("%.1f", time[0]));
+                valueCount.setText(String.format("%.0f", count[0]));
+            });
+
+        } catch (Exception e) {
+            // 忽略异常,继续监控
+        }
+    }
+
+    // 更新指示灯
+    private void updateLight(JLabel light, boolean on) {
+        if(on) {
+            light.setForeground(new Color(0, 200, 0));  // 绿色
+        } else {
+            light.setForeground(Color.RED);  // 红色
+        }
+    }
+
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(() -> {
+            RmiTestDemo demo = new RmiTestDemo();
+            demo.setVisible(true);
+        });
+    }
+}

+ 95 - 0
src/com/mes/test/LaserMonitorTest.java

@@ -0,0 +1,95 @@
+package com.mes.test;
+
+import com.mes.device.LaserDevice;
+import com.mes.ui.LaserMonitorPanel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.*;
+
+/**
+ * 激光设备监控测试工具
+ * 独立运行,用于测试和调试设备信号
+ */
+public class LaserMonitorTest {
+
+    private static final Logger log = LoggerFactory.getLogger(LaserMonitorTest.class);
+
+    public static void main(String[] args) {
+        // 设置UI风格
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // 获取设备IP(可以从命令行参数或对话框获取)
+        String deviceIp = "192.168.1.100";
+
+        if(args.length > 0) {
+            deviceIp = args[0];
+        } else {
+            // 弹出输入对话框
+            deviceIp = JOptionPane.showInputDialog(
+                null,
+                "请输入激光设备IP地址:",
+                "设备连接",
+                JOptionPane.QUESTION_MESSAGE
+            );
+
+            if(deviceIp == null || deviceIp.trim().isEmpty()) {
+                JOptionPane.showMessageDialog(null, "未输入IP地址,程序退出");
+                return;
+            }
+        }
+
+        final String ip = deviceIp;
+
+        // 启动界面
+        SwingUtilities.invokeLater(() -> {
+            try {
+                log.info("正在连接设备: {}", ip);
+
+                // 创建设备实例
+                LaserDevice device = new LaserDevice();
+
+                // 连接设备
+                boolean connected = device.connect(ip);
+
+                if(!connected) {
+                    JOptionPane.showMessageDialog(
+                        null,
+                        "连接设备失败: " + ip,
+                        "连接错误",
+                        JOptionPane.ERROR_MESSAGE
+                    );
+                    return;
+                }
+
+                log.info("设备连接成功");
+
+                // 创建监控面板
+                LaserMonitorPanel monitorPanel = new LaserMonitorPanel(device);
+                monitorPanel.setVisible(true);
+
+                // 窗口关闭时断开设备
+                monitorPanel.addWindowListener(new java.awt.event.WindowAdapter() {
+                    @Override
+                    public void windowClosing(java.awt.event.WindowEvent e) {
+                        log.info("关闭监控面板,断开设备连接");
+                        device.disconnect();
+                    }
+                });
+
+            } catch (Exception e) {
+                log.error("启动监控面板异常", e);
+                JOptionPane.showMessageDialog(
+                    null,
+                    "启动失败: " + e.getMessage(),
+                    "错误",
+                    JOptionPane.ERROR_MESSAGE
+                );
+            }
+        });
+    }
+}

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

@@ -55,7 +55,7 @@ public class DataUtil {
         }
     }
 
-    public static Boolean checkQuality(NettyClient nettyClient, String sn, String user){
+    public static Boolean checkQuality(NettyClient nettyClient, String sn, String user, String side){
         try{
             String msgType = "AQDW";
             String gy = "100000";

+ 320 - 0
src/com/mes/ui/LaserMonitorPanel.java

@@ -0,0 +1,320 @@
+package com.mes.ui;
+
+import com.mes.device.LaserDevice;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * 激光设备信号监控面板
+ * 实时显示设备状态和参数
+ */
+public class LaserMonitorPanel extends JFrame {
+
+    private static final Logger log = LoggerFactory.getLogger(LaserMonitorPanel.class);
+
+    private LaserDevice device;
+
+    // 信号指示灯
+    private JLabel light3100;  // #3100 MES允许启动
+    private JLabel light3200;  // #3200 MES屏蔽
+    private JLabel lightRun;   // MO42.0 运行中
+    private JLabel lightStop;  // MO42.2 停止
+    private JLabel lightReady; // MO42.3 就绪
+
+    // 可写信号的按钮
+    private JButton btn3100;   // 切换#3100
+    private JButton btn3200;   // 切换#3200
+
+    // 数值显示
+    private JLabel valueSpeed; // #33563 进给速度
+    private JLabel valueTime;  // #33868 加工时间
+    private JLabel valueCount; // #33870 零件计数
+
+    // 连接状态
+    private JLabel connStatus;
+
+    // 定时器
+    private Timer monitorTimer;
+
+    // 当前值(用于切换)
+    private double current3100 = 0;
+    private double current3200 = 0;
+
+    public LaserMonitorPanel(LaserDevice device) {
+        this.device = device;
+        initUI();
+        startMonitor();
+    }
+
+    private void initUI() {
+        setTitle("激光设备信号监控");
+        setSize(600, 500);
+        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+        setLocationRelativeTo(null);
+
+        JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
+        mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+
+        // 顶部:连接状态
+        JPanel topPanel = createTopPanel();
+        mainPanel.add(topPanel, BorderLayout.NORTH);
+
+        // 中间:信号监控
+        JPanel centerPanel = new JPanel(new GridLayout(3, 1, 10, 10));
+        centerPanel.add(createControlSignalPanel());  // 可写信号
+        centerPanel.add(createStatusSignalPanel());   // 状态信号
+        centerPanel.add(createValuePanel());          // 数值显示
+        mainPanel.add(centerPanel, BorderLayout.CENTER);
+
+        add(mainPanel);
+    }
+
+    // 顶部面板:连接状态
+    private JPanel createTopPanel() {
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+        panel.setBorder(BorderFactory.createTitledBorder("连接状态"));
+
+        connStatus = new JLabel("●");
+        connStatus.setFont(new Font("Arial", Font.BOLD, 24));
+        connStatus.setForeground(Color.GRAY);
+        panel.add(connStatus);
+
+        JLabel label = new JLabel("设备: " + device.getDeviceIp());
+        label.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        panel.add(label);
+
+        return panel;
+    }
+
+    // 可写信号面板
+    private JPanel createControlSignalPanel() {
+        JPanel panel = new JPanel(new GridLayout(2, 1, 5, 5));
+        panel.setBorder(BorderFactory.createTitledBorder("控制信号(可写)"));
+
+        // #3100 MES允许启动
+        panel.add(createControlRow("#3100 MES允许启动",
+            light3100 = createLight(Color.GRAY),
+            btn3100 = createToggleButton("切换")));
+        btn3100.addActionListener(e -> toggle3100());
+
+        // #3200 MES屏蔽
+        panel.add(createControlRow("#3200 MES屏蔽",
+            light3200 = createLight(Color.GRAY),
+            btn3200 = createToggleButton("切换")));
+        btn3200.addActionListener(e -> toggle3200());
+
+        return panel;
+    }
+
+    // 状态信号面板
+    private JPanel createStatusSignalPanel() {
+        JPanel panel = new JPanel(new GridLayout(3, 1, 5, 5));
+        panel.setBorder(BorderFactory.createTitledBorder("状态信号(只读)"));
+
+        panel.add(createStatusRow("MO42.0 运行中", lightRun = createLight(Color.GRAY)));
+        panel.add(createStatusRow("MO42.2 停止", lightStop = createLight(Color.GRAY)));
+        panel.add(createStatusRow("MO42.3 就绪", lightReady = createLight(Color.GRAY)));
+
+        return panel;
+    }
+
+    // 数值显示面板
+    private JPanel createValuePanel() {
+        JPanel panel = new JPanel(new GridLayout(3, 1, 5, 5));
+        panel.setBorder(BorderFactory.createTitledBorder("参数显示"));
+
+        panel.add(createValueRow("#33563 进给速度", valueSpeed = createValueLabel(), "mm/min"));
+        panel.add(createValueRow("#33868 加工时间", valueTime = createValueLabel(), "秒"));
+        panel.add(createValueRow("#33870 零件计数", valueCount = createValueLabel(), "个"));
+
+        return panel;
+    }
+
+    // 创建指示灯
+    private JLabel createLight(Color color) {
+        JLabel light = new JLabel("●");
+        light.setFont(new Font("Arial", Font.BOLD, 32));
+        light.setForeground(color);
+        return light;
+    }
+
+    // 创建切换按钮
+    private JButton createToggleButton(String text) {
+        JButton btn = new JButton(text);
+        btn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
+        return btn;
+    }
+
+    // 创建数值标签
+    private JLabel createValueLabel() {
+        JLabel label = new JLabel("0.0");
+        label.setFont(new Font("Arial", Font.BOLD, 16));
+        label.setForeground(new Color(0, 100, 200));
+        return label;
+    }
+
+    // 创建控制信号行(信号名 + 指示灯 + 按钮)
+    private JPanel createControlRow(String name, JLabel light, JButton button) {
+        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
+
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        nameLabel.setPreferredSize(new Dimension(180, 25));
+
+        row.add(nameLabel);
+        row.add(light);
+        row.add(button);
+
+        return row;
+    }
+
+    // 创建状态信号行(信号名 + 指示灯)
+    private JPanel createStatusRow(String name, JLabel light) {
+        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
+
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        nameLabel.setPreferredSize(new Dimension(180, 25));
+
+        row.add(nameLabel);
+        row.add(light);
+
+        return row;
+    }
+
+    // 创建数值显示行(信号名 + 数值 + 单位)
+    private JPanel createValueRow(String name, JLabel valueLabel, String unit) {
+        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
+
+        JLabel nameLabel = new JLabel(name);
+        nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        nameLabel.setPreferredSize(new Dimension(180, 25));
+
+        JLabel unitLabel = new JLabel(unit);
+        unitLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
+        unitLabel.setForeground(Color.GRAY);
+
+        row.add(nameLabel);
+        row.add(valueLabel);
+        row.add(unitLabel);
+
+        return row;
+    }
+
+    // 切换#3100
+    private void toggle3100() {
+        try {
+            double newValue = (current3100 == 0) ? 1.0 : 0.0;
+            boolean success = device.writeMacro(3100, newValue);
+            if(success) {
+                current3100 = newValue;
+                updateLight(light3100, newValue == 1.0);
+                log.info("切换#3100 = {}", newValue);
+            } else {
+                JOptionPane.showMessageDialog(this, "写入失败", "错误", JOptionPane.ERROR_MESSAGE);
+            }
+        } catch (Exception e) {
+            log.error("切换#3100异常", e);
+        }
+    }
+
+    // 切换#3200
+    private void toggle3200() {
+        try {
+            double newValue = (current3200 == 0) ? 1.0 : 0.0;
+            boolean success = device.writeMacro(3200, newValue);
+            if(success) {
+                current3200 = newValue;
+                updateLight(light3200, newValue == 1.0);
+                log.info("切换#3200 = {}", newValue);
+            } else {
+                JOptionPane.showMessageDialog(this, "写入失败", "错误", JOptionPane.ERROR_MESSAGE);
+            }
+        } catch (Exception e) {
+            log.error("切换#3200异常", e);
+        }
+    }
+
+    // 启动监控定时器
+    private void startMonitor() {
+        monitorTimer = new Timer();
+        monitorTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                updateMonitor();
+            }
+        }, 500, 500);  // 500ms刷新一次
+    }
+
+    // 更新监控数据
+    private void updateMonitor() {
+        try {
+            // 检查连接状态
+            boolean connected = device.isConnected();
+            SwingUtilities.invokeLater(() -> {
+                updateLight(connStatus, connected);
+            });
+
+            if(!connected) {
+                return;
+            }
+
+            // 读取控制信号
+            current3100 = device.readMacro(3100);
+            current3200 = device.readMacro(3200);
+
+            // 读取状态信号
+            long running = device.readPlc("42.0", 1);
+            long stopped = device.readPlc("42.2", 1);
+            long ready = device.readPlc("42.3", 1);
+
+            // 读取数值参数
+            double speed = device.readMacro(33563);
+            double time = device.readMacro(33868);
+            double count = device.readMacro(33870);
+
+            // 更新UI
+            SwingUtilities.invokeLater(() -> {
+                updateLight(light3100, current3100 == 1.0);
+                updateLight(light3200, current3200 == 1.0);
+                updateLight(lightRun, running == 1);
+                updateLight(lightStop, stopped == 1);
+                updateLight(lightReady, ready == 1);
+
+                valueSpeed.setText(String.format("%.2f", speed));
+                valueTime.setText(String.format("%.1f", time));
+                valueCount.setText(String.format("%.0f", count));
+            });
+
+        } catch (Exception e) {
+            log.error("更新监控数据异常", e);
+        }
+    }
+
+    // 更新指示灯颜色
+    private void updateLight(JLabel light, boolean on) {
+        if(on) {
+            light.setForeground(new Color(0, 200, 0));  // 绿色
+        } else {
+            light.setForeground(Color.RED);  // 红色
+        }
+    }
+
+    // 停止监控
+    public void stopMonitor() {
+        if(monitorTimer != null) {
+            monitorTimer.cancel();
+        }
+    }
+
+    @Override
+    public void dispose() {
+        stopMonitor();
+        super.dispose();
+    }
+}

+ 323 - 105
src/com/mes/ui/MesClient.java

@@ -3,6 +3,8 @@ package com.mes.ui;
 import com.alibaba.fastjson2.JSONObject;
 import com.mes.component.MesRadio;
 import com.mes.component.MesWebView;
+import com.mes.controller.LaserController;
+import com.mes.device.LaserDevice;
 import com.mes.netty.NettyClient;
 import com.mes.netty.ProtocolParam;
 import com.mes.util.*;
@@ -125,6 +127,22 @@ public class MesClient extends JFrame {
     public static boolean timer_flag = false; // 开始计时标志 false=非工作开始计时 true=工件开始计时
     public static long timer_nums = 0; // 秒数
 
+    // 激光切割设备
+    public static LaserDevice laserDevice;
+    public static com.mes.controller.LaserControllerDual laserController;
+    public static String laser_device_ip = ""; // 激光设备IP,从配置文件读取
+
+    // 双面UI组件
+    public static JTextField product_sn2;  // B面工件码
+    public static JLabel pxstatus1;        // A面状态
+    public static JLabel pxstatus2;        // B面状态
+    public static JTextField param1;       // A面进给速度
+    public static JTextField param2;       // A面加工时间
+    public static JTextField param3;       // A面零件数
+    public static JTextField param21;      // B面进给速度
+    public static JTextField param22;      // B面加工时间
+    public static JTextField param23;      // B面零件数
+
     public static void main(String[] args) {
 
         if (LockUtil.getInstance().isAppActive() == true){
@@ -153,6 +171,12 @@ public class MesClient extends JFrame {
 
                         getMaterailData();
 
+                        // 初始化激光设备
+                        initLaserDevice();
+
+                        // 启动激光定时器
+                        startLaserTimers();
+
                     }catch (Exception e){
                         e.printStackTrace();
                     }
@@ -164,7 +188,7 @@ public class MesClient extends JFrame {
     //读配置文件
     private static void readProperty() throws IOException{
         String enconding = "UTF-8";
-        InputStream is = ClassLoader.getSystemResourceAsStream("resources/config/config.properties");
+        InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
         Properties pro = new Properties();
         BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
         pro.load(br);
@@ -174,9 +198,13 @@ public class MesClient extends JFrame {
         mes_heart_beat_cycle = Integer.parseInt(pro.getProperty("mes.heart_beat_cycle"));
         mes_line_sn = pro.getProperty("mes.line_sn");
 
+        // 读取激光设备IP
+        laser_device_ip = pro.getProperty("laser.device.ip", "192.168.1.100");
+
         mes_gw_des = OprnoUtil.getGwDes(mes_line_sn,mes_gw);
 
         log.info(mes_gw + ";" + mes_gw_des + ";" + mes_server_ip + ";" + mes_tcp_port + ";" + mes_heart_beat_cycle);
+        log.info("激光设备IP: " + laser_device_ip);
     }
 
     // 初始化TCP
@@ -280,17 +308,41 @@ public class MesClient extends JFrame {
         check_quality_result = false;
         MesClient.finish_ok_bt.setEnabled(false);
         MesClient.finish_ng_bt.setEnabled(false);
-//        product_sn.setText("000020015308-0100101425022500085");
         product_sn.setText("");
         MesClient.fxlabel.setVisible(false);
 
         MesClient.f_scan_data_bt_1.setEnabled(true);
         MesClient.status_menu.setText("请扫工件码");
 
+        // 清空参数显示
+        if(param1 != null) param1.setText("");
+        if(param2 != null) param2.setText("");
+        if(param3 != null) param3.setText("");
+        if(pxstatus1 != null) pxstatus1.setText("A");
+
+        // 复位激光控制器A面状态
+        if(laserController != null) {
+            laserController.manualResetA();
+        }
+
         updateMaterailData();
         shiftUserCheck();
+    }
+
+    public static void resetScanB() {
+        // B面复位逻辑
+        if(product_sn2 != null) product_sn2.setText("");
+        if(param21 != null) param21.setText("");
+        if(param22 != null) param22.setText("");
+        if(param23 != null) param23.setText("");
+        if(pxstatus2 != null) pxstatus2.setText("B");
+
+        // 复位激光控制器B面状态
+        if(laserController != null) {
+            laserController.manualResetB();
+        }
 
-//        MesClient.setMenuStatus("设备报警停机",-1);
+        shiftUserCheck();
     }
 
     public static int userLoginHours;//用户登录所处小时
@@ -311,9 +363,6 @@ public class MesClient extends JFrame {
             }
         }
     }
-    public static void resetScanB() {
-
-    }
 
     //获取用户20位
     public static void getUser() {
@@ -349,13 +398,7 @@ public class MesClient extends JFrame {
             JOptionPane.showMessageDialog(mesClientFrame,"工作中,勿扫码","提示窗口", JOptionPane.INFORMATION_MESSAGE);
             return;
         }
-        String scanBarcodeTitle = "";
-        switch(scan_type) {
-            case 1:
-                product_sn.setText("");
-                scanBarcodeTitle = "请扫工件码";
-                break;
-        }
+        String scanBarcodeTitle = "请扫工件码(A面或B面)";
 
         //弹窗扫工件码
         String scanBarcode = JOptionPane.showInputDialog(null, scanBarcodeTitle);
@@ -364,32 +407,53 @@ public class MesClient extends JFrame {
             getUser();
             //获取扫码内容36位
             String barcode36 = getBarcode(scanBarcode);//处理36为码
-            //工位号
-            String gw = "";
-            switch(scan_type) {
-                case 1:
-                    product_sn.setText(scanBarcode);
-                    break;
+
+            // 判断A面还是B面(简单逻辑:询问用户)
+            String[] options = {"A面", "B面"};
+            int choice = JOptionPane.showOptionDialog(
+                mesClientFrame,
+                "请选择扫码面:",
+                "选择面",
+                JOptionPane.DEFAULT_OPTION,
+                JOptionPane.QUESTION_MESSAGE,
+                null,
+                options,
+                options[0]
+            );
+
+            String side = choice == 0 ? "A" : "B";
+
+            if(side.equals("A")) {
+                product_sn.setText(scanBarcode);
+            } else {
+                product_sn2.setText(scanBarcode);
             }
+
             //刷新界面
             mesClientFrame.repaint();
 
             if(!tcp_connect_flag) {
                 MesClient.setMenuStatus("设备未连接Mes服务器",-1);
-//                JOptionPane.showMessageDialog(mesClientFrame,"设备未连接Mes服务器","提示窗口", JOptionPane.INFORMATION_MESSAGE);
                 return;
             }
 
-            // 查询工件质量
-            Boolean sendret = DataUtil.checkQuality(nettyClient,barcode36,user20);
+            // 查询工件质量(MES质检)
+            Boolean sendret = DataUtil.checkQuality(nettyClient,barcode36,user20,side);
             if(!sendret){
                 MesClient.setMenuStatus("消息发送失败,请重试",-1);
-//                JOptionPane.showMessageDialog(mesClientFrame,"消息发送失败,请重试","提示窗口", JOptionPane.INFORMATION_MESSAGE);
                 return;
             }
+
+            // 扫码处理交给控制器
+            if(laserController != null) {
+                if(side.equals("A")) {
+                    laserController.onScanCodeA(scanBarcode, user20);
+                } else {
+                    laserController.onScanCodeB(scanBarcode, user20);
+                }
+            }
         }else {
             MesClient.setMenuStatus("请扫工件码,请重试",-1);
-//            JOptionPane.showMessageDialog(mesClientFrame,"请扫工件码","提示窗口", JOptionPane.INFORMATION_MESSAGE);
             return;
         }
     }
@@ -556,109 +620,152 @@ public class MesClient extends JFrame {
         tabbedPane.setFont(new Font("宋体", Font.BOLD, 22));
         contentPane.add(tabbedPane);
 
-        //首页
-        JPanel indexPanelA = new CenteredPanel(972, 450);
+        //首页 - 双面布局(参考OP060)
+        JPanel indexPanelA = new CenteredPanel(972, 550);
         indexScrollPaneA = new JScrollPane(indexPanelA);
         indexPanelA.setLayout(null);
 
-        product_sn = new JTextField();
-        product_sn.setHorizontalAlignment(SwingConstants.CENTER);
-        product_sn.setEditable(false);
-        product_sn.setFont(new Font("微软雅黑", Font.PLAIN, 28));
-        product_sn.setBounds(81, 70, 602, 70);
-        indexPanelA.add(product_sn);
-        product_sn.setColumns(10);
-
+        // 扫码按钮(居中)
         f_scan_data_bt_1 = new JButton("扫码");
         f_scan_data_bt_1.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
-                scan_type = 1;
                 scanBarcode();
             }
         });
         f_scan_data_bt_1.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
-        f_scan_data_bt_1.setFont(new Font("微软雅黑", Font.PLAIN, 32));
-        f_scan_data_bt_1.setBounds(693, 70, 198, 70);
+        f_scan_data_bt_1.setFont(new Font("微软雅黑", Font.PLAIN, 28));
+        f_scan_data_bt_1.setBounds(400, 10, 180, 60);
         indexPanelA.add(f_scan_data_bt_1);
 
-//        String[] hjtitles = new String[]{"焊机1","焊机2","焊机3"};
-//        String[] hjvals = new String[]{"HJ001","HJ002","HJ003"};
-//        mesRadioHj = new MesRadio(hjtitles,hjvals);
-//        mesRadioHj.setSize(500,50);
-//        mesRadioHj.setBounds(190,170,500,50);
-//        indexPanelA.add(mesRadioHj);
+        // ========== A面区域 ==========
+        pxstatus1 = new JLabel("A面");
+        pxstatus1.setForeground(new Color(0, 128, 255));
+        pxstatus1.setHorizontalAlignment(SwingConstants.CENTER);
+        pxstatus1.setFont(new Font("微软雅黑", Font.BOLD, 22));
+        pxstatus1.setBounds(26, 80, 446, 35);
+        indexPanelA.add(pxstatus1);
+
+        product_sn = new JTextField();
+        product_sn.setHorizontalAlignment(SwingConstants.CENTER);
+        product_sn.setEditable(false);
+        product_sn.setFont(new Font("微软雅黑", Font.PLAIN, 20));
+        product_sn.setBounds(26, 120, 446, 60);
+        indexPanelA.add(product_sn);
 
-        fxlabel = new JLabel("该工件为返修件,请仔细检查");
-        fxlabel.setFont(new Font("微软雅黑", Font.PLAIN, 38));
-        fxlabel.setBounds(81, 170, 810, 70);
+        JLabel lblSpeed1 = new JLabel("进给速度:");
+        lblSpeed1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblSpeed1.setBounds(26, 200, 100, 30);
+        indexPanelA.add(lblSpeed1);
+
+        param1 = new JTextField();
+        param1.setEnabled(false);
+        param1.setEditable(false);
+        param1.setHorizontalAlignment(SwingConstants.CENTER);
+        param1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param1.setBounds(130, 200, 100, 30);
+        indexPanelA.add(param1);
+
+        JLabel lblTime1 = new JLabel("加工时间:");
+        lblTime1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblTime1.setBounds(26, 240, 100, 30);
+        indexPanelA.add(lblTime1);
+
+        param2 = new JTextField();
+        param2.setEnabled(false);
+        param2.setEditable(false);
+        param2.setHorizontalAlignment(SwingConstants.CENTER);
+        param2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param2.setBounds(130, 240, 100, 30);
+        indexPanelA.add(param2);
+
+        JLabel lblCount1 = new JLabel("零件数:");
+        lblCount1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblCount1.setBounds(26, 280, 100, 30);
+        indexPanelA.add(lblCount1);
+
+        param3 = new JTextField();
+        param3.setEnabled(false);
+        param3.setEditable(false);
+        param3.setHorizontalAlignment(SwingConstants.CENTER);
+        param3.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param3.setBounds(130, 280, 100, 30);
+        indexPanelA.add(param3);
+
+        // ========== B面区域 ==========
+        JSeparator separator = new JSeparator();
+        separator.setOrientation(SwingConstants.VERTICAL);
+        separator.setBounds(495, 80, 2, 240);
+        indexPanelA.add(separator);
+
+        pxstatus2 = new JLabel("B面");
+        pxstatus2.setForeground(new Color(0, 128, 255));
+        pxstatus2.setHorizontalAlignment(SwingConstants.CENTER);
+        pxstatus2.setFont(new Font("微软雅黑", Font.BOLD, 22));
+        pxstatus2.setBounds(517, 80, 446, 35);
+        indexPanelA.add(pxstatus2);
+
+        product_sn2 = new JTextField();
+        product_sn2.setHorizontalAlignment(SwingConstants.CENTER);
+        product_sn2.setEditable(false);
+        product_sn2.setFont(new Font("微软雅黑", Font.PLAIN, 20));
+        product_sn2.setBounds(517, 120, 446, 60);
+        indexPanelA.add(product_sn2);
+
+        JLabel lblSpeed2 = new JLabel("进给速度:");
+        lblSpeed2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblSpeed2.setBounds(517, 200, 100, 30);
+        indexPanelA.add(lblSpeed2);
+
+        param21 = new JTextField();
+        param21.setEnabled(false);
+        param21.setEditable(false);
+        param21.setHorizontalAlignment(SwingConstants.CENTER);
+        param21.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param21.setBounds(621, 200, 100, 30);
+        indexPanelA.add(param21);
+
+        JLabel lblTime2 = new JLabel("加工时间:");
+        lblTime2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblTime2.setBounds(517, 240, 100, 30);
+        indexPanelA.add(lblTime2);
+
+        param22 = new JTextField();
+        param22.setEnabled(false);
+        param22.setEditable(false);
+        param22.setHorizontalAlignment(SwingConstants.CENTER);
+        param22.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param22.setBounds(621, 240, 100, 30);
+        indexPanelA.add(param22);
+
+        JLabel lblCount2 = new JLabel("零件数:");
+        lblCount2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        lblCount2.setBounds(517, 280, 100, 30);
+        indexPanelA.add(lblCount2);
+
+        param23 = new JTextField();
+        param23.setEnabled(false);
+        param23.setEditable(false);
+        param23.setHorizontalAlignment(SwingConstants.CENTER);
+        param23.setFont(new Font("微软雅黑", Font.PLAIN, 16));
+        param23.setBounds(621, 280, 100, 30);
+        indexPanelA.add(param23);
+
+        // 返修标签(隐藏,需要时显示)
+        fxlabel = new JLabel("返修件,请仔细检查");
+        fxlabel.setFont(new Font("微软雅黑", Font.PLAIN, 28));
+        fxlabel.setBounds(300, 350, 400, 50);
         fxlabel.setForeground(Color.RED);
         fxlabel.setHorizontalAlignment(SwingConstants.CENTER);
         fxlabel.setVisible(false);
         indexPanelA.add(fxlabel);
 
+        // 占位按钮(隐藏)
         finish_ok_bt = new JButton("OK");
-        finish_ok_bt.setEnabled(false);
-        finish_ok_bt.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                if(work_status == 1 && check_quality_result){
-
-                    String sn = getBarcode(product_sn.getText());
-                    if(sn.isEmpty()){
-                        MesClient.setMenuStatus("工件码为空,请重试",-1);
-                        return;
-                    }
-                    getUser();
-
-                    String qret = "OK";
-                    Boolean sendret = DataUtil.sendQuality(nettyClient,sn,qret,user20);
-                    if(!sendret){
-                        MesClient.setMenuStatus("消息发送失败,请重试",-1);
-                        return;
-                    }else{
-//                        MesClient.resetScanA();
-//                        MesClient.scan_type = 1;
-//                        MesClient.scanBarcode();
-//                        MesClient.setMenuStatus("结果提交成功,请扫下一件",0);
-                    }
-                }
-            }
-        });
-        finish_ok_bt.setIcon(new ImageIcon(MesClient.class.getResource("/bg/ok_bg.png")));
-        finish_ok_bt.setFont(new Font("微软雅黑", Font.PLAIN, 32));
-        finish_ok_bt.setBounds(185, 291, 240, 80);
-        finish_ok_bt.setEnabled(false);
+        finish_ok_bt.setVisible(false);
         indexPanelA.add(finish_ok_bt);
 
         finish_ng_bt = new JButton("NG");
-        finish_ng_bt.setEnabled(false);
-        finish_ng_bt.addActionListener(new ActionListener() {
-            public void actionPerformed(ActionEvent e) {
-                if(work_status == 1 && check_quality_result){
-
-                    String sn = getBarcode(product_sn.getText());
-                    if(sn.isEmpty()){
-                        MesClient.setMenuStatus("工件码为空,请重试",-1);
-                        return;
-                    }
-                    getUser();
-                    String qret = "NG";
-                    Boolean sendret = DataUtil.sendQuality(nettyClient,sn,qret,user20);
-                    if(!sendret){
-                        MesClient.setMenuStatus("消息发送失败,请重试",-1);
-                        return;
-                    }else{
-//                        MesClient.resetScanA();
-//                        MesClient.scan_type = 1;
-//                        MesClient.scanBarcode();
-//                        MesClient.setMenuStatus("结果提交成功,请扫下一件",0);
-                    }
-                }
-            }
-        });
-        finish_ng_bt.setIcon(new ImageIcon(MesClient.class.getResource("/bg/ng_bg.png")));
-        finish_ng_bt.setFont(new Font("微软雅黑", Font.PLAIN, 32));
-        finish_ng_bt.setBounds(508, 291, 240, 80);
-        finish_ng_bt.setEnabled(false);
+        finish_ng_bt.setVisible(false);
         indexPanelA.add(finish_ng_bt);
 
         tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
@@ -938,4 +1045,115 @@ public class MesClient extends JFrame {
         });
     }
 
+    /**
+     * 初始化激光设备
+     */
+    public static void initLaserDevice() {
+        try {
+            log.info("开始初始化激光设备...");
+
+            // 创建设备实例
+            laserDevice = new LaserDevice();
+
+            // 连接设备
+            boolean connected = laserDevice.connect(laser_device_ip);
+
+            if(connected) {
+                log.info("激光设备连接成功: {}", laser_device_ip);
+
+                // 创建双面控制器
+                laserController = new com.mes.controller.LaserControllerDual(laserDevice);
+
+                log.info("激光设备初始化完成");
+            } else {
+                log.error("激光设备连接失败: {}", laser_device_ip);
+            }
+
+        } catch (Exception e) {
+            log.error("初始化激光设备异常", e);
+        }
+    }
+
+    /**
+     * 启动激光设备定时器
+     */
+    public static void startLaserTimers() {
+        try {
+            log.info("启动激光设备定时器...");
+
+            // 1. 状态检测定时器(1秒)
+            Timer laserStatusTimer = new Timer();
+            laserStatusTimer.schedule(new TimerTask() {
+                public void run() {
+                    try {
+                        if(laserController != null) {
+                            laserController.checkStatus();
+                        }
+                    } catch (Exception e) {
+                        log.error("激光状态检测异常", e);
+                    }
+                }
+            }, 1000, 1000);
+
+            // 2. 参数采集定时器(1秒)
+            Timer laserParamTimer = new Timer();
+            laserParamTimer.schedule(new TimerTask() {
+                public void run() {
+                    try {
+                        if(laserController != null) {
+                            laserController.collectParams();
+                        }
+                    } catch (Exception e) {
+                        log.error("激光参数采集异常", e);
+                    }
+                }
+            }, 1000, 1000);
+
+            // 3. 参数上传定时器(30秒)
+            Timer laserUploadTimer = new Timer();
+            laserUploadTimer.schedule(new TimerTask() {
+                public void run() {
+                    try {
+                        uploadLaserParams();
+                    } catch (Exception e) {
+                        log.error("激光参数上传异常", e);
+                    }
+                }
+            }, 10000, 30000);
+
+            log.info("激光设备定时器启动完成");
+
+        } catch (Exception e) {
+            log.error("启动激光定时器异常", e);
+        }
+    }
+
+    /**
+     * 上传激光参数到MES
+     */
+    public static void uploadLaserParams() {
+        try {
+            List<LaserParamReq> params = JdbcUtils.getLaserParams();
+
+            if(params.size() > 0) {
+                log.info("开始上传激光参数,共{}条", params.size());
+
+                // TODO: 调用MES接口上传参数
+                // 参考OP60的upParams方法
+                // String url = "http://" + mes_server_ip + ":8980/js/a/mes/xxx";
+                // HttpUtils.sendPostRequestJson(url, JSON.toJSONString(params));
+
+                // 上传成功后标记为已同步
+                for(LaserParamReq param : params) {
+                    JdbcUtils.updateLaserSync(param.getId(), 1);
+                }
+
+                log.info("激光参数上传完成");
+            }
+
+        } catch (Exception e) {
+            log.error("上传激光参数异常", e);
+        }
+    }
+
 }

+ 42 - 24
src/com/mes/ui/MesRevice.java

@@ -14,32 +14,42 @@ public class MesRevice {
         try{
             if(processMsgRet.equalsIgnoreCase("UD")) {
                 String sn = ProtocolParam.getSn(mes_msg).trim();
-                MesClient.status_menu.setForeground(Color.GREEN);
-                MesClient.check_quality_result = true;//质量合格,可以绑定加工
-                MesClient.status_menu.setText("该工件可以加工");
-                MesClient.work_status = 1;
-                MesClient.f_scan_data_bt_1.setEnabled(false);
-                MesClient.finish_ok_bt.setEnabled(true);
-                MesClient.finish_ng_bt.setEnabled(true);
+                String side = ProtocolParam.getOprno(mes_msg).trim(); // 暂时从oprno获取面信息,实际需要从消息中解析
 
-                String oprno = ProtocolParam.getOprno(mes_msg).trim();
-                if(oprno.equals("OP400")){
-                    String lx = ProtocolParam.getLx(mes_msg);
-                    if(lx.equals("FX")){
-                        MesClient.fxlabel.setVisible(true);
+                // 判断是A面还是B面(简单逻辑:根据当前扫码的工件码判断)
+                boolean isA = sn.equals(MesClient.product_sn.getText().trim());
+                boolean isB = sn.equals(MesClient.product_sn2.getText().trim());
+
+                if(isA) {
+                    MesClient.status_menu.setForeground(Color.GREEN);
+                    MesClient.status_menu.setText("A面工件可以加工");
+                    MesClient.pxstatus1.setText("A面质检通过");
+
+                    // 通知控制器质检通过
+                    if(MesClient.laserController != null) {
+                        MesClient.laserController.onMesQualityPassA();
+                    }
+                } else if(isB) {
+                    MesClient.status_menu.setForeground(Color.GREEN);
+                    MesClient.status_menu.setText("B面工件可以加工");
+                    MesClient.pxstatus2.setText("B面质检通过");
+
+                    // 通知控制器质检通过
+                    if(MesClient.laserController != null) {
+                        MesClient.laserController.onMesQualityPassB();
                     }
                 }
 
-                // 自动提交OK结果
-                String barcode36 = MesClient.getBarcode(sn);
-                if(!barcode36.isEmpty()){
-                    MesClient.getUser();
-                    String qret = "OK";
-                    Boolean sendret = DataUtil.sendQuality(MesClient.nettyClient,barcode36,qret,MesClient.user20);
-                    if(!sendret){
-                        MesClient.setMenuStatus("消息发送失败,请重试",-1);
+                String oprno = ProtocolParam.getOprno(mes_msg).trim();
+                String lx = ProtocolParam.getLx(mes_msg);
+                if(lx.equals("FX")){
+                    if(isA) {
+                        MesClient.pxstatus1.setText("A面返修件,请仔细检查");
+                    } else if(isB) {
+                        MesClient.pxstatus2.setText("B面返修件,请仔细检查");
                     }
                 }
+
             }else {
                 MesClient.check_quality_result = false;
                 String lmsg = ErrorMsg.getErrorMsg(processMsgRet, ProtocolParam.getLx(mes_msg));
@@ -91,11 +101,19 @@ public class MesRevice {
     public static void updateResultRevice(String processMsgRet,String mes_msg){
         try{
             if(processMsgRet.equalsIgnoreCase("OK")) {
+                String sn = ProtocolParam.getSn(mes_msg).trim();
 
-                MesClient.resetScanA();
-                MesClient.setMenuStatus("结果提交成功,请扫下一件",0);
-                MesClient.scan_type = 1;
-                MesClient.scanBarcode();
+                // 判断是A面还是B面
+                boolean isA = sn.equals(MesClient.product_sn.getText().trim());
+                boolean isB = sn.equals(MesClient.product_sn2.getText().trim());
+
+                if(isA) {
+                    MesClient.resetScanA();
+                    MesClient.setMenuStatus("A面结果提交成功",0);
+                } else if(isB) {
+                    MesClient.resetScanB();
+                    MesClient.setMenuStatus("B面结果提交成功",0);
+                }
 
             }else{
                 MesClient.setMenuStatus("结果提交失败,请重试",-1);

+ 100 - 3
src/com/mes/util/JdbcUtils.java

@@ -77,6 +77,18 @@ public class JdbcUtils {
 				")";
 		statement.executeUpdate(submitRecord);
 
+		// 创建 激光切割参数表
+		String laserParams = "CREATE TABLE if not exists laser_params(\n" +
+				"   id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自增ID\n" +
+				"   oprno VARCHAR(20),                    -- 工位号 \n" +
+				"   line_sn VARCHAR(20),                  -- 产线号 \n" +
+				"   sn VARCHAR(48),                       -- 工件码\n" +
+				"   params TEXT,                          -- 参数JSON数组 \n" +
+				"   create_time DATETIME,                 -- 创建时间\n" +
+				"   sync_status INTEGER DEFAULT 0         -- 同步状态(0未同步 1已同步)\n" +
+				")";
+		statement.executeUpdate(laserParams);
+
         statement.close();
     }
     
@@ -127,7 +139,92 @@ public class JdbcUtils {
 		return ret;
 	}
 
-    
+	/**
+	 * 插入激光切割参数数据
+	 * @param oprno 工位号
+	 * @param lineSn 产线号
+	 * @param sn 工件码
+	 * @param params 参数JSON字符串
+	 * @return 是否成功
+	 */
+	public static boolean insertLaserData(String oprno, String lineSn, String sn, String params) {
+		boolean ret = false;
+		String createTime = DateLocalUtils.getCurrentTime();
+		try {
+			if (JdbcUtils.conn == null || JdbcUtils.conn.isClosed()) {
+				JdbcUtils.openConnection();
+			}
+			Statement statement = conn.createStatement();
+			String insertSQL = "INSERT INTO laser_params (oprno, line_sn, sn, params, create_time, sync_status)" +
+					"VALUES('" + oprno + "', '" + lineSn + "', '" + sn + "', '" + params + "', '" + createTime + "', 0)";
+			statement.executeUpdate(insertSQL);
+			statement.close();
+			ret = true;
+			log.info("向laser_params表插入数据成功: oprno={}, sn={}", oprno, sn);
+		} catch (SQLException e) {
+			ret = false;
+			log.error("向laser_params表插入数据失败", e);
+		}
+		return ret;
+	}
+
+	/**
+	 * 查询未同步的激光切割参数
+	 * @return 参数列表
+	 */
+	public static java.util.List<LaserParamReq> getLaserParams() {
+		java.util.List<LaserParamReq> list = new java.util.ArrayList<>();
+		try {
+			if (JdbcUtils.conn == null || JdbcUtils.conn.isClosed()) {
+				JdbcUtils.openConnection();
+			}
+			Statement statement = conn.createStatement();
+			String querySQL = "SELECT id, oprno, line_sn, sn, params FROM laser_params WHERE sync_status = 0 ORDER BY id LIMIT 100";
+			java.sql.ResultSet rs = statement.executeQuery(querySQL);
+
+			while (rs.next()) {
+				LaserParamReq req = new LaserParamReq();
+				req.setId(rs.getInt("id"));
+				req.setOprno(rs.getString("oprno"));
+				req.setLineSn(rs.getString("line_sn"));
+				req.setSn(rs.getString("sn"));
+				req.setParams(rs.getString("params"));
+				list.add(req);
+			}
+
+			rs.close();
+			statement.close();
+		} catch (SQLException e) {
+			log.error("查询laser_params失败", e);
+		}
+		return list;
+	}
+
+	/**
+	 * 更新激光切割参数同步状态
+	 * @param id 记录ID
+	 * @param status 同步状态 0=未同步 1=已同步
+	 * @return 是否成功
+	 */
+	public static boolean updateLaserSync(int id, int status) {
+		boolean ret = false;
+		try {
+			if (JdbcUtils.conn == null || JdbcUtils.conn.isClosed()) {
+				JdbcUtils.openConnection();
+			}
+			Statement statement = conn.createStatement();
+			String updateSQL = "UPDATE laser_params SET sync_status = " + status + " WHERE id = " + id;
+			statement.executeUpdate(updateSQL);
+			statement.close();
+			ret = true;
+			log.debug("更新laser_params同步状态成功: id={}, status={}", id, status);
+		} catch (SQLException e) {
+			ret = false;
+			log.error("更新laser_params同步状态失败", e);
+		}
+		return ret;
+	}
+
     public static void close(){
         try {
         	if(conn!=null) {
@@ -137,8 +234,8 @@ public class JdbcUtils {
             e.printStackTrace();
         }
     }
-    
-    
+
+
 
 }
  

+ 54 - 0
src/com/mes/util/LaserParamReq.java

@@ -0,0 +1,54 @@
+package com.mes.util;
+
+/**
+ * 激光切割参数请求对象
+ * 对应laser_params表
+ */
+public class LaserParamReq {
+
+    private int id;
+    private String oprno;
+    private String lineSn;
+    private String sn;
+    private String params;  // JSON格式的参数数组
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getOprno() {
+        return oprno;
+    }
+
+    public void setOprno(String oprno) {
+        this.oprno = oprno;
+    }
+
+    public String getLineSn() {
+        return lineSn;
+    }
+
+    public void setLineSn(String lineSn) {
+        this.lineSn = lineSn;
+    }
+
+    public String getSn() {
+        return sn;
+    }
+
+    public void setSn(String sn) {
+        this.sn = sn;
+    }
+
+    public String getParams() {
+        return params;
+    }
+
+    public void setParams(String params) {
+        this.params = params;
+    }
+}

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

@@ -4,4 +4,7 @@ mes.server_ip=192.168.114.99
 mes.tcp_port=3000
 
 mes.heart_beat_cycle=60
-mes.line_sn=XT
+mes.line_sn=XT
+
+# 激光切割设备配置
+laser.device.ip=192.168.1.100