wangxichen 2 tygodni temu
rodzic
commit
70d4324d0e

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+.idea
+.settings
+classes
+bin
+out
+*.db
+*.iml

+ 1 - 0
.idea/vcs.xml

@@ -2,5 +2,6 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/mesclient-op60-cmt" vcs="Git" />
   </component>
 </project>

BIN
RemoteComm_x64.dll


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

@@ -1,341 +0,0 @@
-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;
-    }
-}

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

@@ -295,4 +295,25 @@ public class LaserDevice {
     public double readSpeed() {
         return readMacro(33563);
     }
+
+    /**
+     * 读取转台位置 #1581
+     * @return 0=转台未到位, 1=A加工/B上料, 2=B加工/A上料
+     */
+    public int readTurntable() {
+        return (int) readMacro(1581);
+    }
+
+    /**
+     * 读取当前处于「上料位」的面(操作工装料+扫码的那一面)
+     * #1581=1 → A加工/B上料 → 返回 B
+     * #1581=2 → B加工/A上料 → 返回 A
+     * #1581=0 → 转台未到位 → 返回空串
+     */
+    public String readSideAtLoad() {
+        int v = readTurntable();
+        if (v == 1) return "B";
+        if (v == 2) return "A";
+        return "";
+    }
 }

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

@@ -1,643 +0,0 @@
-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);
-        });
-    }
-}

+ 11 - 68
src/com/mes/test/LaserMonitorTest.java

@@ -9,87 +9,30 @@ 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();
+            log.warn("设置外观失败", e);
         }
 
-        // 获取设备IP(可以从命令行参数或对话框获取)
-        String deviceIp = "192.168.1.100";
+        // 默认 IP,命令行参数可覆盖
+        final String defaultIp = (args.length > 0 && !args[0].trim().isEmpty())
+                ? args[0].trim()
+                : "192.168.2.199";
 
-        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
-                );
-            }
+            LaserDevice device = new LaserDevice();
+            LaserMonitorPanel panel = new LaserMonitorPanel(device, defaultIp);
+            panel.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 独立跑要真退出
+            panel.setVisible(true);
         });
     }
 }

+ 196 - 122
src/com/mes/ui/LaserMonitorPanel.java

