|
|
@@ -0,0 +1,455 @@
|
|
|
+package com.mes.ui;
|
|
|
+
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
+import com.mes.util.DateLocalUtils;
|
|
|
+import com.mes.util.JdbcUtils;
|
|
|
+import com.mes.util.ModbusWeightReader;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.swing.*;
|
|
|
+import javax.swing.border.LineBorder;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.event.ComponentAdapter;
|
|
|
+import java.awt.event.ComponentEvent;
|
|
|
+import java.sql.PreparedStatement;
|
|
|
+import java.sql.ResultSet;
|
|
|
+import java.sql.Statement;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
+import java.util.Timer;
|
|
|
+import java.util.TimerTask;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 称重提交面板
|
|
|
+ * 展示炉次、班次、实时净重、物料类型、当前时间
|
|
|
+ * 净重通过 Modbus-TCP 读取 Float 寄存器
|
|
|
+ */
|
|
|
+public class WeighSubmitPanel extends JPanel {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(WeighSubmitPanel.class);
|
|
|
+
|
|
|
+ public static final String MATERIAL_INGOT = "原铝锭";
|
|
|
+ public static final String MATERIAL_RECYCLE = "回炉料";
|
|
|
+
|
|
|
+ private JLabel furnaceLabel;
|
|
|
+ private JLabel furnaceValueLabel;
|
|
|
+ private JLabel shiftLabel;
|
|
|
+ private JLabel shiftValueLabel;
|
|
|
+ private JLabel weightLabel;
|
|
|
+ private JLabel weightValueLabel;
|
|
|
+ private JLabel materialLabel;
|
|
|
+ private JButton btnIngot;
|
|
|
+ private JButton btnRecycle;
|
|
|
+ private JLabel timeLabel;
|
|
|
+ private JLabel timeValueLabel;
|
|
|
+ private JButton btnSubmit;
|
|
|
+ private JLabel modbusStatusLabel;
|
|
|
+
|
|
|
+ private int furnaceNo = 1;
|
|
|
+ private String selectedMaterial = MATERIAL_INGOT;
|
|
|
+ private double netWeight = 0.0;
|
|
|
+ private Timer refreshTimer;
|
|
|
+ private Timer modbusTimer;
|
|
|
+ private ModbusWeightReader weightReader;
|
|
|
+ private final AtomicBoolean modbusReading = new AtomicBoolean(false);
|
|
|
+
|
|
|
+ private static final Color SELECTED_BG = new Color(46, 125, 50);
|
|
|
+ private static final Color SELECTED_FG = Color.WHITE;
|
|
|
+ private static final Color NORMAL_BG = new Color(236, 239, 241);
|
|
|
+ private static final Color NORMAL_FG = new Color(33, 33, 33);
|
|
|
+ private static final Color VALUE_COLOR = new Color(25, 118, 210);
|
|
|
+
|
|
|
+ public WeighSubmitPanel() {
|
|
|
+ initUI();
|
|
|
+ loadFurnaceNo();
|
|
|
+ updateShift();
|
|
|
+ updateMaterialButtons();
|
|
|
+ startRefreshTimer();
|
|
|
+ startModbusPolling();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initUI() {
|
|
|
+ setLayout(null);
|
|
|
+ setBackground(Color.WHITE);
|
|
|
+
|
|
|
+ Font labelFont = new Font("微软雅黑", Font.PLAIN, 22);
|
|
|
+ Font valueFont = new Font("微软雅黑", Font.BOLD, 28);
|
|
|
+ Font weightFont = new Font("微软雅黑", Font.BOLD, 48);
|
|
|
+ Font buttonFont = new Font("微软雅黑", Font.BOLD, 22);
|
|
|
+
|
|
|
+ int leftX = 80;
|
|
|
+ int labelW = 140;
|
|
|
+ int valueX = 230;
|
|
|
+ int valueW = 520;
|
|
|
+ int rowH = 70;
|
|
|
+ int y = 40;
|
|
|
+
|
|
|
+ // 炉次
|
|
|
+ furnaceLabel = new JLabel("炉次:");
|
|
|
+ furnaceLabel.setFont(labelFont);
|
|
|
+ furnaceLabel.setBounds(leftX, y, labelW, rowH);
|
|
|
+ add(furnaceLabel);
|
|
|
+
|
|
|
+ furnaceValueLabel = new JLabel(String.valueOf(furnaceNo));
|
|
|
+ furnaceValueLabel.setFont(valueFont);
|
|
|
+ furnaceValueLabel.setForeground(VALUE_COLOR);
|
|
|
+ furnaceValueLabel.setBounds(valueX, y, valueW, rowH);
|
|
|
+ add(furnaceValueLabel);
|
|
|
+ y += rowH + 10;
|
|
|
+
|
|
|
+ // 班次
|
|
|
+ shiftLabel = new JLabel("班次:");
|
|
|
+ shiftLabel.setFont(labelFont);
|
|
|
+ shiftLabel.setBounds(leftX, y, labelW, rowH);
|
|
|
+ add(shiftLabel);
|
|
|
+
|
|
|
+ shiftValueLabel = new JLabel("白班");
|
|
|
+ shiftValueLabel.setFont(valueFont);
|
|
|
+ shiftValueLabel.setForeground(VALUE_COLOR);
|
|
|
+ shiftValueLabel.setBounds(valueX, y, valueW, rowH);
|
|
|
+ add(shiftValueLabel);
|
|
|
+ y += rowH + 10;
|
|
|
+
|
|
|
+ // 实时净重
|
|
|
+ weightLabel = new JLabel("实时净重:");
|
|
|
+ weightLabel.setFont(labelFont);
|
|
|
+ weightLabel.setBounds(leftX, y, labelW + 20, rowH);
|
|
|
+ add(weightLabel);
|
|
|
+
|
|
|
+ weightValueLabel = new JLabel(formatWeight(netWeight));
|
|
|
+ weightValueLabel.setFont(weightFont);
|
|
|
+ weightValueLabel.setForeground(new Color(211, 47, 47));
|
|
|
+ weightValueLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
|
|
+ weightValueLabel.setBounds(valueX, y, valueW, rowH + 10);
|
|
|
+ add(weightValueLabel);
|
|
|
+ y += rowH + 10;
|
|
|
+
|
|
|
+ modbusStatusLabel = new JLabel("Modbus:连接中...");
|
|
|
+ modbusStatusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ modbusStatusLabel.setForeground(Color.GRAY);
|
|
|
+ modbusStatusLabel.setBounds(valueX, y, valueW, 30);
|
|
|
+ add(modbusStatusLabel);
|
|
|
+ y += 40;
|
|
|
+
|
|
|
+ // 物料类型
|
|
|
+ materialLabel = new JLabel("物料类型:");
|
|
|
+ materialLabel.setFont(labelFont);
|
|
|
+ materialLabel.setBounds(leftX, y, labelW + 20, rowH);
|
|
|
+ add(materialLabel);
|
|
|
+
|
|
|
+ btnIngot = new JButton(MATERIAL_INGOT);
|
|
|
+ btnIngot.setFont(buttonFont);
|
|
|
+ btnIngot.setFocusPainted(false);
|
|
|
+ btnIngot.setBounds(valueX, y + 8, 180, 55);
|
|
|
+ btnIngot.addActionListener(e -> {
|
|
|
+ selectedMaterial = MATERIAL_INGOT;
|
|
|
+ updateMaterialButtons();
|
|
|
+ });
|
|
|
+ add(btnIngot);
|
|
|
+
|
|
|
+ btnRecycle = new JButton(MATERIAL_RECYCLE);
|
|
|
+ btnRecycle.setFont(buttonFont);
|
|
|
+ btnRecycle.setFocusPainted(false);
|
|
|
+ btnRecycle.setBounds(valueX + 210, y + 8, 180, 55);
|
|
|
+ btnRecycle.addActionListener(e -> {
|
|
|
+ selectedMaterial = MATERIAL_RECYCLE;
|
|
|
+ updateMaterialButtons();
|
|
|
+ });
|
|
|
+ add(btnRecycle);
|
|
|
+ y += rowH + 30;
|
|
|
+
|
|
|
+ // 当前时间
|
|
|
+ timeLabel = new JLabel("当前时间:");
|
|
|
+ timeLabel.setFont(labelFont);
|
|
|
+ timeLabel.setBounds(leftX, y, labelW + 20, rowH);
|
|
|
+ add(timeLabel);
|
|
|
+
|
|
|
+ timeValueLabel = new JLabel(DateLocalUtils.getCurrentTime());
|
|
|
+ timeValueLabel.setFont(valueFont);
|
|
|
+ timeValueLabel.setForeground(VALUE_COLOR);
|
|
|
+ timeValueLabel.setBounds(valueX, y, valueW, rowH);
|
|
|
+ add(timeValueLabel);
|
|
|
+ y += rowH + 40;
|
|
|
+
|
|
|
+ // 提交按钮
|
|
|
+ btnSubmit = new JButton("称重提交");
|
|
|
+ btnSubmit.setFont(new Font("微软雅黑", Font.BOLD, 28));
|
|
|
+ btnSubmit.setBounds(valueX, y, 280, 70);
|
|
|
+ btnSubmit.setBackground(new Color(25, 118, 210));
|
|
|
+ btnSubmit.setForeground(Color.WHITE);
|
|
|
+ btnSubmit.setFocusPainted(false);
|
|
|
+ btnSubmit.setBorder(new LineBorder(new Color(21, 101, 192), 1));
|
|
|
+ btnSubmit.addActionListener(e -> onSubmit());
|
|
|
+ add(btnSubmit);
|
|
|
+
|
|
|
+ addComponentListener(new ComponentAdapter() {
|
|
|
+ @Override
|
|
|
+ public void componentResized(ComponentEvent e) {
|
|
|
+ // 保持绝对布局,工业触屏固定尺寸即可
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateMaterialButtons() {
|
|
|
+ boolean ingotSelected = MATERIAL_INGOT.equals(selectedMaterial);
|
|
|
+ styleMaterialButton(btnIngot, ingotSelected);
|
|
|
+ styleMaterialButton(btnRecycle, !ingotSelected);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void styleMaterialButton(JButton button, boolean selected) {
|
|
|
+ if (selected) {
|
|
|
+ button.setBackground(SELECTED_BG);
|
|
|
+ button.setForeground(SELECTED_FG);
|
|
|
+ button.setBorder(new LineBorder(SELECTED_BG, 2));
|
|
|
+ } else {
|
|
|
+ button.setBackground(NORMAL_BG);
|
|
|
+ button.setForeground(NORMAL_FG);
|
|
|
+ button.setBorder(new LineBorder(new Color(189, 189, 189), 1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 白班:当天 08:00 ~ 20:00;夜班:20:00 ~ 次日 08:00
|
|
|
+ */
|
|
|
+ public static String resolveShift(LocalDateTime now) {
|
|
|
+ LocalTime time = now.toLocalTime();
|
|
|
+ LocalTime dayStart = LocalTime.of(8, 0);
|
|
|
+ LocalTime nightStart = LocalTime.of(20, 0);
|
|
|
+ if (!time.isBefore(dayStart) && time.isBefore(nightStart)) {
|
|
|
+ return "白班";
|
|
|
+ }
|
|
|
+ return "夜班";
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateShift() {
|
|
|
+ shiftValueLabel.setText(resolveShift(LocalDateTime.now()));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void startRefreshTimer() {
|
|
|
+ if (refreshTimer != null) {
|
|
|
+ refreshTimer.cancel();
|
|
|
+ }
|
|
|
+ refreshTimer = new Timer("weigh-refresh", true);
|
|
|
+ refreshTimer.scheduleAtFixedRate(new TimerTask() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ SwingUtilities.invokeLater(() -> {
|
|
|
+ timeValueLabel.setText(DateLocalUtils.getCurrentTime());
|
|
|
+ updateShift();
|
|
|
+ // 跨天时自动重置炉次
|
|
|
+ ensureFurnaceForToday();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }, 0, 1000);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定时通过 Modbus-TCP 读取净重
|
|
|
+ */
|
|
|
+ private void startModbusPolling() {
|
|
|
+ weightReader = new ModbusWeightReader();
|
|
|
+ if (modbusTimer != null) {
|
|
|
+ modbusTimer.cancel();
|
|
|
+ }
|
|
|
+ modbusTimer = new Timer("modbus-weight", true);
|
|
|
+ modbusTimer.scheduleAtFixedRate(new TimerTask() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (!modbusReading.compareAndSet(false, true)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Float value = weightReader.readWeight();
|
|
|
+ SwingUtilities.invokeLater(() -> {
|
|
|
+ if (value != null) {
|
|
|
+ setNetWeight(value.doubleValue());
|
|
|
+ modbusStatusLabel.setText("Modbus:已连接 (" + weightReader.getEndpointDesc() + ")");
|
|
|
+ modbusStatusLabel.setForeground(new Color(46, 125, 50));
|
|
|
+ } else {
|
|
|
+ String err = weightReader.getLastError();
|
|
|
+ modbusStatusLabel.setText("Modbus:读取失败" + (err != null ? " - " + err : ""));
|
|
|
+ modbusStatusLabel.setForeground(Color.RED);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ modbusReading.set(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 500, 500);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String formatWeight(double weight) {
|
|
|
+ return String.format("%.2f kg", weight);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新实时净重显示
|
|
|
+ */
|
|
|
+ public void setNetWeight(double weight) {
|
|
|
+ this.netWeight = Math.round(weight * 100.0) / 100.0;
|
|
|
+ if (SwingUtilities.isEventDispatchThread()) {
|
|
|
+ weightValueLabel.setText(formatWeight(netWeight));
|
|
|
+ } else {
|
|
|
+ SwingUtilities.invokeLater(() -> weightValueLabel.setText(formatWeight(netWeight)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public double getNetWeight() {
|
|
|
+ return netWeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getFurnaceNo() {
|
|
|
+ return furnaceNo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getSelectedMaterial() {
|
|
|
+ return selectedMaterial;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getCurrentShift() {
|
|
|
+ return shiftValueLabel.getText();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void loadFurnaceNo() {
|
|
|
+ ensureFurnaceForToday();
|
|
|
+ furnaceValueLabel.setText(String.valueOf(furnaceNo));
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ensureFurnaceForToday() {
|
|
|
+ String today = LocalDate.now().toString();
|
|
|
+ try {
|
|
|
+ if (JdbcUtils.conn == null || JdbcUtils.conn.isClosed()) {
|
|
|
+ JdbcUtils.openConnection();
|
|
|
+ }
|
|
|
+ JdbcUtils.ensureFurnaceTable();
|
|
|
+
|
|
|
+ String selectSql = "SELECT furnace_no, record_date FROM furnace_counter ORDER BY id DESC LIMIT 1";
|
|
|
+ try (Statement statement = JdbcUtils.conn.createStatement();
|
|
|
+ ResultSet rs = statement.executeQuery(selectSql)) {
|
|
|
+ if (rs.next()) {
|
|
|
+ String recordDate = rs.getString("record_date");
|
|
|
+ int no = rs.getInt("furnace_no");
|
|
|
+ if (today.equals(recordDate)) {
|
|
|
+ furnaceNo = Math.max(no, 1);
|
|
|
+ } else {
|
|
|
+ // 新的一天,从 1 重新开始
|
|
|
+ furnaceNo = 1;
|
|
|
+ upsertFurnaceNo(today, furnaceNo);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ furnaceNo = 1;
|
|
|
+ upsertFurnaceNo(today, furnaceNo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ furnaceValueLabel.setText(String.valueOf(furnaceNo));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("读取炉次失败: {}", e.getMessage());
|
|
|
+ furnaceNo = 1;
|
|
|
+ furnaceValueLabel.setText("1");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void upsertFurnaceNo(String date, int no) throws Exception {
|
|
|
+ String deleteSql = "DELETE FROM furnace_counter";
|
|
|
+ String insertSql = "INSERT INTO furnace_counter (furnace_no, record_date) VALUES (?, ?)";
|
|
|
+ try (Statement del = JdbcUtils.conn.createStatement()) {
|
|
|
+ del.executeUpdate(deleteSql);
|
|
|
+ }
|
|
|
+ try (PreparedStatement ps = JdbcUtils.conn.prepareStatement(insertSql)) {
|
|
|
+ ps.setInt(1, no);
|
|
|
+ ps.setString(2, date);
|
|
|
+ ps.executeUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提交成功后炉次 +1(同一天内)
|
|
|
+ */
|
|
|
+ public void incrementFurnaceNo() {
|
|
|
+ try {
|
|
|
+ String today = LocalDate.now().toString();
|
|
|
+ furnaceNo = furnaceNo + 1;
|
|
|
+ upsertFurnaceNo(today, furnaceNo);
|
|
|
+ furnaceValueLabel.setText(String.valueOf(furnaceNo));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("更新炉次失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void onSubmit() {
|
|
|
+ if (selectedMaterial == null || selectedMaterial.isEmpty()) {
|
|
|
+ JOptionPane.showMessageDialog(this, "请选择物料类型", "提示", JOptionPane.WARNING_MESSAGE);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ final String shift = resolveShift(LocalDateTime.now());
|
|
|
+ final String time = DateLocalUtils.getCurrentTime();
|
|
|
+ final String lc = String.valueOf(furnaceNo);
|
|
|
+ final String type = selectedMaterial;
|
|
|
+ final String zl = String.format("%.2f", netWeight);
|
|
|
+ String msg = String.format(
|
|
|
+ "确认提交?\n炉次:%s\n班次:%s\n净重:%s\n物料:%s\n时间:%s",
|
|
|
+ lc, shift, formatWeight(netWeight), type, time
|
|
|
+ );
|
|
|
+ int choice = JOptionPane.showConfirmDialog(this, msg, "称重提交确认", JOptionPane.YES_NO_OPTION);
|
|
|
+ if (choice != JOptionPane.YES_OPTION) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ btnSubmit.setEnabled(false);
|
|
|
+ MesClient.setMenuStatus("正在提交称重数据...", 0);
|
|
|
+
|
|
|
+ new SwingWorker<JSONObject, Void>() {
|
|
|
+ @Override
|
|
|
+ protected JSONObject doInBackground() {
|
|
|
+ MesClient.getUser();
|
|
|
+ return DataUtil.submitWeigh(lc, shift, type, zl, MesClient.user20);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void done() {
|
|
|
+ btnSubmit.setEnabled(true);
|
|
|
+ try {
|
|
|
+ JSONObject retObj = get();
|
|
|
+ if (retObj != null && retObj.get("result") != null
|
|
|
+ && "true".equalsIgnoreCase(retObj.get("result").toString())) {
|
|
|
+ MesClient.setMenuStatus("称重提交成功", 0);
|
|
|
+ incrementFurnaceNo();
|
|
|
+ JOptionPane.showMessageDialog(WeighSubmitPanel.this,
|
|
|
+ "提交成功,下一炉次:" + furnaceNo, "提示", JOptionPane.INFORMATION_MESSAGE);
|
|
|
+ } else {
|
|
|
+ String errMsg = "请求失败,请重试";
|
|
|
+ if (retObj != null && retObj.getString("message") != null) {
|
|
|
+ errMsg = retObj.getString("message");
|
|
|
+ }
|
|
|
+ MesClient.setMenuStatus(errMsg, -1);
|
|
|
+ JOptionPane.showMessageDialog(WeighSubmitPanel.this, errMsg, "提交失败", JOptionPane.ERROR_MESSAGE);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("称重提交异常: {}", e.getMessage());
|
|
|
+ MesClient.setMenuStatus("提交异常,请重试", -1);
|
|
|
+ JOptionPane.showMessageDialog(WeighSubmitPanel.this,
|
|
|
+ "提交异常:" + e.getMessage(), "提交失败", JOptionPane.ERROR_MESSAGE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.execute();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void disposePanel() {
|
|
|
+ if (refreshTimer != null) {
|
|
|
+ refreshTimer.cancel();
|
|
|
+ refreshTimer = null;
|
|
|
+ }
|
|
|
+ if (modbusTimer != null) {
|
|
|
+ modbusTimer.cancel();
|
|
|
+ modbusTimer = null;
|
|
|
+ }
|
|
|
+ if (weightReader != null) {
|
|
|
+ weightReader.close();
|
|
|
+ weightReader = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|