|
@@ -0,0 +1,336 @@
|
|
|
|
|
+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;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 设备管理:IP、预设数量、按颗数区间的铆接参数配置
|
|
|
|
|
+ */
|
|
|
|
|
+public class DeviceManagePanel extends JPanel {
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ 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() {
|
|
|
|
|
+ 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"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 重新从数据库加载到界面(进入设备管理页时调用)
|
|
|
|
|
+ public void reloadFromDb() {
|
|
|
|
|
+ Map<String, Object> config = JdbcUtils.getConfig();
|
|
|
|
|
+ if (config.size() > 0) {
|
|
|
|
|
+ MesClient.aSetNum = (Short) config.get("a_set_num");
|
|
|
|
|
+ MesClient.bSetNum = (Short) config.get("b_set_num");
|
|
|
|
|
+ MesClient.curIpA = (String) config.get("plc_ip_a");
|
|
|
|
|
+ MesClient.curIpB = (String) config.get("plc_ip_b");
|
|
|
|
|
+ }
|
|
|
|
|
+ MesClient.loadRivetParamConfig();
|
|
|
|
|
+ loadConfigToUi();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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) throws IllegalArgumentException {
|
|
|
|
|
+ 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) + "行颗数范围无效");
|
|
|
|
|
+ }
|
|
|
|
|
+ int fMin = parseIntCell(model, i, 2);
|
|
|
|
|
+ int fMax = parseIntCell(model, i, 3);
|
|
|
|
|
+ int sMin = parseIntCell(model, i, 4);
|
|
|
|
|
+ int sMax = parseIntCell(model, i, 5);
|
|
|
|
|
+ if (fMin > fMax) {
|
|
|
|
|
+ throw new IllegalArgumentException(gunType + "枪第" + (i + 1) + "行 F-min 不能大于 F-max");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (sMin > sMax) {
|
|
|
|
|
+ throw new IllegalArgumentException(gunType + "枪第" + (i + 1) + "行 S-min 不能大于 S-max");
|
|
|
|
|
+ }
|
|
|
|
|
+ // PLC Int16 写入上限,避免溢出后下发错误值
|
|
|
|
|
+ if (fMin < Short.MIN_VALUE || fMax > Short.MAX_VALUE
|
|
|
|
|
+ || sMin < Short.MIN_VALUE || sMax > Short.MAX_VALUE) {
|
|
|
|
|
+ throw new IllegalArgumentException(gunType + "枪第" + (i + 1) + "行参数超出范围(-32768~32767)");
|
|
|
|
|
+ }
|
|
|
|
|
+ RivetParamRange range = new RivetParamRange();
|
|
|
|
|
+ range.setGunType(gunType);
|
|
|
|
|
+ range.setStartRivet(start);
|
|
|
|
|
+ range.setEndRivet(end);
|
|
|
|
|
+ range.setFMin(fMin);
|
|
|
|
|
+ range.setFMax(fMax);
|
|
|
|
|
+ range.setSMin(sMin);
|
|
|
|
|
+ range.setSMax(sMax);
|
|
|
|
|
+ ranges.add(range);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (ranges.isEmpty()) {
|
|
|
|
|
+ throw new IllegalArgumentException(gunType + "枪至少配置一行参数区间");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查区间是否重叠,重叠会导致下发时命中错误行
|
|
|
|
|
+ for (int i = 0; i < ranges.size(); i++) {
|
|
|
|
|
+ RivetParamRange a = ranges.get(i);
|
|
|
|
|
+ for (int j = i + 1; j < ranges.size(); j++) {
|
|
|
|
|
+ RivetParamRange b = ranges.get(j);
|
|
|
|
|
+ if (a.getStartRivet() <= b.getEndRivet() && b.getStartRivet() <= a.getEndRivet()) {
|
|
|
|
|
+ throw new IllegalArgumentException(gunType + "枪参数区间存在重叠:"
|
|
|
|
|
+ + a.getStartRivet() + "-" + a.getEndRivet()
|
|
|
|
|
+ + " 与 " + b.getStartRivet() + "-" + b.getEndRivet());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 提交表格编辑并将参数下发到PLC
|
|
|
|
|
+ private boolean pushRivetParamsToDevice(ModbusTcp plc, List<RivetParamRange> ranges, String gunType) {
|
|
|
|
|
+ if (plc == null) {
|
|
|
|
|
+ System.out.println(gunType + "枪PLC未连接,跳过参数下发");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ ModbusUtil.syncRivetParamsToPlc(plc, ranges);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ } catch (Exception ex) {
|
|
|
|
|
+ ex.printStackTrace();
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void saveAllConfig() {
|
|
|
|
|
+ // 保存前先结束表格编辑,确保最新输入写入模型
|
|
|
|
|
+ if (tableA != null && tableA.isEditing()) {
|
|
|
|
|
+ tableA.getCellEditor().stopCellEditing();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tableB != null && tableB.isEditing()) {
|
|
|
|
|
+ tableB.getCellEditor().stopCellEditing();
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ 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");
|
|
|
|
|
+
|
|
|
|
|
+ boolean configOk = JdbcUtils.updateConfig(newASet, newBSet, newIpA, newIpB);
|
|
|
|
|
+ boolean saveAOk = JdbcUtils.saveRivetParams("A", rangesA);
|
|
|
|
|
+ boolean saveBOk = JdbcUtils.saveRivetParams("B", rangesB);
|
|
|
|
|
+ if (!configOk || !saveAOk || !saveBOk) {
|
|
|
|
|
+ JOptionPane.showMessageDialog(MesClient.mesClientFrame,
|
|
|
|
|
+ "配置保存失败,请查看控制台日志后重试(常见原因:数据库被占用)",
|
|
|
|
|
+ "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
+ // 回读数据库,避免界面显示未真正落库的值
|
|
|
|
|
+ reloadFromDb();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ if (MesClient.plcA != null) {
|
|
|
|
|
+ MesClient.plcA.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ MesClient.curIpA = newIpA;
|
|
|
|
|
+ MesClient.plcA = new ModbusTcp(1, MesClient.curIpA);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (ipBChanged) {
|
|
|
|
|
+ if (MesClient.plcB != null) {
|
|
|
|
|
+ MesClient.plcB.close();
|
|
|
|
|
+ }
|
|
|
|
|
+ MesClient.curIpB = newIpB;
|
|
|
|
|
+ MesClient.plcB = new ModbusTcp(1, MesClient.curIpB);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存后立即将拉力/行程参数下发到设备
|
|
|
|
|
+ boolean syncA = pushRivetParamsToDevice(MesClient.plcA, rangesA, "A");
|
|
|
|
|
+ boolean syncB = pushRivetParamsToDevice(MesClient.plcB, rangesB, "B");
|
|
|
|
|
+ if (syncA && syncB) {
|
|
|
|
|
+ JOptionPane.showMessageDialog(MesClient.mesClientFrame, "配置已保存,并已下发到设备", "提示", JOptionPane.INFORMATION_MESSAGE);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ JOptionPane.showMessageDialog(MesClient.mesClientFrame,
|
|
|
|
|
+ "配置已保存到本地,但部分设备参数下发失败,请检查设备连接;下次开工时会按新配置重新下发",
|
|
|
|
|
+ "提示", JOptionPane.WARNING_MESSAGE);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
|
|
+ JOptionPane.showMessageDialog(MesClient.mesClientFrame, "请输入有效的数字", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
+ } catch (IllegalArgumentException ex) {
|
|
|
|
|
+ JOptionPane.showMessageDialog(MesClient.mesClientFrame, ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|