@@ -11,7 +11,7 @@ import java.util.TimerTask;
 
 /**
  * 激光设备信号监控面板
- * 实时显示设备状态和参数
+ * 实时显示设备状态和参数(按 op090 宏变量点位表 #1590~#1598)
  */
 public class LaserMonitorPanel extends JFrame {
 
@@ -19,118 +19,194 @@ public class LaserMonitorPanel extends JFrame {
 
     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 light1590;   // #1590 MES允许启动
+    private JButton btn1590;
+
+    // 加工状态(只读)
+    private JLabel lightWorkA;  // #1591 A面加工中
+    private JLabel lightWorkB;  // #1592 B面加工中
+    private JLabel lightFinA;   // #1593 A面加工完成
+    private JLabel lightFinB;   // #1594 B面加工完成
+
+    // 加工参数(只读数值)
+    private JLabel valueTimeA;  // #1595 A面加工时间
+    private JLabel valueTimeB;  // #1596 B面加工时间
+    private JLabel valueCntA;   // #1597 A面零件数
+    private JLabel valueCntB;   // #1598 B面零件数
+
+    // 转台位置
+    private JLabel valueTurntable; // #1581 转台位置
+
+    // 连接控制
+    private JTextField ipField;
+    private JButton connectBtn;
+    private JButton disconnectBtn;
     private JLabel connStatus;
 
+    // 默认 IP
+    private String defaultIp;
+
     // 定时器
     private Timer monitorTimer;
 
-    // 当前值(用于切换)
-    private double current3100 = 0;
-    private double current3200 = 0;
+    // #1590 当前值(用于切换)
+    private double current1590 = 0;
 
     public LaserMonitorPanel(LaserDevice device) {
+        this(device, "192.168.2.199");
+    }
+
+    public LaserMonitorPanel(LaserDevice device, String defaultIp) {
         this.device = device;
+        this.defaultIp = defaultIp == null ? "" : defaultIp;
         initUI();
         startMonitor();
     }
 
     private void initUI() {
         setTitle("激光设备信号监控");
-        setSize(600, 500);
+        setSize(640, 620);
         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 topWrap = new JPanel(new GridLayout(2, 1, 5, 5));
+        topWrap.add(createTopPanel());
+        topWrap.add(createTurntablePanel());
+        mainPanel.add(topWrap, BorderLayout.NORTH);
 
-        // 中间:信号监控
+        // 中间:三块分组
         JPanel centerPanel = new JPanel(new GridLayout(3, 1, 10, 10));
-        centerPanel.add(createControlSignalPanel());  // 可写信号
-        centerPanel.add(createStatusSignalPanel());   // 状态信号
-        centerPanel.add(createValuePanel());          // 数值显示
+        centerPanel.add(createControlSignalPanel());  // #1590
+        centerPanel.add(createStatusSignalPanel());   // #1591~#1594
+        centerPanel.add(createValuePanel());          // #1595~#1598
         mainPanel.add(centerPanel, BorderLayout.CENTER);
 
         add(mainPanel);
     }
 
-    // 顶部面板:连接状态
+    // 转台位置面板 #1581
+    private JPanel createTurntablePanel() {
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
+        panel.setBorder(BorderFactory.createTitledBorder("转台位置 #1581"));
+
+        valueTurntable = new JLabel("--");
+        valueTurntable.setFont(new Font("微软雅黑", Font.BOLD, 16));
+        valueTurntable.setForeground(new Color(0, 100, 200));
+        panel.add(valueTurntable);
+
+        return panel;
+    }
+
+    // 顶部面板:连接控制(IP + 连接/断开 + 状态灯)
     private JPanel createTopPanel() {
-        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
-        panel.setBorder(BorderFactory.createTitledBorder("连接状态"));
+        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 5));
+        panel.setBorder(BorderFactory.createTitledBorder("连接控制"));
+
+        JLabel ipLabel = new JLabel("设备IP:");
+        ipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        panel.add(ipLabel);
+
+        ipField = new JTextField(defaultIp, 15);
+        ipField.setFont(new Font("Consolas", Font.PLAIN, 14));
+        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);
 
         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 void doConnect() {
+        String ip = ipField.getText().trim();
+        if (ip.isEmpty()) {
+            JOptionPane.showMessageDialog(this, "请输入设备IP", "提示", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+        connectBtn.setEnabled(false);
+        // 后台线程连接,避免卡 UI
+        new Thread(() -> {
+            boolean ok = device.connect(ip);
+            SwingUtilities.invokeLater(() -> {
+                if (ok) {
+                    connectBtn.setEnabled(false);
+                    disconnectBtn.setEnabled(true);
+                    ipField.setEnabled(false);
+                    log.info("设备连接成功: {}", ip);
+                } else {
+                    connectBtn.setEnabled(true);
+                    disconnectBtn.setEnabled(false);
+                    JOptionPane.showMessageDialog(this,
+                            "连接设备失败: " + ip, "错误", JOptionPane.ERROR_MESSAGE);
+                }
+            });
+        }, "LaserMonitor-Connect").start();
+    }
+
+    // 断开设备
+    private void doDisconnect() {
+        try {
+            device.disconnect();
+        } catch (Exception e) {
+            log.error("断开设备异常", e);
+        }
+        connectBtn.setEnabled(true);
+        disconnectBtn.setEnabled(false);
+        ipField.setEnabled(true);
+        log.info("设备已断开");
+    }
+
+    // 控制信号面板
     private JPanel createControlSignalPanel() {
-        JPanel panel = new JPanel(new GridLayout(2, 1, 5, 5));
+        JPanel panel = new JPanel(new GridLayout(1, 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());
+        panel.add(createControlRow("#1590 MES允许启动",
+                light1590 = createLight(Color.GRAY),
+                btn1590 = createToggleButton("切换")));
+        btn1590.addActionListener(e -> toggle1590());
 
         return panel;
     }
 
-    // 状态信号面板
+    // 加工状态面板
     private JPanel createStatusSignalPanel() {
-        JPanel panel = new JPanel(new GridLayout(3, 1, 5, 5));
-        panel.setBorder(BorderFactory.createTitledBorder("状态信号(只读)"));
+        JPanel panel = new JPanel(new GridLayout(2, 2, 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)));
+        panel.add(createStatusRow("#1591 A面加工中", lightWorkA = createLight(Color.GRAY)));
+        panel.add(createStatusRow("#1592 B面加工中", lightWorkB = createLight(Color.GRAY)));
+        panel.add(createStatusRow("#1593 A面加工完成", lightFinA = createLight(Color.GRAY)));
+        panel.add(createStatusRow("#1594 B面加工完成", lightFinB = createLight(Color.GRAY)));
 
         return panel;
     }
 
     // 数值显示面板
     private JPanel createValuePanel() {
-        JPanel panel = new JPanel(new GridLayout(3, 1, 5, 5));
-        panel.setBorder(BorderFactory.createTitledBorder("参数显示"));
+        JPanel panel = new JPanel(new GridLayout(2, 2, 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(), "个"));
+        panel.add(createValueRow("#1595 A面加工时间", valueTimeA = createValueLabel(), "秒"));
+        panel.add(createValueRow("#1596 B面加工时间", valueTimeB = createValueLabel(), "秒"));
+        panel.add(createValueRow("#1597 A面零件数", valueCntA = createValueLabel(), "个"));
+        panel.add(createValueRow("#1598 B面零件数", valueCntB = createValueLabel(), "个"));
 
         return panel;
     }
@@ -138,7 +214,7 @@ public class LaserMonitorPanel extends JFrame {
     // 创建指示灯
     private JLabel createLight(Color color) {
         JLabel light = new JLabel("●");
-        light.setFont(new Font("Arial", Font.BOLD, 32));
+        light.setFont(new Font("Arial", Font.BOLD, 28));
         light.setForeground(color);
         return light;
     }
@@ -158,13 +234,13 @@ public class LaserMonitorPanel extends JFrame {
         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));
+        nameLabel.setPreferredSize(new Dimension(200, 25));
 
         row.add(nameLabel);
         row.add(light);
@@ -173,13 +249,13 @@ public class LaserMonitorPanel extends JFrame {
         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));
+        nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        nameLabel.setPreferredSize(new Dimension(160, 25));
 
         row.add(nameLabel);
         row.add(light);
@@ -187,13 +263,13 @@ public class LaserMonitorPanel extends JFrame {
         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));
