hfy 2 дней назад
Родитель
Сommit
8bc9d62b74

+ 264 - 0
src/com/mes/ui/DeviceManagePanel.java

@@ -0,0 +1,264 @@
+package com.mes.ui;
+
+import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
+import com.mes.util.JdbcUtils;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableModel;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 设备管理:IP、预设数量、按颗数区间的铆接参数配置。
+ */
+public class DeviceManagePanel extends JPanel {
+
+    // 参数表按颗数区间维护;F/S 对应 PLC 的拉力/行程上下限。
+    private static final String[] PARAM_COLUMNS = {"起始颗", "结束颗", "F-min", "F-max", "S-min", "S-max"};
+    private static final int CONTENT_WIDTH = 940;
+
+    private JTextField txtASet;
+    private JTextField txtBSet;
+    private JTextField txtIpA;
+    private JTextField txtIpB;
+    private DefaultTableModel tableModelA;
+    private DefaultTableModel tableModelB;
+    private JTable tableA;
+    private JTable tableB;
+
+    public DeviceManagePanel() {
+        setLayout(null);
+        setPreferredSize(new Dimension(980, 720));
+        buildUi();
+        loadConfigToUi();
+    }
+
+    private void buildUi() {
+        Font labelFont = new Font("微软雅黑", Font.PLAIN, 18);
+        Font fieldFont = new Font("微软雅黑", Font.PLAIN, 18);
+        Font hintFont = new Font("微软雅黑", Font.PLAIN, 14);
+
+        // 顶部维护两把枪的任务数量和 PLC IP,保存后会立即刷新 MesClient 中的运行配置。
+        addLabel("A枪预设数量:", 20, 16, 130, 34, labelFont);
+        txtASet = addField(String.valueOf(MesClient.aSetNum), 150, 16, 120, 34, fieldFont);
+
+        addLabel("B枪预设数量:", 320, 16, 130, 34, labelFont);
+        txtBSet = addField(String.valueOf(MesClient.bSetNum), 450, 16, 120, 34, fieldFont);
+
+        addLabel("A枪IP:", 20, 58, 80, 34, labelFont);
+        txtIpA = addField(MesClient.curIpA, 100, 58, 170, 34, fieldFont);
+
+        addLabel("B枪IP:", 320, 58, 80, 34, labelFont);
+        txtIpB = addField(MesClient.curIpB, 400, 58, 170, 34, fieldFont);
+
+        addLabel("说明:S-min/S-max 为PLC原始值,界面行程显示值 = S / 1000", 20, 98, CONTENT_WIDTH, 24, hintFont);
+
+        addLabel("A枪参数区间", 20, 128, 200, 28, labelFont);
+        tableModelA = createParamTableModel();
+        tableA = createParamTable(tableModelA, 20, 156, CONTENT_WIDTH, 150);
+
+        JButton btnAddA = new JButton("A枪添加行");
+        btnAddA.setFont(labelFont);
+        btnAddA.setBounds(20, 316, 120, 32);
+        btnAddA.addActionListener(e -> tableModelA.addRow(new Object[]{"", "", "0", "0", "0", "0"}));
+        add(btnAddA);
+
+        JButton btnDelA = new JButton("A枪删除行");
+        btnDelA.setFont(labelFont);
+        btnDelA.setBounds(150, 316, 120, 32);
+        btnDelA.addActionListener(e -> removeSelectedRow(tableA, tableModelA));
+        add(btnDelA);
+
+        addLabel("B枪参数区间", 20, 360, 200, 28, labelFont);
+        tableModelB = createParamTableModel();
+        tableB = createParamTable(tableModelB, 20, 388, CONTENT_WIDTH, 150);
+
+        JButton btnAddB = new JButton("B枪添加行");
+        btnAddB.setFont(labelFont);
+        btnAddB.setBounds(20, 548, 120, 32);
+        btnAddB.addActionListener(e -> tableModelB.addRow(new Object[]{"", "", "0", "0", "0", "0"}));
+        add(btnAddB);
+
+        JButton btnDelB = new JButton("B枪删除行");
+        btnDelB.setFont(labelFont);
+        btnDelB.setBounds(150, 548, 120, 32);
+        btnDelB.addActionListener(e -> removeSelectedRow(tableB, tableModelB));
+        add(btnDelB);
+
+        JButton btnSave = new JButton("保存全部设置");
+        btnSave.setFont(new Font("微软雅黑", Font.PLAIN, 20));
+        btnSave.setBounds(380, 596, 180, 45);
+        btnSave.addActionListener(e -> saveAllConfig());
+        add(btnSave);
+
+        addLabel("每行表示“从起始颗到结束颗”使用同一套参数;开工时下发第1颗参数,每完成一颗后自动切换下一颗区间。",
+                20, 652, CONTENT_WIDTH, 40, hintFont);
+    }
+
+    private void addLabel(String text, int x, int y, int w, int h, Font font) {
+        JLabel label = new JLabel("<html>" + text + "</html>");
+        label.setFont(font);
+        label.setBounds(x, y, w, h);
+        add(label);
+    }
+
+    private JTextField addField(String text, int x, int y, int w, int h, Font font) {
+        JTextField field = new JTextField(text);
+        field.setFont(font);
+        field.setBounds(x, y, w, h);
+        add(field);
+        return field;
+    }
+
+    private DefaultTableModel createParamTableModel() {
+        return new DefaultTableModel(PARAM_COLUMNS, 0) {
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                return true;
+            }
+        };
+    }
+
+    private JTable createParamTable(DefaultTableModel model, int x, int y, int w, int h) {
+        JTable table = new JTable(model);
+        table.setRowHeight(28);
+        table.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+        JScrollPane scrollPane = new JScrollPane(table);
+        scrollPane.setBounds(x, y, w, h);
+        add(scrollPane);
+        return table;
+    }
+
+    private void removeSelectedRow(JTable table, DefaultTableModel model) {
+        int row = table.getSelectedRow();
+        if (row >= 0) {
+            model.removeRow(row);
+        }
+    }
+
+    private void loadConfigToUi() {
+        // 从 MesClient 当前缓存和本地 SQLite 表中回填,保证切到设备管理页时看到的是实际运行值。
+        txtASet.setText(String.valueOf(MesClient.aSetNum));
+        txtBSet.setText(String.valueOf(MesClient.bSetNum));
+        txtIpA.setText(MesClient.curIpA);
+        txtIpB.setText(MesClient.curIpB);
+        fillTableModel(tableModelA, JdbcUtils.getRivetParams("A"));
+        fillTableModel(tableModelB, JdbcUtils.getRivetParams("B"));
+    }
+
+    private void fillTableModel(DefaultTableModel model, List<RivetParamRange> ranges) {
+        model.setRowCount(0);
+        for (RivetParamRange range : ranges) {
+            model.addRow(new Object[]{
+                    String.valueOf(range.getStartRivet()),
+                    String.valueOf(range.getEndRivet()),
+                    String.valueOf(range.getFMin()),
+                    String.valueOf(range.getFMax()),
+                    String.valueOf(range.getSMin()),
+                    String.valueOf(range.getSMax())
+            });
+        }
+    }
+
+    private List<RivetParamRange> parseTableModel(DefaultTableModel model, String gunType) {
+        List<RivetParamRange> ranges = new ArrayList<>();
+        for (int i = 0; i < model.getRowCount(); i++) {
+            String startText = String.valueOf(model.getValueAt(i, 0)).trim();
+            String endText = String.valueOf(model.getValueAt(i, 1)).trim();
+            if (startText.isEmpty() && endText.isEmpty()) {
+                continue;
+            }
+            if (startText.isEmpty() || endText.isEmpty()) {
+                throw new IllegalArgumentException(gunType + "枪第" + (i + 1) + "行起始颗/结束颗不能为空");
+            }
+            int start = Integer.parseInt(startText);
+            int end = Integer.parseInt(endText);
+            if (start < 1 || end < start) {
+                throw new IllegalArgumentException(gunType + "枪第" + (i + 1) + "行颗数范围无效");
+            }
+            // 一行配置代表一段连续颗数,下发时通过 RivetParamRange.contains 定位使用哪组参数。
+            RivetParamRange range = new RivetParamRange();
+            range.setGunType(gunType);
+            range.setStartRivet(start);
+            range.setEndRivet(end);
+            range.setFMin(parseIntCell(model, i, 2));
+            range.setFMax(parseIntCell(model, i, 3));
+            range.setSMin(parseIntCell(model, i, 4));
+            range.setSMax(parseIntCell(model, i, 5));
+            ranges.add(range);
+        }
+        if (ranges.isEmpty()) {
+            throw new IllegalArgumentException(gunType + "枪至少配置一行参数区间");
+        }
+        return ranges;
+    }
+
+    private int parseIntCell(DefaultTableModel model, int row, int col) {
+        String text = String.valueOf(model.getValueAt(row, col)).trim();
+        if (text.isEmpty()) {
+            return 0;
+        }
+        return Integer.parseInt(text);
+    }
+
+    private void saveAllConfig() {
+        try {
+            // 先完整校验界面输入,避免只保存了 IP 或只保存了某一把枪参数。
+            short newASet = Short.parseShort(txtASet.getText().trim());
+            short newBSet = Short.parseShort(txtBSet.getText().trim());
+            String newIpA = txtIpA.getText().trim();
+            String newIpB = txtIpB.getText().trim();
+            if (newIpA.isEmpty() || newIpB.isEmpty()) {
+                JOptionPane.showMessageDialog(MesClient.mesClientFrame, "IP地址不能为空", "错误", JOptionPane.ERROR_MESSAGE);
+                return;
+            }
+
+            List<RivetParamRange> rangesA = parseTableModel(tableModelA, "A");
+            List<RivetParamRange> rangesB = parseTableModel(tableModelB, "B");
+
+            JdbcUtils.updateConfig(newASet, newBSet, newIpA, newIpB);
+            JdbcUtils.saveRivetParams("A", rangesA);
+            JdbcUtils.saveRivetParams("B", rangesB);
+
+            // 同步刷新内存缓存,后续开工/换颗数时直接使用最新配置下发 PLC。
+            MesClient.aSetNum = newASet;
+            MesClient.bSetNum = newBSet;
+            MesClient.rivetParamRangesA = rangesA;
+            MesClient.rivetParamRangesB = rangesB;
+
+            if (MesClient.param1 != null) {
+                MesClient.param1.setText(String.valueOf(MesClient.aSetNum));
+            }
+            if (MesClient.param3 != null) {
+                MesClient.param3.setText(String.valueOf(MesClient.bSetNum));
+            }
+
+            boolean ipAChanged = !newIpA.equals(MesClient.curIpA);
+            boolean ipBChanged = !newIpB.equals(MesClient.curIpB);
+            if (ipAChanged) {
+                // IP 改动后关闭旧连接并按新地址创建 ModbusTcp,避免继续向旧设备写参数。
+                if (MesClient.plcA != null) {
+                    MesClient.plcA.close();
+                }
+                MesClient.curIpA = newIpA;
+                MesClient.plcA = new ModbusTcp(1, MesClient.curIpA);
+            }
+            if (ipBChanged) {
+                // B枪同 A枪处理:只在地址变化时重连,减少对生产中的连接干扰。
+                if (MesClient.plcB != null) {
+                    MesClient.plcB.close();
+                }
+                MesClient.curIpB = newIpB;
+                MesClient.plcB = new ModbusTcp(1, MesClient.curIpB);
+            }
+
+            JOptionPane.showMessageDialog(MesClient.mesClientFrame, "配置已保存", "提示", JOptionPane.INFORMATION_MESSAGE);
+        } catch (NumberFormatException ex) {
+            JOptionPane.showMessageDialog(MesClient.mesClientFrame, "请输入有效的数字", "错误", JOptionPane.ERROR_MESSAGE);
+        } catch (IllegalArgumentException ex) {
+            JOptionPane.showMessageDialog(MesClient.mesClientFrame, ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
+        }
+    }
+}

