|
|
@@ -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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|