+        nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        nameLabel.setPreferredSize(new Dimension(160, 25));
 
         JLabel unitLabel = new JLabel(unit);
         unitLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
@@ -206,37 +282,20 @@ public class LaserMonitorPanel extends JFrame {
         return row;
     }
 
-    // 切换#3100
-    private void toggle3100() {
+    // 切换 #1590
+    private void toggle1590() {
         try {
-            double newValue = (current3100 == 0) ? 1.0 : 0.0;
-            boolean success = device.writeMacro(3100, newValue);
+            int newValue = (current1590 == 0) ? 1 : 0;
+            boolean success = device.writeMesEnable(newValue);
             if(success) {
-                current3100 = newValue;
-                updateLight(light3100, newValue == 1.0);
-                log.info("切换#3100 = {}", newValue);
+                current1590 = newValue;
+                updateLight(light1590, newValue == 1);
+                log.info("切换#1590 = {}", 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);
+            log.error("切换#1590异常", e);
         }
     }
 
@@ -254,41 +313,41 @@ public class LaserMonitorPanel extends JFrame {
     // 更新监控数据
     private void updateMonitor() {
         try {
-            // 检查连接状态
             boolean connected = device.isConnected();
-            SwingUtilities.invokeLater(() -> {
-                updateLight(connStatus, connected);
-            });
+            SwingUtilities.invokeLater(() -> updateLight(connStatus, connected));
+            if(!connected) return;
 
-            if(!connected) {
-                return;
-            }
+            // 转台位置
+            int turntable = device.readTurntable();
 
-            // 读取控制信号
-            current3100 = device.readMacro(3100);
-            current3200 = device.readMacro(3200);
+            // 控制信号
+            current1590 = device.readMacro(1590);
 
-            // 读取状态信号
-            long running = device.readPlc("42.0", 1);
-            long stopped = device.readPlc("42.2", 1);
-            long ready = device.readPlc("42.3", 1);
+            // 加工状态
+            int workA = device.readWorking("A");
+            int workB = device.readWorking("B");
+            int finA = device.readFinish("A");
+            int finB = device.readFinish("B");
 
-            // 读取数值参数
-            double speed = device.readMacro(33563);
-            double time = device.readMacro(33868);
-            double count = device.readMacro(33870);
+            // 加工参数
+            double timeA = device.readTime("A");
+            double timeB = device.readTime("B");
+            int cntA = device.readCount("A");
+            int cntB = device.readCount("B");
 
-            // 更新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));
+                valueTurntable.setText(turntableDesc(turntable));
+
+                updateLight(light1590, current1590 == 1.0);
+                updateLight(lightWorkA, workA == 1);
+                updateLight(lightWorkB, workB == 1);
+                updateLight(lightFinA, finA == 1);
+                updateLight(lightFinB, finB == 1);
+
+                valueTimeA.setText(String.format("%.1f", timeA));
+                valueTimeB.setText(String.format("%.1f", timeB));
+                valueCntA.setText(String.valueOf(cntA));
+                valueCntB.setText(String.valueOf(cntB));
             });
 
         } catch (Exception e) {
@@ -305,6 +364,14 @@ public class LaserMonitorPanel extends JFrame {
         }
     }
 
+    // 转台位置数值转描述
+    private static String turntableDesc(int v) {
+        if (v == 1) return "1  A面加工位 / B面上料位";
+        if (v == 2) return "2  B面加工位 / A面上料位";
+        if (v == 0) return "0  转台未到位";
+        return v + "  (未知)";
+    }
+
     // 停止监控
     public void stopMonitor() {
         if(monitorTimer != null) {
@@ -315,6 +382,13 @@ public class LaserMonitorPanel extends JFrame {
     @Override
     public void dispose() {
         stopMonitor();
+        try {
+            if (device != null && device.isConnected()) {
+                device.disconnect();
+            }
+        } catch (Exception e) {
+            log.error("关闭时断开设备异常", e);
+        }
         super.dispose();
     }
 }

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

@@ -3,7 +3,6 @@ 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;
@@ -143,6 +142,11 @@ public class MesClient extends JFrame {
     public static JTextField param22;      // B面加工时间
     public static JTextField param23;      // B面零件数
 
+    // 顶部扫码区(参考 OP60)
+    public static JTextField scanTextField;        // 扫码输入框
+    public static JLabel scanCurSideLabel;         // 当前上料面标签
+    public static String lastSideAtLoad = "";      // 上次检测到的上料面
+
     public static void main(String[] args) {
 
         if (LockUtil.getInstance().isAppActive() == true){
@@ -188,7 +192,7 @@ public class MesClient extends JFrame {
     //读配置文件
     private static void readProperty() throws IOException{
         String enconding = "UTF-8";
-        InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
+        InputStream is = ClassLoader.getSystemResourceAsStream("resources/config/config.properties");
         Properties pro = new Properties();
         BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
         pro.load(br);
@@ -393,69 +397,94 @@ public class MesClient extends JFrame {
         return barcodeRet;
     }
 
+    /**
+     * 更新顶部「当前上料面」标签
+     * side="A" → 当前:A面(上料位)
+     * side="B" → 当前:B面(上料位)
+     * side=""  → 转台未到位
+     */
+    public static void updateCurSideLabel(String side) {
+        if (scanCurSideLabel == null) return;
+        if ("A".equals(side)) {
+            scanCurSideLabel.setText("当前:A面");
+            scanCurSideLabel.setForeground(new Color(0, 128, 64));
+        } else if ("B".equals(side)) {
+            scanCurSideLabel.setText("当前:B面");
+            scanCurSideLabel.setForeground(new Color(0, 128, 64));
+        } else {
+            scanCurSideLabel.setText("当前:转台未到位");
+            scanCurSideLabel.setForeground(new Color(200, 100, 0));
+        }
+    }
+
+    /**
+     * 扫码处理(参考 OP60,走顶部 scanTextField)
+     * A/B 面由 #1581 转台位置自动判断(=1 → 上料面 B,=2 → 上料面 A,=0 → 未到位)
+     */
     public static void scanBarcode() {
-        if(work_status == 1){
-            JOptionPane.showMessageDialog(mesClientFrame,"工作中,勿扫码","提示窗口", JOptionPane.INFORMATION_MESSAGE);
-            return;
+        // 从扫码框取值并清空
+        String sn = scanTextField != null ? scanTextField.getText().trim() : "";
+        if (scanTextField != null) {
+            scanTextField.setText("");
         }
-        String scanBarcodeTitle = "请扫工件码(A面或B面)";
+        mesClientFrame.repaint();
 
-        //弹窗扫工件码
-        String scanBarcode = JOptionPane.showInputDialog(null, scanBarcodeTitle);
-        if(scanBarcode!=null&&!scanBarcode.equalsIgnoreCase("")) {
-            //获取用户
-            getUser();
-            //获取扫码内容36位
-            String barcode36 = getBarcode(scanBarcode);//处理36为码
-
-            // 判断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);
-            }
+        if (sn.isEmpty()) {
+            MesClient.setMenuStatus("工件码不能为空", -1);
+            return;
+        }
 
-            //刷新界面
-            mesClientFrame.repaint();
+        // 判 A/B 面:读 #1581
+        String side = (laserDevice != null) ? laserDevice.readSideAtLoad() : "";
+        if (side.isEmpty()) {
+            MesClient.setMenuStatus("转台未到位,暂不能扫码", -1);
+            return;
+        }
 
-            if(!tcp_connect_flag) {
-                MesClient.setMenuStatus("设备未连接Mes服务器",-1);
+        // 已合格工件未处理完 → 拒收
+        if (side.equals("A")) {
+            if (!product_sn.getText().isEmpty()
+                    && com.mes.controller.LaserControllerDual.mesQualityFlagA) {
+                MesClient.setMenuStatus("A面已有合格工件码,勿重复扫码", -1);
                 return;
             }
-
-            // 查询工件质量(MES质检)
-            Boolean sendret = DataUtil.checkQuality(nettyClient,barcode36,user20,side);
-            if(!sendret){
-                MesClient.setMenuStatus("消息发送失败,请重试",-1);
+            product_sn.setText(sn);
+        } else {
+            if (!product_sn2.getText().isEmpty()
+                    && com.mes.controller.LaserControllerDual.mesQualityFlagB) {
+                MesClient.setMenuStatus("B面已有合格工件码,勿重复扫码", -1);
                 return;
             }
+            product_sn2.setText(sn);
+        }
 
-            // 扫码处理交给控制器
-            if(laserController != null) {
-                if(side.equals("A")) {
-                    laserController.onScanCodeA(scanBarcode, user20);
-                } else {
-                    laserController.onScanCodeB(scanBarcode, user20);
-                }
-            }
-        }else {
-            MesClient.setMenuStatus("请扫工件码,请重试",-1);
+        MesClient.setMenuStatus("扫码成功", 0);
+        mesClientFrame.repaint();
+
+        if (!tcp_connect_flag) {
+            MesClient.setMenuStatus("设备未连接Mes服务器", -1);
             return;
         }
+
+        // 取用户
+        getUser();
+        String barcode36 = getBarcode(sn);
+
+        // 查询工件质量(MES 质检)
+        Boolean sendret = DataUtil.checkQuality(nettyClient, barcode36, user20, side);
+        if (!sendret) {
+            MesClient.setMenuStatus("消息发送失败,请重试", -1);
+            return;
+        }
+
+        // 扫码处理交给控制器(推进状态机 tjFlag → 1)
+        if (laserController != null) {
+            if (side.equals("A")) {
+                laserController.onScanCodeA(sn, user20);
+            } else {
+                laserController.onScanCodeB(sn, user20);
+            }
+        }
     }
 
     public static void logoff() {
@@ -621,153 +650,210 @@ public class MesClient extends JFrame {
         contentPane.add(tabbedPane);
 
         //首页 - 双面布局(参考OP060)
-        JPanel indexPanelA = new CenteredPanel(972, 550);
+        JPanel indexPanelA = new JPanel();
         indexScrollPaneA = new JScrollPane(indexPanelA);
         indexPanelA.setLayout(null);
 
-        // 扫码按钮(居中)
-        f_scan_data_bt_1 = new JButton("扫码");
-        f_scan_data_bt_1.addActionListener(new ActionListener() {
+        // ========== 顶部扫码区(参考 OP60)==========
+        scanCurSideLabel = new JLabel("当前:转台未到位");
+        scanCurSideLabel.setForeground(new Color(128, 128, 128));
+        scanCurSideLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        scanCurSideLabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 20));
+        scanCurSideLabel.setBounds(26, 15, 240, 50);
+        indexPanelA.add(scanCurSideLabel);
+
+        scanTextField = new JTextField("");
+        scanTextField.setHorizontalAlignment(SwingConstants.LEFT);
+        scanTextField.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 22));
+        scanTextField.setBounds(280, 15, 683, 50);
+        scanTextField.setColumns(10);
+        scanTextField.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 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, 28));
-        f_scan_data_bt_1.setBounds(400, 10, 180, 60);
-        indexPanelA.add(f_scan_data_bt_1);
+        indexPanelA.add(scanTextField);
 
         // ========== A面区域 ==========
-        pxstatus1 = new JLabel("A面");
-        pxstatus1.setForeground(new Color(0, 128, 255));
+        pxstatus1 = new JLabel("A");
+        pxstatus1.setForeground(new Color(255, 128, 64));
         pxstatus1.setHorizontalAlignment(SwingConstants.CENTER);
-        pxstatus1.setFont(new Font("微软雅黑", Font.BOLD, 22));
-        pxstatus1.setBounds(26, 80, 446, 35);
+        pxstatus1.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 20));
+        pxstatus1.setBounds(26, 90, 446, 44);
         indexPanelA.add(pxstatus1);
 
         product_sn = new JTextField();
+        product_sn.setText("");
         product_sn.setHorizontalAlignment(SwingConstants.CENTER);
         product_sn.setEditable(false);
-        product_sn.setFont(new Font("微软雅黑", Font.PLAIN, 20));
-        product_sn.setBounds(26, 120, 446, 60);
+        product_sn.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 22));
+        product_sn.setBounds(26, 144, 446, 70);
+        product_sn.setColumns(10);
         indexPanelA.add(product_sn);
 
-        JLabel lblSpeed1 = new JLabel("进给速度:");
-        lblSpeed1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblSpeed1.setBounds(26, 200, 100, 30);
+        // A面参数显示(下移 3 个字体大小 = 54px)
+        JLabel lblSpeed1 = new JLabel("进给速度");
+        lblSpeed1.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblSpeed1.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblSpeed1.setBounds(26, 334, 100, 40);
         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);
+        param1.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param1.setColumns(10);
+        param1.setBounds(136, 334, 100, 40);
         indexPanelA.add(param1);
 
-        JLabel lblTime1 = new JLabel("加工时间:");
-        lblTime1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblTime1.setBounds(26, 240, 100, 30);
+        JLabel lblTime1 = new JLabel("加工时间");
+        lblTime1.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblTime1.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblTime1.setBounds(266, 334, 100, 40);
         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);
+        param2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param2.setColumns(10);
+        param2.setBounds(376, 334, 100, 40);
         indexPanelA.add(param2);
 
-        JLabel lblCount1 = new JLabel("零件数:");
-        lblCount1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblCount1.setBounds(26, 280, 100, 30);
+        JLabel lblCount1 = new JLabel("零件数");
+        lblCount1.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblCount1.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblCount1.setBounds(26, 395, 100, 40);
         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);
+        param3.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param3.setColumns(10);
+        param3.setBounds(136, 395, 100, 40);
         indexPanelA.add(param3);
 
-        // ========== B面区域 ==========
-        JSeparator separator = new JSeparator();
-        separator.setOrientation(SwingConstants.VERTICAL);
-        separator.setBounds(495, 80, 2, 240);
-        indexPanelA.add(separator);
+        // ========== 中间分隔线(上半段:工件码区域)==========
+        JSeparator separator_1_top = new JSeparator();
+        separator_1_top.setOrientation(SwingConstants.VERTICAL);
+        separator_1_top.setBounds(495, 77, 23, 217);
+        indexPanelA.add(separator_1_top);
+
+        // ========== 中间分隔线(下半段:参数区域,跟着下移 54px)==========
+        JSeparator separator_1 = new JSeparator();
+        separator_1.setOrientation(SwingConstants.VERTICAL);
+        separator_1.setBounds(495, 294, 23, 157);
+        indexPanelA.add(separator_1);
 
-        pxstatus2 = new JLabel("B面");
-        pxstatus2.setForeground(new Color(0, 128, 255));
+        // ========== B面区域 ==========
+        pxstatus2 = new JLabel("B");
+        pxstatus2.setForeground(new Color(255, 128, 64));
         pxstatus2.setHorizontalAlignment(SwingConstants.CENTER);
-        pxstatus2.setFont(new Font("微软雅黑", Font.BOLD, 22));
-        pxstatus2.setBounds(517, 80, 446, 35);
+        pxstatus2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 20));
+        pxstatus2.setBounds(517, 90, 446, 44);
         indexPanelA.add(pxstatus2);
 
         product_sn2 = new JTextField();
+        product_sn2.setText("");
         product_sn2.setHorizontalAlignment(SwingConstants.CENTER);
+        product_sn2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 22));
         product_sn2.setEditable(false);