+ 143 - 15
src/com/mes/ui/MesClient.java

@@ -79,15 +79,22 @@ public class MesClient extends JFrame {
     public static MesWebView jfxPanel = null;
     public static JPanel indexPanelC;
     public static MesWebView jfxPanel2 = null;
+    public static JPanel indexPanelD;
     public static MesRadio mesRadioHj;
 
     public static JTextField param1;
     public static JTextField param2;
     public static JTextField param3;
     public static JTextField param4;
-
-    public static ModbusTcp plcA = new ModbusTcp(1, "192.168.1.7");
-    public static ModbusTcp plcB = new ModbusTcp(1, "192.168.1.8");
+    public static JTextField param5;
+    public static JTextField param6;
+    public static JTextField param7;
+    public static JTextField param8;
+
+    public static ModbusTcp plcA;
+    public static ModbusTcp plcB;
+    public static String curIpA = "";
+    public static String curIpB = "";
 //    public static ModbusTcp plcA = new ModbusTcp(1, "192.168.1.27");
 //    public static ModbusTcp plcB = new ModbusTcp(1, "192.168.1.28");
 
@@ -103,7 +110,7 @@ public class MesClient extends JFrame {
     public static Short aMax = 0;
     public static Short aFinish = 0;
     public static List<Map> alist = new ArrayList<>();
-    public static Short bSetNum = 41;
+    public static Short bSetNum = 42;
 //    public static Short bSetNum = 27;
     public static Short sortB = 0;
     public static Short bMax = 0;
@@ -111,6 +118,9 @@ public class MesClient extends JFrame {
     public static Short deviceControl = 0; // 0=本地 1=远程
     public static Integer tjStatus = 0; // 1=提交失败
 
+    public static List<RivetParamRange> rivetParamRangesA = new ArrayList<>();
+    public static List<RivetParamRange> rivetParamRangesB = new ArrayList<>();
+
     public static String tjFlagTextErr = "结果上传MES失败,请重试";
 
     public static String curSn = "";
@@ -127,12 +137,13 @@ public class MesClient extends JFrame {
                     //读文件配置
                     readProperty();
 
+                    JdbcUtils.getConn();
+                    initLocalConfig();
+
                     // 显示界面
                     mesClientFrame = new MesClient();
                     mesClientFrame.setVisible(false);
 
-                    JdbcUtils.getConn();
-
                     welcomeWin = new LoginFarme();
                     welcomeWin.setVisible(true);
 
@@ -216,6 +227,41 @@ public class MesClient extends JFrame {
         System.out.println(mes_gw + ";" + mes_gw_des + ";" + mes_server_ip + ";" + mes_tcp_port + ";" + mes_heart_beat_cycle);
     }
 
+    private static void initLocalConfig() {
+        // 本地设备配置优先从 SQLite 加载,便于现场直接在设备管理页调整后持久化。
+        Map<String, Object> config = JdbcUtils.getConfig();
+        if (config.size() > 0) {
+            aSetNum = (Short) config.get("a_set_num");
+            bSetNum = (Short) config.get("b_set_num");
+            curIpA = (String) config.get("plc_ip_a");
+            curIpB = (String) config.get("plc_ip_b");
+            // ModbusTcp 实例必须等 IP 配置确定后再创建,否则会一直连默认设备。
+            plcA = new ModbusTcp(1, curIpA);
+            plcB = new ModbusTcp(1, curIpB);
+
+            if (param1 != null) param1.setText(String.valueOf(aSetNum));
+            if (param3 != null) param3.setText(String.valueOf(bSetNum));
+
+            System.out.println("加载本地配置成功:aSetNum=" + aSetNum + ", bSetNum=" + bSetNum + ", plcA=" + curIpA + ", plcB=" + curIpB);
+        } else {
+            // 防御性兜底:正常情况下 JdbcUtils 会先创建默认配置,这里用于数据库异常或空表场景。
+            aSetNum = 40;
+            bSetNum = 42;
+            curIpA = "192.168.1.7";
+            curIpB = "192.168.1.8";
+            plcA = new ModbusTcp(1, curIpA);
+            plcB = new ModbusTcp(1, curIpB);
+            System.out.println("未找到本地配置,使用默认值");
+        }
+        loadRivetParamConfig();
+    }
+
+    public static void loadRivetParamConfig() {
+        // 参数区间缓存到内存中,PLC 轮询线程按颗数切换时不需要频繁查数据库。
+        rivetParamRangesA = JdbcUtils.getRivetParams("A");
+        rivetParamRangesB = JdbcUtils.getRivetParams("B");
+    }
+
     public static void getPlcParam() {
         if(cjTimer!=null) {
             cjTimer.cancel();
@@ -416,10 +462,11 @@ public class MesClient extends JFrame {
         MesClient.blist = new ArrayList<>();
         MesClient.sortB = 0;
         MesClient.sortA = 0;
-        MesClient.param1.setText("");
+        MesClient.param1.setText(String.valueOf(aSetNum));
         MesClient.param2.setText("");
-        MesClient.param3.setText("");
+        MesClient.param3.setText(String.valueOf(bSetNum));
         MesClient.param4.setText("");
+        resetForceStrokeDisplay();
 
         deviceControl = ModbusUtil.getControlModel(plcA);
         if(deviceControl == 1){
@@ -431,8 +478,8 @@ public class MesClient extends JFrame {
         ModbusUtil.setPowerOff(MesClient.plcA); // 远程关机
         ModbusUtil.setPowerOff(MesClient.plcB); // 远程关机
 
-        ModbusUtil.setTask(MesClient.plcA,MesClient.aSetNum);
-        ModbusUtil.setTask(MesClient.plcB,MesClient.bSetNum);
+        ModbusUtil.setTask(MesClient.plcA, MesClient.aSetNum, "A");
+        ModbusUtil.setTask(MesClient.plcB, MesClient.bSetNum, "B");
 
         updateMaterailData();
     }
@@ -441,6 +488,13 @@ public class MesClient extends JFrame {
 
     }
 
+    public static void resetForceStrokeDisplay() {
+        if (param5 != null) param5.setText("-");
+        if (param6 != null) param6.setText("-");
+        if (param7 != null) param7.setText("-");
+        if (param8 != null) param8.setText("-");
+    }
+
     //获取用户20位
     public static void getUser() {
         user20 = user_menu.getText().toString();
@@ -533,6 +587,7 @@ public class MesClient extends JFrame {
         connect_request_flag = false;
     }
 
+    public static int lastSelectedIndex = 0;
     public MesClient() {
         setIconImage(Toolkit.getDefaultToolkit().getImage(MesClient.class.getResource("/bg/logo.png")));
         setTitle("MES系统客户端:"+mes_gw + "- " + mes_gw_des);
@@ -763,7 +818,7 @@ public class MesClient extends JFrame {
         param1 = new JTextField();
         param1.setHorizontalAlignment(SwingConstants.CENTER);
         param1.setFont(new Font("微软雅黑", Font.PLAIN, 18));
-        param1.setText("0");
+        param1.setText(String.valueOf(aSetNum));
         param1.setEditable(false);
         param1.setBounds(288, 241, 83, 34);
         indexPanelA.add(param1);
@@ -795,7 +850,7 @@ public class MesClient extends JFrame {
         indexPanelA.add(lblNewLabel_1_2);
 
         param3 = new JTextField();
-        param3.setText("0");
+        param3.setText(String.valueOf(bSetNum));
         param3.setHorizontalAlignment(SwingConstants.CENTER);
         param3.setFont(new Font("微软雅黑", Font.PLAIN, 18));
         param3.setEditable(false);
@@ -817,6 +872,62 @@ public class MesClient extends JFrame {
         param4.setBounds(608, 285, 83, 34);
         indexPanelA.add(param4);
 
+        JLabel lblForceA = new JLabel("\u62c9\u529b");
+        lblForceA.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        lblForceA.setBounds(204, 329, 50, 34);
+        indexPanelA.add(lblForceA);
+
+        param5 = new JTextField();
+        param5.setHorizontalAlignment(SwingConstants.CENTER);
+        param5.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        param5.setText("-");
+        param5.setEditable(false);
+        param5.setColumns(10);
+        param5.setBounds(260, 329, 65, 34);
+        indexPanelA.add(param5);
+
+        JLabel lblStrokeA = new JLabel("\u884c\u7a0b");
+        lblStrokeA.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        lblStrokeA.setBounds(330, 329, 50, 34);
+        indexPanelA.add(lblStrokeA);
+
+        param6 = new JTextField();
+        param6.setHorizontalAlignment(SwingConstants.CENTER);
+        param6.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        param6.setText("-");
+        param6.setEditable(false);
+        param6.setColumns(10);
+        param6.setBounds(380, 329, 65, 34);
+        indexPanelA.add(param6);
+
+        JLabel lblForceB = new JLabel("\u62c9\u529b");
+        lblForceB.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        lblForceB.setBounds(525, 329, 50, 34);
+        indexPanelA.add(lblForceB);
+
+        param7 = new JTextField();
+        param7.setHorizontalAlignment(SwingConstants.CENTER);
+        param7.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        param7.setText("-");
+        param7.setEditable(false);
+        param7.setColumns(10);
+        param7.setBounds(581, 329, 65, 34);
+        indexPanelA.add(param7);
+
+        JLabel lblStrokeB = new JLabel("\u884c\u7a0b");
+        lblStrokeB.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        lblStrokeB.setBounds(651, 329, 50, 34);
+        indexPanelA.add(lblStrokeB);
+
+        param8 = new JTextField();
+        param8.setHorizontalAlignment(SwingConstants.CENTER);
+        param8.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        param8.setText("-");
+        param8.setEditable(false);
+        param8.setColumns(10);
+        param8.setBounds(701, 329, 65, 34);
+        indexPanelA.add(param8);
+
         tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
         tabbedPane.setEnabledAt(0, true);
 
@@ -835,16 +946,33 @@ public class MesClient extends JFrame {
 
 		tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPane, null);
 
+        indexPanelD = new DeviceManagePanel();
+        JScrollPane scrollPanelD = new JScrollPane(indexPanelD);
+        scrollPanelD.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+        scrollPanelD.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+        tabbedPane.addTab("设备管理", new ImageIcon(MesClient.class.getResource("/bg/menu_setting.png")), scrollPanelD, null);
 
-		tabbedPane.addChangeListener(new ChangeListener() {
+
+        tabbedPane.addChangeListener(new ChangeListener() {
             @Override
             public void stateChanged(ChangeEvent e) {
                 JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
                 int selectedIndex = tabbedPane.getSelectedIndex();
                 System.out.println("selectedIndex:"+selectedIndex);
 
-                if(selectedIndex == 1){
-
+                if (selectedIndex == 3) {
+                    // 设备管理能改 PLC 地址和工艺参数,进入前做一次简单管理员口令校验。
+                    String input = JOptionPane.showInputDialog(mesClientFrame, "请输入管理员密码以进入设备管理:", "权限验证", JOptionPane.QUESTION_MESSAGE);
+                    if (input != null && input.equals("503833")) {
+                        lastSelectedIndex = selectedIndex;
+                    } else {
+                        if (input != null) {
+                            JOptionPane.showMessageDialog(mesClientFrame, "密码错误,请重试", "错误", JOptionPane.ERROR_MESSAGE);
+                        }
+                        tabbedPane.setSelectedIndex(lastSelectedIndex);
+                    }
+                } else {
+                    lastSelectedIndex = selectedIndex;
                 }
             }
         });

+ 2 - 2
src/com/mes/ui/MesRevice.java

@@ -56,8 +56,8 @@ public class MesRevice {
                 ModbusUtil.setPowerOn(MesClient.plcA); // 远程开机
                 ModbusUtil.setPowerOn(MesClient.plcB); // 远程开机
 
-                ModbusUtil.setTask(MesClient.plcA,MesClient.aSetNum);
-                ModbusUtil.setTask(MesClient.plcB,MesClient.bSetNum);
+                ModbusUtil.setTask(MesClient.plcA, MesClient.aSetNum, "A");
+                ModbusUtil.setTask(MesClient.plcB, MesClient.bSetNum, "B");
             }
         }catch (Exception e){
             e.printStackTrace();

+ 67 - 21
src/com/mes/ui/ModbusUtil.java

@@ -3,10 +3,25 @@ package com.mes.ui;
 import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
 import com.mes.util.JdbcUtils;
 
+import javax.swing.*;
 import java.nio.charset.Charset;
+import java.util.List;
 
 public class ModbusUtil {
 
+    // 读取当前实际输出值并刷新工作面板;1064=F-out,1065=S-out。
+    private static void updateForceStrokeDisplay(ModbusTcp plc, JTextField forceField, JTextField strokeField) {
+        if (forceField == null || strokeField == null) {
+            return;
+        }
+        try {
+            forceField.setText(plc.readInt16(1064) + "");
+            strokeField.setText((float) plc.readInt16(1065) / 1000 + "");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
     public static void getDataA(ModbusTcp plc){
 
         // 说明预设数量有变化不能修改
@@ -17,6 +32,7 @@ public class ModbusUtil {
         // 预设数量=41129  完成数=41137
 
         Short cur = plc.readInt16(1136);
+        updateForceStrokeDisplay(plc, MesClient.param5, MesClient.param6);
 //        System.out.println("F-out:"+plc.readInt16(1064));
 //        System.out.println("S-out:"+plc.readInt16(1065));
 //        System.out.println("F-min:"+plc.readInt16(1068));
@@ -29,10 +45,12 @@ public class ModbusUtil {
             MesClient.aMax = plc.readInt16(1128); // 任务数量
             MesClient.param1.setText(String.valueOf(MesClient.aMax));
         }
+        MesClient.param2.setText(cur + "");
 
         if(MesClient.sortA == cur - 1){
             MesClient.sortA = cur;
             MesClient.aFinish = plc.readInt16(1138);
+            // 记录当前这一颗的实际输出和当时的上下限,用于后续上传 MES。
             String fout = plc.readInt16(1064)+"";
             String sout = (float)plc.readInt16(1065)/1000+"";
 
@@ -42,12 +60,12 @@ public class ModbusUtil {
             String fmax = plc.readInt16(1069)+"";
             String smax = (float)plc.readInt16(1071)/1000+"";
 
-            MesClient.param2.setText(cur+"");
             System.out.println("cur:"+cur);
 
-            if(MesClient.sortA == 4){
-                    plc.writeInt16(1070,(short) 2000);
-                    plc.writeInt16(1071,(short) 5000);
+            // 当前颗数完成后,提前把下一颗所在区间的参数写入 PLC。
+            int nextRivet = cur + 1;
+            if (nextRivet <= MesClient.aMax) {
+                applyParamsForRivet(plc, MesClient.rivetParamRangesA, nextRivet);
             }
 
 
@@ -65,7 +83,9 @@ public class ModbusUtil {
         // 41069=F-min  41071=S-min 41070=F-max  41072=S-max
         // 预设数量=41129  完成数=41137
 
+
         Short cur = plc.readInt16(1136);
+        updateForceStrokeDisplay(plc, MesClient.param7, MesClient.param8);
 //        System.out.println("F-out:"+plc.readInt16(1064));
 //        System.out.println("S-out:"+plc.readInt16(1065));
 //        System.out.println("F-min:"+plc.readInt16(1068));
@@ -80,9 +100,11 @@ public class ModbusUtil {
             MesClient.bMax = plc.readInt16(1128);
             MesClient.param3.setText(String.valueOf(MesClient.bMax));
         }
+        MesClient.param4.setText(cur + "");
 
         if(MesClient.sortB == cur - 1){
             MesClient.sortB = cur;
+            // B枪同样先保存本颗结果,再切换下一颗参数。
             String fout = plc.readInt16(1064)+"";
             String sout = (float)plc.readInt16(1065)/1000+"";
 
@@ -92,16 +114,13 @@ public class ModbusUtil {
             String fmax = plc.readInt16(1069)+"";
             String smax = (float)plc.readInt16(1071)/1000+"";
 
-            if(MesClient.sortB == 18){
-//                if(MesClient.bMax == 36){
-//                    plc.writeInt16(1070,(short) 4000);
-//                    plc.writeInt16(1071,(short) 5500);
-//                }
+            // 当前颗数完成后,提前把下一颗所在区间的参数写入 PLC。
+            int nextRivet = cur + 1;
+            if (nextRivet <= MesClient.bMax) {
+                applyParamsForRivet(plc, MesClient.rivetParamRangesB, nextRivet);
             }
 
 
-            MesClient.param4.setText(cur+"");
-
             System.out.println("cur:"+cur);
             if(!MesClient.product_sn.getText().isEmpty()){
                JdbcUtils.insertProdData(MesClient.mes_gw, MesClient.mes_line_sn, MesClient.product_sn.getText(),"B",fout,sout,fmin,smin,fmax,smax,"1",cur+"", MesClient.user_menu.getText());
@@ -212,8 +231,40 @@ public class ModbusUtil {
         return ret;
     }
 
+    // 按颗数查找命中的参数区间;找不到时保持 PLC 当前参数不变。
+    public static RivetParamRange findRivetParamRange(List<RivetParamRange> ranges, int rivetNo) {
+        if (ranges == null) {
+            return null;
+        }
+        for (RivetParamRange range : ranges) {
+            if (range.contains(rivetNo)) {
+                return range;
+            }
+        }
+        return null;
+    }
+
+    // 下发一颗对应的参数区间:1068/1069 是 F-min/F-max,1070/1071 是 S-min/S-max。
+    public static void applyParamsForRivet(ModbusTcp plc, List<RivetParamRange> ranges, int rivetNo) {
+        RivetParamRange range = findRivetParamRange(ranges, rivetNo);
+        if (range == null) {
+            System.out.println("未找到第" + rivetNo + "颗对应的参数配置");
+            return;
+        }
+        try {
+            plc.writeInt16(1068, (short) range.getFMin());
+            plc.writeInt16(1069, (short) range.getFMax());
+            plc.writeInt16(1070, (short) range.getSMin());
+            plc.writeInt16(1071, (short) range.getSMax());
+            System.out.println("下发铆接参数:第" + rivetNo + "颗, F=" + range.getFMin() + "-" + range.getFMax()
+                    + ", S=" + range.getSMin() + "-" + range.getSMax());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
     // 重置任务
-    public static void setTask(ModbusTcp plc,Short setNum){
+    public static void setTask(ModbusTcp plc, Short setNum, String gunType) {
         try{
             // 设置模式 1=标记铆接模式
             plc.writeInt16(1092,(short) 1);
@@ -222,15 +273,10 @@ public class ModbusUtil {
             plc.writeInt16(1136,(short) 0);
             plc.writeInt16(1138,(short) 0);
 
-            if(setNum == 22){
-                plc.writeInt16(1070,(short) 2500);
-                plc.writeInt16(1071,(short) 4500);
-            }
-
-            if (setNum == 40){
-                plc.writeInt16(1070,(short) 2000);
-                plc.writeInt16(1071,(short) 4000);
-            }
+            // 新任务开始时先写第 1 颗参数,后续在 getDataA/getDataB 中按完成数切换下一颗。
+            List<RivetParamRange> ranges = "A".equalsIgnoreCase(gunType)
+                    ? MesClient.rivetParamRangesA : MesClient.rivetParamRangesB;
+            applyParamsForRivet(plc, ranges, 1);
 
         }catch (Exception e){
             e.printStackTrace();

+ 2 - 2
src/com/mes/ui/OprnoUtil.java

@@ -19,9 +19,9 @@ public class OprnoUtil {
     };
     public static String[] xtoprnodes = new String[]{
             "右边梁镭雕二维码","左右边梁防爆阀拉铆","CMT框架一序焊接",
-            "人工补焊", "框架CMT二序焊接", "人工补焊", "焊道检查", "总成正面CNC",
+            "人工补焊", "框架CMT二序焊接", "液冷板装配", "焊道检查", "总成正面CNC",
             "总成反面CNC", "框架去毛刺+清洁", "封堵片焊接+打磨", "边框气密", "焊道补焊",
-            "框架反面涂胶", "液冷板安装", "正面溢胶清理,补胶", "液冷板激光点固", "液冷板水嘴处焊接",
+            "总成反面装配(2序)", "液冷板安装", "总成正面装配1", "液冷板激光点固", "液冷板水嘴处焊接",
             "焊道打磨", "液冷板FSW", "匙孔补焊打磨", "总成反面拉铆", "总成正面拉铆1",
             "总成正面拉铆2", "边梁套筒涂胶+压合", "箱体封堵", "胶水固化", "半成品气密",
             "补强板安装", "胶水固化", "拆卸补强板压紧工装", "FSW焊道涂胶+双层拉铆螺母涂胶", "人工抹胶",

+ 90 - 0
src/com/mes/ui/RivetParamRange.java

@@ -0,0 +1,90 @@
+package com.mes.ui;
+
+/**
+ * 拉铆枪按颗数区间的PLC参数配置。
+ */
+public class RivetParamRange {
+    // A/B 枪类型,用于区分同一张配置表中的两套参数。
+    private String gunType;
+    // 当前参数生效的起止颗数,包含边界。
+    private int startRivet;
+    private int endRivet;
+    // PLC 拉力/行程上下限;S 值按 PLC 原始值保存,显示时再除以 1000。
+    private int fMin;
+    private int fMax;
+    private int sMin;
+    private int sMax;
+
+    public RivetParamRange() {
+    }
+
+    public RivetParamRange(String gunType, int startRivet, int endRivet, int fMin, int fMax, int sMin, int sMax) {
+        this.gunType = gunType;
+        this.startRivet = startRivet;
+        this.endRivet = endRivet;
+        this.fMin = fMin;
+        this.fMax = fMax;
+        this.sMin = sMin;
+        this.sMax = sMax;
+    }
+
+    public String getGunType() {
+        return gunType;
+    }
+
+    public void setGunType(String gunType) {
+        this.gunType = gunType;
+    }
+
+    public int getStartRivet() {
+        return startRivet;
+    }
+
+    public void setStartRivet(int startRivet) {
+        this.startRivet = startRivet;
+    }
+
+    public int getEndRivet() {
+        return endRivet;
+    }
+
+    public void setEndRivet(int endRivet) {
+        this.endRivet = endRivet;
+    }
+
+    public int getFMin() {
+        return fMin;
+    }
+
+    public void setFMin(int fMin) {
+        this.fMin = fMin;
+    }
+
+    public int getFMax() {
+        return fMax;
+    }
+
+    public void setFMax(int fMax) {
+        this.fMax = fMax;
+    }
+
+    public int getSMin() {
+        return sMin;
+    }
+
+    public void setSMin(int sMin) {
+        this.sMin = sMin;
+    }
+
+    public int getSMax() {
+        return sMax;
+    }
+
+    public void setSMax(int sMax) {
+        this.sMax = sMax;
+    }
+
+    public boolean contains(int rivetNo) {
+        return rivetNo >= startRivet && rivetNo <= endRivet;
+    }
+}

+ 147 - 2
src/com/mes/util/JdbcUtils.java

@@ -1,6 +1,7 @@
 package com.mes.util;
 
 import com.mes.ui.ProdReq;
+import com.mes.ui.RivetParamRange;
 
 import java.sql.*;
 import java.util.ArrayList;
@@ -18,9 +19,11 @@ public class JdbcUtils {
             System.out.println("连接到SQLite数据库成功!");
             create_bw_record();//初始化结构表
             create_bw_prod();
+            create_config_table();
+            create_rivet_param_table();
         } catch (Exception e) {
             // TODO Auto-generated catch block
-        	close();//关闭数据库连接
+         	close();//关闭数据库连接
             e.printStackTrace();
         }
         return conn;
@@ -74,9 +77,151 @@ public class JdbcUtils {
 		System.out.println("表prod创建成功!");
 		statement.close();
 	}
+
+	public static void create_config_table() throws SQLException {
+		Statement statement = conn.createStatement();
+		// 保存两把枪的基础配置:预设数量和 PLC IP。
+		String sql = "CREATE TABLE if not exists bw_config("
+				+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+				+ "a_set_num SHORT, "
+				+ "b_set_num SHORT, "
+				+ "plc_ip_a VARCHAR(20), "
+				+ "plc_ip_b VARCHAR(20))";
+		statement.executeUpdate(sql);
+
+		// 首次运行自动写入默认值,保证 MesClient 启动时一定能创建 ModbusTcp。
+		ResultSet rs = statement.executeQuery("SELECT count(*) FROM bw_config");
+		if (rs.next() && rs.getInt(1) == 0) {
+			statement.executeUpdate("INSERT INTO bw_config (a_set_num, b_set_num, plc_ip_a, plc_ip_b) "
+					+ "VALUES (40, 42, '192.168.1.7', '192.168.1.8')");
+		}
+		rs.close();
+		System.out.println("表config创建并初始化成功!");
+		statement.close();
+	}
+
+	public static java.util.Map<String, Object> getConfig() {
+		java.util.Map<String, Object> config = new java.util.HashMap<>();
+		// 当前只维护一套本机配置,取第一行作为运行配置。
+		String sql = "SELECT a_set_num, b_set_num, plc_ip_a, plc_ip_b FROM bw_config LIMIT 1";
+		Connection conn = JdbcUtils.getConn();
+		try (Statement stmt = conn.createStatement();
+			 ResultSet rs = stmt.executeQuery(sql)) {
+			if (rs.next()) {
+				config.put("a_set_num", rs.getShort("a_set_num"));
+				config.put("b_set_num", rs.getShort("b_set_num"));
+				config.put("plc_ip_a", rs.getString("plc_ip_a"));
+				config.put("plc_ip_b", rs.getString("plc_ip_b"));
+			}
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return config;
+	}
+
+	public static void create_rivet_param_table() throws SQLException {
+		Statement statement = conn.createStatement();
+		// 每行是一把枪的一段颗数区间,保存下发 PLC 的拉力/行程上下限。
+		String sql = "CREATE TABLE if not exists bw_rivet_param("
+				+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+				+ "gun_type VARCHAR(1), "
+				+ "start_rivet INTEGER, "
+				+ "end_rivet INTEGER, "
+				+ "f_min INTEGER, "
+				+ "f_max INTEGER, "
+				+ "s_min INTEGER, "
+				+ "s_max INTEGER, "
+				+ "sort_order INTEGER)";
+		statement.executeUpdate(sql);
+
+		// 默认参数与旧代码中硬编码的 A=40、B=42 场景保持一致。
+		ResultSet rs = statement.executeQuery("SELECT count(*) FROM bw_rivet_param");
+		if (rs.next() && rs.getInt(1) == 0) {
+			statement.executeUpdate("INSERT INTO bw_rivet_param (gun_type,start_rivet,end_rivet,f_min,f_max,s_min,s_max,sort_order) VALUES "
+					+ "('A',1,40,10000,12000,3000,4200,1),"
+					+ "('B',1,42,10000,12000,2000,3000,1)");
+		}
+		rs.close();
+		System.out.println("表rivet_param创建并初始化成功!");
+		statement.close();
+	}
+
+	public static List<RivetParamRange> getRivetParams(String gunType) {
+		List<RivetParamRange> list = new ArrayList<>();
+		// sort_order 用于保留设备管理面板中的配置顺序,start_rivet 作为兜底排序。
+		String sql = "SELECT gun_type,start_rivet,end_rivet,f_min,f_max,s_min,s_max FROM bw_rivet_param "
+				+ "WHERE gun_type = ? ORDER BY sort_order ASC, start_rivet ASC";
+		Connection conn = JdbcUtils.getConn();
+		try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
+			pstmt.setString(1, gunType);
+			ResultSet rs = pstmt.executeQuery();
+			while (rs.next()) {
+				RivetParamRange range = new RivetParamRange();
+				range.setGunType(rs.getString("gun_type"));
+				range.setStartRivet(rs.getInt("start_rivet"));
+				range.setEndRivet(rs.getInt("end_rivet"));
+				range.setFMin(rs.getInt("f_min"));
+				range.setFMax(rs.getInt("f_max"));
+				range.setSMin(rs.getInt("s_min"));
+				range.setSMax(rs.getInt("s_max"));
+				list.add(range);
+			}
+			rs.close();
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+		return list;
+	}
+
+	public static void saveRivetParams(String gunType, List<RivetParamRange> ranges) {
+		Connection conn = JdbcUtils.getConn();
+		try {
+			// 保存时按枪位整体替换,避免删除/插入行后残留旧区间。
+			PreparedStatement deleteStmt = conn.prepareStatement("DELETE FROM bw_rivet_param WHERE gun_type = ?");
+			deleteStmt.setString(1, gunType);
+			deleteStmt.executeUpdate();
+			deleteStmt.close();
+
+			String insertSql = "INSERT INTO bw_rivet_param (gun_type,start_rivet,end_rivet,f_min,f_max,s_min,s_max,sort_order) "
+					+ "VALUES (?,?,?,?,?,?,?,?)";
+			PreparedStatement insertStmt = conn.prepareStatement(insertSql);
+			for (int i = 0; i < ranges.size(); i++) {
+				RivetParamRange range = ranges.get(i);
+				insertStmt.setString(1, gunType);
+				insertStmt.setInt(2, range.getStartRivet());
+				insertStmt.setInt(3, range.getEndRivet());
+				insertStmt.setInt(4, range.getFMin());
+				insertStmt.setInt(5, range.getFMax());
+				insertStmt.setInt(6, range.getSMin());
+				insertStmt.setInt(7, range.getSMax());
+				insertStmt.setInt(8, i + 1);
+				insertStmt.executeUpdate();
+			}
+			insertStmt.close();
+			System.out.println("保存铆接参数成功:gunType=" + gunType + ", rows=" + ranges.size());
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+	}
+
+	public static void updateConfig(short aSetNum, short bSetNum, String plcIpA, String plcIpB) {
+		// bw_config 固定使用第一行作为当前设备配置。
+		String sql = "UPDATE bw_config SET a_set_num = ?, b_set_num = ?, plc_ip_a = ?, plc_ip_b = ? WHERE id = 1";
+		Connection conn = JdbcUtils.getConn();
+		try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
+			pstmt.setShort(1, aSetNum);
+			pstmt.setShort(2, bSetNum);
+			pstmt.setString(3, plcIpA);
+			pstmt.setString(4, plcIpB);
+			pstmt.executeUpdate();
+			System.out.println("更新本地配置成功:aSetNum=" + aSetNum + ", bSetNum=" + bSetNum + ", IP_A=" + plcIpA + ", IP_B=" + plcIpB);
+		} catch (SQLException e) {
+			e.printStackTrace();
+		}
+	}
     
     public static void close(){
-    	System.out.println("SQLite数据库连接关闭!");
+     	System.out.println("SQLite数据库连接关闭!");
         try {
         	if(conn!=null) {
         		conn.close();

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

@@ -1,4 +1,4 @@
-mes.gw=OP290A
+mes.gw=OP240A
 #mes.server_ip=127.0.0.1
 mes.server_ip=192.168.14.99
 mes.tcp_port=3000