-        product_sn2.setFont(new Font("微软雅黑", Font.PLAIN, 20));
-        product_sn2.setBounds(517, 120, 446, 60);
+        product_sn2.setColumns(10);
+        product_sn2.setBounds(517, 144, 446, 70);
         indexPanelA.add(product_sn2);
 
-        JLabel lblSpeed2 = new JLabel("进给速度:");
-        lblSpeed2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblSpeed2.setBounds(517, 200, 100, 30);
+        // ========== 横向分隔线(下移 3 个字体大小 = 54px)==========
+        JSeparator separator_h = new JSeparator();
+        separator_h.setOrientation(SwingConstants.HORIZONTAL);
+        separator_h.setBounds(26, 294, 937, 10);
+        indexPanelA.add(separator_h);
+
+        // B面参数显示(下移 3 个字体大小 = 54px)
+        JLabel lblSpeed2 = new JLabel("进给速度");
+        lblSpeed2.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblSpeed2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblSpeed2.setBounds(528, 334, 100, 40);
         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);
+        param21.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param21.setColumns(10);
+        param21.setBounds(638, 334, 100, 40);
         indexPanelA.add(param21);
 
-        JLabel lblTime2 = new JLabel("加工时间:");
-        lblTime2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblTime2.setBounds(517, 240, 100, 30);
+        JLabel lblTime2 = new JLabel("加工时间");
+        lblTime2.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblTime2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblTime2.setBounds(772, 334, 100, 40);
         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);
+        param22.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param22.setColumns(10);
+        param22.setBounds(882, 334, 100, 40);
         indexPanelA.add(param22);
 
-        JLabel lblCount2 = new JLabel("零件数:");
-        lblCount2.setFont(new Font("微软雅黑", Font.PLAIN, 16));
-        lblCount2.setBounds(517, 280, 100, 30);
+        JLabel lblCount2 = new JLabel("零件数");
+        lblCount2.setHorizontalAlignment(SwingConstants.RIGHT);
+        lblCount2.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        lblCount2.setBounds(528, 395, 100, 40);
         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);
+        param23.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 18));
+        param23.setColumns(10);
+        param23.setBounds(638, 395, 100, 40);
         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 = new JButton("扫码");
         finish_ok_bt.setVisible(false);
+        finish_ok_bt.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                scanBarcode();
+            }
+        });
+        finish_ok_bt.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
+        finish_ok_bt.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 32));
+        finish_ok_bt.setBounds(136, 167, 230, 80);
+        finish_ok_bt.setEnabled(false);
         indexPanelA.add(finish_ok_bt);
 
-        finish_ng_bt = new JButton("NG");
+        finish_ng_bt = new JButton("扫码");
         finish_ng_bt.setVisible(false);
+        finish_ng_bt.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                scanBarcode();
+            }
+        });
+        finish_ng_bt.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
+        finish_ng_bt.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 32));
+        finish_ng_bt.setBounds(633, 167, 230, 80);
+        finish_ng_bt.setEnabled(false);
         indexPanelA.add(finish_ng_bt);
 
+        // 返修标签
+        fxlabel = new JLabel("该工件为返修件,请仔细检查");
+        fxlabel.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 38));
+        fxlabel.setBounds(250, 430, 500, 70);
+        fxlabel.setForeground(Color.RED);
+        fxlabel.setHorizontalAlignment(SwingConstants.CENTER);
+        fxlabel.setVisible(false);
+        indexPanelA.add(fxlabel);
+
         tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
         tabbedPane.setEnabledAt(0, true);
 
@@ -1109,6 +1195,30 @@ public class MesClient extends JFrame {
                 }
             }, 1000, 1000);
 
+            // 2.5 转台位置检测(1秒)— 更新顶部标签 + 保持扫码框焦点
+            Timer sideDetectTimer = new Timer();
+            sideDetectTimer.schedule(new TimerTask() {
+                public void run() {
+                    try {
+                        if (scanTextField != null) {
+                            SwingUtilities.invokeLater(() -> scanTextField.requestFocusInWindow());
+                        }
+                        if (laserDevice == null || !laserDevice.isConnected()) return;
+
+                        String side = laserDevice.readSideAtLoad();
+                        SwingUtilities.invokeLater(() -> updateCurSideLabel(side));
+
+                        // 记录最近一次上料面(供状态跟踪用;不主动写 #1590,避免跟 checkStatus 打架)
+                        if (!side.equals(lastSideAtLoad)) {
+                            lastSideAtLoad = side;
+                            log.info("转台面切换,检测到上料面={}", side.isEmpty() ? "未到位" : side);
+                        }
+                    } catch (Exception e) {
+                        log.error("转台位置检测异常", e);
+                    }
+                }
+            }, 1000, 1000);
+
             // 3. 参数上传定时器(30秒)
             Timer laserUploadTimer = new Timer();
             laserUploadTimer.schedule(new TimerTask() {