sunxianglong 5 часов назад
Родитель
Сommit
61500fd022

+ 1 - 0
.classpath

@@ -21,4 +21,5 @@
 	<classpathentry kind="lib" path="lib/sqlite-jdbc-3.36.0.3.jar"/>
 	<classpathentry kind="lib" path="lib/commons-codec-1.15.jar"/>
 	<classpathentry kind="lib" path="lib/fastjson2-2.0.16.jar"/>
+	<classpathentry kind="lib" path="lib/iot-communication-1.4.4.jar"/>
 </classpath>

+ 77 - 31
src/com/mes/ui/DataUtil.java

@@ -214,61 +214,56 @@ public class DataUtil {
     }
 
     /**
-     * 查询工作记录
-     * @param oprno 工位号(空则查所有工位)
-     * @param sn 工件码(可选,用于搜索)
+     * 查询称重工作记录(后台 mesWuliaoCz/pclist)
+     * @param lc 炉次(可选)
+     * @param type 物料类型(可选)
      * @param pageNo 页码
      * @param pageSize 每页条数
-     * @return 工作记录响应
      */
-    public static WorkRecordResp getWorkRecordList(String oprno, String sn, int pageNo, int pageSize) {
+    public static WorkRecordResp getWeighRecordList(String lc, String type, int pageNo, int pageSize) {
         WorkRecordResp resp = new WorkRecordResp();
         try {
-            String enconding = "UTF-8";
+            String encoding = "UTF-8";
             InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
             Properties pro = new Properties();
-            BufferedReader br = new BufferedReader(new InputStreamReader(is, enconding));
+            BufferedReader br = new BufferedReader(new InputStreamReader(is, encoding));
             pro.load(br);
             String mes_server_ip = pro.getProperty("mes.server_ip");
-            String lineSn = pro.getProperty("mes.line_sn").trim();
-            
-            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesProductRecord/workData";
+
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesWuliaoCz/pclist";
             StringBuilder params = new StringBuilder();
             params.append("__ajax=json");
-            params.append("&lineSn=").append(lineSn);
             params.append("&pageNo=").append(pageNo);
             params.append("&pageSize=").append(pageSize);
-            
-            if (oprno != null && !oprno.isEmpty()) {
-                params.append("&oprno=").append(oprno);
+            if (lc != null && !lc.isEmpty()) {
+                params.append("&lc=").append(URLEncoder.encode(lc, encoding));
             }
-            // 注意:不传auto参数,这样"所有工位"查询时会返回整条产线所有工位的数据
-            if (sn != null && !sn.isEmpty()) {
-                params.append("&sn=").append(sn);
+            if (type != null && !type.isEmpty()) {
+                params.append("&type=").append(URLEncoder.encode(type, encoding));
             }
-            
-            log.info("查询工作记录: url=" + url + ", params=" + params.toString());
+
+            log.info("查询称重记录: url={}, params={}", url, params);
             String result = doPost(url, params.toString());
-            log.info("查询工作记录结果: result=" + result);
-            
+            log.info("查询称重记录结果: {}", result);
+
             if (result == null || result.trim().isEmpty()) {
                 resp.setResult(false);
                 resp.setMessage("请求返回空结果");
                 return resp;
             }
-            
+
             JSONObject jsonObj = JSONObject.parseObject(result);
             if (jsonObj == null) {
                 resp.setResult(false);
                 resp.setMessage("解析响应失败");
                 return resp;
             }
-            
+
             resp.setResult(true);
             resp.setPageNo(jsonObj.getIntValue("pageNo"));
             resp.setPageSize(jsonObj.getIntValue("pageSize"));
             resp.setCount(jsonObj.getLongValue("count"));
-            
+
             List<WorkRecordData> list = new ArrayList<>();
             JSONArray listArray = jsonObj.getJSONArray("list");
             if (listArray != null) {
@@ -276,22 +271,73 @@ public class DataUtil {
                     JSONObject item = listArray.getJSONObject(i);
                     WorkRecordData data = new WorkRecordData();
                     data.setId(item.getString("id"));
-                    data.setSn(item.getString("sn"));
-                    data.setOprno(item.getString("oprno"));
-                    data.setUpdateBy(item.getString("updateBy"));
-                    data.setUpdateDate(item.getString("updateDate"));
-                    data.setContent(item.getString("content"));
+                    data.setLc(item.getString("lc"));
+                    data.setBc(item.getString("bc"));
+                    data.setType(item.getString("type"));
+                    data.setZl(item.getString("zl"));
+                    // 操作人:优先 remarks,其次 createBy.userName/userCode
+                    String updateBy = item.getString("remarks");
+                    if (updateBy == null || updateBy.isEmpty()) {
+                        Object createBy = item.get("createBy");
+                        if (createBy instanceof JSONObject) {
+                            JSONObject cb = (JSONObject) createBy;
+                            updateBy = cb.getString("userName");
+                            if (updateBy == null || updateBy.isEmpty()) {
+                                updateBy = cb.getString("userCode");
+                            }
+                        }
+                    }
+                    data.setUpdateBy(updateBy);
+                    data.setCreateDate(item.getString("createDate"));
                     list.add(data);
                 }
             }
             resp.setList(list);
-            
         } catch (Exception e) {
-            log.error("查询工作记录异常: " + e.getMessage());
+            log.error("查询称重记录异常: {}", e.getMessage());
             e.printStackTrace();
             resp.setResult(false);
             resp.setMessage("查询异常: " + e.getMessage());
         }
         return resp;
     }
+
+    /**
+     * 称重数据提交到服务器
+     * @param lc 炉次
+     * @param bc 班次
+     * @param type 物料类型
+     * @param zl 重量
+     * @param user 操作人
+     */
+    public static JSONObject submitWeigh(String lc, String bc, String type, String zl, String user) {
+        try {
+            String encoding = "UTF-8";
+            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
+            Properties pro = new Properties();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is, encoding));
+            pro.load(br);
+            String mes_server_ip = pro.getProperty("mes.server_ip");
+            String url = "http://" + mes_server_ip + ":8980/js/a/mes/mesWuliaoCz/pcsubmit";
+            StringBuilder params = new StringBuilder();
+            params.append("__ajax=json");
+            params.append("&lc=").append(lc == null ? "" : URLEncoder.encode(lc, encoding));
+            params.append("&bc=").append(bc == null ? "" : URLEncoder.encode(bc, encoding));
+            params.append("&type=").append(type == null ? "" : URLEncoder.encode(type, encoding));
+            params.append("&zl=").append(zl == null ? "" : URLEncoder.encode(zl, encoding));
+            if (user != null && !user.isEmpty()) {
+                params.append("&ucode=").append(URLEncoder.encode(user, encoding));
+            }
+            log.info("称重提交 params=" + params);
+            String result = doPost(url, params.toString());
+            log.info("称重提交 result=" + result);
+            if (result == null || result.equalsIgnoreCase("false")) {
+                return null;
+            }
+            return JSONObject.parseObject(result);
+        } catch (Exception e) {
+            log.info("称重提交异常 e=" + e.getMessage());
+            return null;
+        }
+    }
 }

+ 26 - 41
src/com/mes/ui/MesClient.java

@@ -45,6 +45,7 @@ public class MesClient extends JFrame {
     public static JScrollPane indexScrollPaneA;
     // public static JScrollPane searchScrollPane; // 已弃用,改为使用 WorkRecordPanel
     public static JScrollPane searchScrollPaneDj;
+    public static WeighSubmitPanel weighSubmitPanel;
 
 
     public static Boolean check_quality_result = false;
@@ -453,6 +454,16 @@ public class MesClient extends JFrame {
         tabbedPane.setFont(new Font("宋体", Font.BOLD, 22));
         contentPane.add(tabbedPane);
 
+        // 称重提交面板(首页)
+        weighSubmitPanel = new WeighSubmitPanel();
+        JScrollPane weighScrollPane = new JScrollPane(weighSubmitPanel);
+        tabbedPane.addTab("称重提交", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), weighScrollPane, null);
+
+        // Modbus 称重读取测试页(暂不展示)
+        // ModbusWeightTestPanel modbusWeightTestPanel = new ModbusWeightTestPanel();
+        // JScrollPane modbusTestScrollPane = new JScrollPane(modbusWeightTestPanel);
+        // tabbedPane.addTab("称重测试", new ImageIcon(MesClient.class.getResource("/bg/menu_setting.png")), modbusTestScrollPane, null);
+
         //首页
         JPanel indexPanelA = new JPanel();
         indexScrollPaneA = new JScrollPane(indexPanelA);
@@ -565,8 +576,9 @@ public class MesClient extends JFrame {
         finish_ng_bt.setEnabled(false);
         indexPanelA.add(finish_ng_bt);
 
-        tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
-        tabbedPane.setEnabledAt(0, true);
+        // 工作面板、开班点检暂不展示
+        // tabbedPane.addTab("工作面板", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), indexScrollPaneA, null);
+        // tabbedPane.setEnabledAt(0, true);
 
 //		searchScrollPane = new JScrollPane((Component) null);
 
@@ -574,52 +586,25 @@ public class MesClient extends JFrame {
         searchScrollPaneDj = new JScrollPane(indexPanelC);
         indexPanelC.setLayout(null);
 
-        tabbedPane.addTab("开班点检", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPaneDj, null);
-
-
-        // ========== 工作记录 Tab(改造:当前工位Tab + 所有工位Tab) ==========
-        // 旧代码已注释:
-        // indexPanelB = new JPanel();
-        // searchScrollPane = new JScrollPane(indexPanelB);
-        // indexPanelB.setLayout(null);
-        // tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPane, null);
+        // tabbedPane.addTab("开班点检", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), searchScrollPaneDj, null);
 
-        // 创建工作记录的子 TabbedPane
-        JTabbedPane workRecordTabbedPane = new JTabbedPane(JTabbedPane.TOP);
-        workRecordTabbedPane.setFont(new Font("微软雅黑", Font.PLAIN, 14));
-
-        // OKNG 工位:当前工位 + 所有工位
-        WorkRecordPanel[] workRecordPanels = new WorkRecordPanel[2];
-        
-        // 当前工位Tab(使用配置文件中的 mes.gw)
-        workRecordPanels[0] = new WorkRecordPanel(mes_gw, mes_line_sn);
-        workRecordTabbedPane.addTab(mes_gw, null, workRecordPanels[0], "查询" + mes_gw + "的工作记录");
-
-        // 所有工位Tab(查询整条产线所有工位)
-        workRecordPanels[1] = new WorkRecordPanel(null, mes_line_sn);
-        workRecordTabbedPane.addTab("所有工位", null, workRecordPanels[1], "查询整条产线所有工位的工作记录");
-
-        // 切换子Tab时自动刷新数据
-        workRecordTabbedPane.addChangeListener(e -> {
-            int selectedIndex = workRecordTabbedPane.getSelectedIndex();
-            if (selectedIndex >= 0 && selectedIndex < workRecordPanels.length) {
-                workRecordPanels[selectedIndex].refreshData();
-            }
-        });
-
-        // 将子 TabbedPane 添加到主 TabbedPane
-        tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), workRecordTabbedPane, null);
 
+        // ========== 工作记录:称重数据列表 ==========
+        WorkRecordPanel workRecordPanel = new WorkRecordPanel();
+        tabbedPane.addTab("工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), workRecordPanel, null);
 
+        // 切换到工作记录时自动刷新
 		tabbedPane.addChangeListener(new ChangeListener() {
             @Override
             public void stateChanged(ChangeEvent e) {
-                JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
-                int selectedIndex = tabbedPane.getSelectedIndex();
+                JTabbedPane pane = (JTabbedPane) e.getSource();
+                int selectedIndex = pane.getSelectedIndex();
                 System.out.println("selectedIndex:"+selectedIndex);
-
-                if(selectedIndex == 1){
-
+                if (selectedIndex >= 0) {
+                    String title = pane.getTitleAt(selectedIndex);
+                    if ("工作记录".equals(title)) {
+                        workRecordPanel.refreshData();
+                    }
                 }
             }
         });

+ 180 - 0
src/com/mes/ui/ModbusWeightTestPanel.java

@@ -0,0 +1,180 @@
+package com.mes.ui;
+
+import com.mes.util.DateLocalUtils;
+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.util.Map;
+
+/**
+ * Modbus 称重读取测试页:功能码 0x03 + Float,并展示原始字节与 4 种字节序结果
+ */
+public class ModbusWeightTestPanel extends JPanel {
+
+    private static final Logger log = LoggerFactory.getLogger(ModbusWeightTestPanel.class);
+
+    private final JLabel weightValueLabel;
+    private final JLabel statusLabel;
+    private final JLabel configLabel;
+    private final JButton btnRead;
+    private final JTextArea logArea;
+
+    private ModbusWeightReader weightReader;
+
+    public ModbusWeightTestPanel() {
+        setLayout(null);
+        setBackground(Color.WHITE);
+
+        Font titleFont = new Font("微软雅黑", Font.BOLD, 26);
+        Font valueFont = new Font("微软雅黑", Font.BOLD, 56);
+        Font normalFont = new Font("微软雅黑", Font.PLAIN, 16);
+        Font buttonFont = new Font("微软雅黑", Font.BOLD, 24);
+
+        JLabel title = new JLabel("Modbus 称重读取测试(0x03 / Float)");
+        title.setFont(titleFont);
+        title.setBounds(60, 30, 700, 40);
+        add(title);
+
+        weightReader = new ModbusWeightReader();
+        configLabel = new JLabel("连接:" + weightReader.getEndpointDesc() + "  功能码:0x03  类型:Float");
+        configLabel.setFont(normalFont);
+        configLabel.setForeground(new Color(97, 97, 97));
+        configLabel.setBounds(60, 80, 900, 30);
+        add(configLabel);
+
+        JLabel tip = new JLabel("若显示 -0.00,多半是字节序不对;请看下方 4 种解析,哪个接近真实重量就选哪个");
+        tip.setFont(new Font("微软雅黑", Font.PLAIN, 14));
+        tip.setForeground(new Color(230, 81, 0));
+        tip.setBounds(60, 110, 900, 25);
+        add(tip);
+
+        JLabel weightTitle = new JLabel("当前重量:");
+        weightTitle.setFont(new Font("微软雅黑", Font.PLAIN, 22));
+        weightTitle.setBounds(60, 150, 140, 50);
+        add(weightTitle);
+
+        weightValueLabel = new JLabel("--.-- kg");
+        weightValueLabel.setFont(valueFont);
+        weightValueLabel.setForeground(new Color(211, 47, 47));
+        weightValueLabel.setBounds(200, 140, 500, 70);
+        add(weightValueLabel);
+
+        statusLabel = new JLabel("状态:等待读取");
+        statusLabel.setFont(normalFont);
+        statusLabel.setForeground(Color.GRAY);
+        statusLabel.setBounds(60, 220, 900, 30);
+        add(statusLabel);
+
+        btnRead = new JButton("读取重量");
+        btnRead.setFont(buttonFont);
+        btnRead.setBounds(60, 270, 220, 70);
+        btnRead.setBackground(new Color(25, 118, 210));
+        btnRead.setForeground(Color.WHITE);
+        btnRead.setFocusPainted(false);
+        btnRead.setBorder(new LineBorder(new Color(21, 101, 192), 1));
+        btnRead.addActionListener(e -> readOnce());
+        add(btnRead);
+
+        JLabel logTitle = new JLabel("读取日志(含原始字节 / 四种字节序):");
+        logTitle.setFont(normalFont);
+        logTitle.setBounds(60, 360, 500, 30);
+        add(logTitle);
+
+        logArea = new JTextArea();
+        logArea.setEditable(false);
+        logArea.setFont(new Font("Consolas", Font.PLAIN, 14));
+        logArea.setLineWrap(true);
+        JScrollPane scrollPane = new JScrollPane(logArea);
+        scrollPane.setBounds(60, 395, 900, 220);
+        add(scrollPane);
+
+        JButton btnClear = new JButton("清空日志");
+        btnClear.setFont(new Font("微软雅黑", Font.PLAIN, 18));
+        btnClear.setBounds(300, 285, 140, 50);
+        btnClear.addActionListener(e -> logArea.setText(""));
+        add(btnClear);
+    }
+
+    private void readOnce() {
+        btnRead.setEnabled(false);
+        statusLabel.setText("状态:读取中(FC=0x03, Float)...");
+        statusLabel.setForeground(new Color(25, 118, 210));
+
+        new SwingWorker<Object[], Void>() {
+            @Override
+            protected Object[] doInBackground() {
+                if (weightReader == null) {
+                    weightReader = new ModbusWeightReader();
+                }
+                byte[] raw = weightReader.readRawBytes();
+                Float value = weightReader.readWeight();
+                return new Object[]{raw, value};
+            }
+
+            @Override
+            protected void done() {
+                btnRead.setEnabled(true);
+                try {
+                    Object[] result = get();
+                    byte[] raw = (byte[]) result[0];
+                    Float value = (Float) result[1];
+                    String time = DateLocalUtils.getCurrentTime();
+
+                    if (raw != null) {
+                        String hex = ModbusWeightReader.toHex(raw);
+                        Map<String, Float> all = ModbusWeightReader.decodeAllFormats(raw);
+                        StringBuilder sb = new StringBuilder();
+                        sb.append(time).append("  RAW=").append(hex);
+                        for (Map.Entry<String, Float> e : all.entrySet()) {
+                            sb.append("\n    ").append(e.getKey()).append(" = ")
+                                    .append(String.format("%.2f", e.getValue()));
+                        }
+                        appendLog(sb.toString());
+                    }
+
+                    if (value != null) {
+                        String text = String.format("%.2f kg", value);
+                        weightValueLabel.setText(text);
+                        statusLabel.setText("状态:读取成功(当前配置字节序 " + weightReader.getByteFormat() + ")");
+                        statusLabel.setForeground(new Color(46, 125, 50));
+                        appendLog("  => 采用配置 " + weightReader.getByteFormat() + " => " + text);
+                    } else {
+                        String err = weightReader.getLastError();
+                        if (err == null || err.isEmpty()) {
+                            err = "未知错误";
+                        }
+                        weightValueLabel.setText("--.-- kg");
+                        statusLabel.setText("状态:读取失败 - " + err);
+                        statusLabel.setForeground(Color.RED);
+                        appendLog(time + "  失败  " + err);
+                    }
+                } catch (Exception e) {
+                    log.error("测试读取异常: {}", e.getMessage());
+                    statusLabel.setText("状态:异常 - " + e.getMessage());
+                    statusLabel.setForeground(Color.RED);
+                    appendLog(DateLocalUtils.getCurrentTime() + "  异常  " + e.getMessage());
+                }
+            }
+        }.execute();
+    }
+
+    private void appendLog(String line) {
+        if (logArea.getText().isEmpty()) {
+            logArea.setText(line);
+        } else {
+            logArea.append("\n" + line);
+        }
+        logArea.setCaretPosition(logArea.getDocument().getLength());
+    }
+
+    public void disposePanel() {
+        if (weightReader != null) {
+            weightReader.close();
+            weightReader = null;
+        }
+    }
+}

+ 455 - 0
src/com/mes/ui/WeighSubmitPanel.java

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

+ 35 - 26
src/com/mes/ui/WorkRecordData.java

@@ -1,15 +1,16 @@
 package com.mes.ui;
 
 /**
- * 工作记录数据实体类
+ * 称重工作记录数据
  */
 public class WorkRecordData {
     private String id;
-    private String sn;          // 工件码
-    private String oprno;       // 工位号
-    private String updateBy;    // 作业人员
-    private String updateDate;  // 日期
-    private String content;     // 工位结果
+    private String lc;          // 炉次
+    private String bc;          // 班次
+    private String type;        // 物料类型
+    private String zl;          // 重量
+    private String updateBy;    // 操作人(remarks/ucode)
+    private String createDate;  // 创建时间
 
     public String getId() {
         return id;
@@ -19,43 +20,51 @@ public class WorkRecordData {
         this.id = id;
     }
 
-    public String getSn() {
-        return sn;
+    public String getLc() {
+        return lc;
     }
 
-    public void setSn(String sn) {
-        this.sn = sn;
+    public void setLc(String lc) {
+        this.lc = lc;
     }
 
-    public String getOprno() {
-        return oprno;
+    public String getBc() {
+        return bc;
     }
 
-    public void setOprno(String oprno) {
-        this.oprno = oprno;
+    public void setBc(String bc) {
+        this.bc = bc;
     }
 
-    public String getUpdateBy() {
-        return updateBy;
+    public String getType() {
+        return type;
     }
 
-    public void setUpdateBy(String updateBy) {
-        this.updateBy = updateBy;
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getZl() {
+        return zl;
     }
 
-    public String getUpdateDate() {
-        return updateDate;
+    public void setZl(String zl) {
+        this.zl = zl;
     }
 
-    public void setUpdateDate(String updateDate) {
-        this.updateDate = updateDate;
+    public String getUpdateBy() {
+        return updateBy;
+    }
+
+    public void setUpdateBy(String updateBy) {
+        this.updateBy = updateBy;
     }
 
-    public String getContent() {
-        return content;
+    public String getCreateDate() {
+        return createDate;
     }
 
-    public void setContent(String content) {
-        this.content = content;
+    public void setCreateDate(String createDate) {
+        this.createDate = createDate;
     }
 }

+ 66 - 186
src/com/mes/ui/WorkRecordPanel.java

@@ -13,30 +13,19 @@ import java.awt.event.ComponentEvent;
 import java.util.List;
 
 /**
- * 工作记录面板组件
- * 支持分页查询和展示工作记录数据
+ * 称重工作记录面板:从后台 mesWuliaoCz/pclist 拉取数据
  */
 public class WorkRecordPanel extends JPanel {
 
     private static final Logger log = LoggerFactory.getLogger(WorkRecordPanel.class);
 
-    // 表格列名
-    private static final String[] COLUMN_NAMES = {"工件码", "工位号", "工位结果", "作业人员", "日期"};
-
-    // 分页配置
+    private static final String[] COLUMN_NAMES = {"炉次", "班次", "物料类型", "重量(kg)", "操作人", "时间"};
     private static final int PAGE_SIZE = 20;
 
-    // 查询条件
-    private String oprno;  // 工位号(null或空则查全部)
-    private String lineSn; // 产线编号
-    private boolean isAllStation; // 是否是"所有工位"模式
-
-    // 分页状态
     private int pageNo = 1;
     private long totalCount = 0;
     private int totalPages = 0;
 
-    // UI组件
     private JTable table;
     private DefaultTableModel tableModel;
     private JLabel pageLabel;
@@ -45,31 +34,22 @@ public class WorkRecordPanel extends JPanel {
     private JButton btnRefresh;
     private JButton btnSearch;
     private JLabel statusLabel;
-    private JTextField snSearchField;      // 工件码搜索框
-    private JTextField oprnoSearchField;   // 工位号搜索框(仅"所有工位"模式)
-    private JLabel snLabel;
-    private JLabel oprnoLabel;
-
-    /**
-     * 构造函数
-     * @param oprno 工位号(如OP150A查询该工位,null或空则查所有工位)
-     * @param lineSn 产线编号
-     */
-    public WorkRecordPanel(String oprno, String lineSn) {
-        this.oprno = oprno;
-        this.lineSn = lineSn;
-        this.isAllStation = (oprno == null || oprno.isEmpty());
+    private JTextField lcSearchField;
+    private JTextField typeSearchField;
+
+    public WorkRecordPanel() {
         initUI();
     }
 
-    /**
-     * 初始化UI
-     */
+    /** 兼容旧构造:忽略工位参数,统一查询称重记录 */
+    public WorkRecordPanel(String oprno, String lineSn) {
+        this();
+    }
+
     private void initUI() {
         setLayout(null);
         setBackground(Color.WHITE);
 
-        // 顶部控制区
         JPanel topPanel = new JPanel();
         topPanel.setLayout(null);
         topPanel.setBounds(0, 0, 1200, 50);
@@ -78,45 +58,38 @@ public class WorkRecordPanel extends JPanel {
 
         int xPos = 10;
 
-        // 状态标签
-        String displayOprno = isAllStation ? "所有工位" : oprno;
-        statusLabel = new JLabel("工位:" + displayOprno);
+        statusLabel = new JLabel("称重记录");
         statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
-        statusLabel.setBounds(xPos, 10, 160, 30);
+        statusLabel.setBounds(xPos, 10, 140, 30);
         topPanel.add(statusLabel);
-        xPos += 165;
-
-        // 工件码搜索
-        snLabel = new JLabel("工件码:");
-        snLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
-        snLabel.setBounds(xPos, 10, 50, 30);
-        topPanel.add(snLabel);
-        xPos += 52;
-
-        snSearchField = new JTextField();
-        snSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
-        snSearchField.setBounds(xPos, 10, 200, 30);
-        snSearchField.addActionListener(e -> doSearch()); // 回车搜索
-        topPanel.add(snSearchField);
-        xPos += 210;
-
-        // 工位号搜索(仅"所有工位"模式显示)
-        if (isAllStation) {
-            oprnoLabel = new JLabel("工位:");
-            oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
-            oprnoLabel.setBounds(xPos, 10, 40, 30);
-            topPanel.add(oprnoLabel);
-            xPos += 42;
-
-            oprnoSearchField = new JTextField();
-            oprnoSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
-            oprnoSearchField.setBounds(xPos, 10, 100, 30);
-            oprnoSearchField.addActionListener(e -> doSearch()); // 回车搜索
-            topPanel.add(oprnoSearchField);
-            xPos += 110;
-        }
+        xPos += 145;
+
+        JLabel lcLabel = new JLabel("炉次:");
+        lcLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        lcLabel.setBounds(xPos, 10, 40, 30);
+        topPanel.add(lcLabel);
+        xPos += 42;
+
+        lcSearchField = new JTextField();
+        lcSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        lcSearchField.setBounds(xPos, 10, 80, 30);
+        lcSearchField.addActionListener(e -> doSearch());
+        topPanel.add(lcSearchField);
+        xPos += 90;
+
+        JLabel typeLabel = new JLabel("物料:");
+        typeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        typeLabel.setBounds(xPos, 10, 40, 30);
+        topPanel.add(typeLabel);
+        xPos += 42;
+
+        typeSearchField = new JTextField();
+        typeSearchField.setFont(new Font("微软雅黑", Font.PLAIN, 13));
+        typeSearchField.setBounds(xPos, 10, 120, 30);
+        typeSearchField.addActionListener(e -> doSearch());
+        topPanel.add(typeSearchField);
+        xPos += 130;
 
-        // 查询按钮
         btnSearch = new JButton("查询");
         btnSearch.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         btnSearch.setBounds(xPos, 10, 70, 30);
@@ -124,7 +97,6 @@ public class WorkRecordPanel extends JPanel {
         topPanel.add(btnSearch);
         xPos += 80;
 
-        // 刷新按钮
         btnRefresh = new JButton("刷新");
         btnRefresh.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         btnRefresh.setBounds(xPos, 10, 70, 30);
@@ -135,7 +107,6 @@ public class WorkRecordPanel extends JPanel {
         topPanel.add(btnRefresh);
         xPos += 80;
 
-        // 上一页按钮
         btnPrevious = new JButton("上一页");
         btnPrevious.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         btnPrevious.setBounds(xPos, 10, 80, 30);
@@ -148,7 +119,6 @@ public class WorkRecordPanel extends JPanel {
         topPanel.add(btnPrevious);
         xPos += 85;
 
-        // 页码显示
         pageLabel = new JLabel("0/0");
         pageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         pageLabel.setHorizontalAlignment(SwingConstants.CENTER);
@@ -157,7 +127,6 @@ public class WorkRecordPanel extends JPanel {
         topPanel.add(pageLabel);
         xPos += 75;
 
-        // 下一页按钮
         btnNext = new JButton("下一页");
         btnNext.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         btnNext.setBounds(xPos, 10, 80, 30);
@@ -169,13 +138,11 @@ public class WorkRecordPanel extends JPanel {
         });
         topPanel.add(btnNext);
 
-        // 表格面板
         JPanel tablePanel = new JPanel();
         tablePanel.setBounds(0, 50, 1200, 518);
         tablePanel.setLayout(new GridLayout(1, 1, 0, 0));
         add(tablePanel);
 
-        // 创建表格模型
         tableModel = new DefaultTableModel(COLUMN_NAMES, 0) {
             @Override
             public boolean isCellEditable(int row, int column) {
@@ -183,136 +150,79 @@ public class WorkRecordPanel extends JPanel {
             }
         };
 
-        // 创建表格
         table = new JTable(tableModel);
         table.setRowHeight(32);
         table.setFont(new Font("微软雅黑", Font.PLAIN, 13));
         table.getTableHeader().setFont(new Font("微软雅黑", Font.BOLD, 14));
         table.getTableHeader().setReorderingAllowed(false);
-
-        // 设置列宽
         setColumnWidths();
 
-        // 设置居中渲染器
         DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
         centerRenderer.setHorizontalAlignment(JLabel.CENTER);
         for (int i = 0; i < table.getColumnCount(); i++) {
             table.getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
         }
 
-        // 添加滚动面板
-        JScrollPane scrollPane = new JScrollPane(table);
-        tablePanel.add(scrollPane);
+        tablePanel.add(new JScrollPane(table));
 
-        // 添加组件大小变化监听器
         addComponentListener(new ComponentAdapter() {
             @Override
             public void componentResized(ComponentEvent e) {
                 int w = getWidth();
                 int h = getHeight();
-
-                // 调整顶部面板宽度
                 topPanel.setBounds(0, 0, w, 50);
-
-                // 调整表格面板
                 if (h > 50) {
                     tablePanel.setBounds(0, 50, w, h - 50);
                 }
-
                 topPanel.revalidate();
                 tablePanel.revalidate();
             }
         });
     }
 
-    /**
-     * 设置列宽
-     */
     private void setColumnWidths() {
-        TableColumn column;
-        // 工件码
-        column = table.getColumnModel().getColumn(0);
-        column.setPreferredWidth(260);
-        // 工位号
-        column = table.getColumnModel().getColumn(1);
-        column.setPreferredWidth(90);
-        // 工位结果
-        column = table.getColumnModel().getColumn(2);
-        column.setPreferredWidth(70);
-        // 作业人员
-        column = table.getColumnModel().getColumn(3);
-        column.setPreferredWidth(100);
-        // 日期
-        column = table.getColumnModel().getColumn(4);
-        column.setPreferredWidth(150);
+        int[] widths = {80, 80, 120, 100, 120, 180};
+        for (int i = 0; i < widths.length && i < table.getColumnCount(); i++) {
+            TableColumn column = table.getColumnModel().getColumn(i);
+            column.setPreferredWidth(widths[i]);
+        }
     }
 
-    /**
-     * 清空搜索框
-     */
     private void clearSearchFields() {
-        snSearchField.setText("");
-        if (oprnoSearchField != null) {
-            oprnoSearchField.setText("");
-        }
+        lcSearchField.setText("");
+        typeSearchField.setText("");
     }
 
-    /**
-     * 执行搜索
-     */
     private void doSearch() {
         pageNo = 1;
         loadData();
     }
 
-    /**
-     * 刷新数据(重置到第一页)
-     */
     public void refreshData() {
         pageNo = 1;
         loadData();
     }
 
-    /**
-     * 加载数据
-     */
     public void loadData() {
-        // 获取搜索条件
-        String searchSn = snSearchField.getText().trim();
-        String searchOprno = oprno; // 默认使用构造函数传入的工位号
-
-        // 如果是"所有工位"模式,检查是否有工位号搜索条件
-        if (isAllStation && oprnoSearchField != null) {
-            String inputOprno = oprnoSearchField.getText().trim();
-            if (!inputOprno.isEmpty()) {
-                searchOprno = inputOprno;
-            }
-        }
-
-        final String finalSearchOprno = searchOprno;
-        final String finalSearchSn = searchSn;
+        final String lc = lcSearchField.getText().trim();
+        final String type = typeSearchField.getText().trim();
 
-        // 在后台线程中执行数据加载
         SwingWorker<WorkRecordResp, Void> worker = new SwingWorker<WorkRecordResp, Void>() {
             @Override
-            protected WorkRecordResp doInBackground() throws Exception {
-                // 更新状态
+            protected WorkRecordResp doInBackground() {
                 SwingUtilities.invokeLater(() -> {
                     statusLabel.setText("正在加载...");
                     setButtonsEnabled(false);
                 });
-
-                // 调用接口查询数据
-                return DataUtil.getWorkRecordList(finalSearchOprno, finalSearchSn, pageNo, PAGE_SIZE);
+                return DataUtil.getWeighRecordList(lc, type, pageNo, PAGE_SIZE);
             }
 
             @Override
             protected void done() {
                 try {
-                    WorkRecordResp resp = get();
-                    updateUI(resp, finalSearchOprno);
+                    updateUI(get());
                 } catch (Exception e) {
-                    log.error("加载工作记录失败: {}", e.getMessage());
+                    log.error("加载称重记录失败: {}", e.getMessage());
                     updateUIError("加载失败: " + e.getMessage());
                 }
             }
@@ -320,9 +230,6 @@ public class WorkRecordPanel extends JPanel {
         worker.execute();
     }
 
-    /**
-     * 设置按钮启用状态
-     */
     private void setButtonsEnabled(boolean enabled) {
         btnSearch.setEnabled(enabled);
         btnRefresh.setEnabled(enabled);
@@ -330,11 +237,7 @@ public class WorkRecordPanel extends JPanel {
         btnNext.setEnabled(enabled && pageNo < totalPages);
     }
 
-    /**
-     * 更新UI显示数据
-     */
-    private void updateUI(WorkRecordResp resp, String queryOprno) {
-        // 清空表格
+    private void updateUI(WorkRecordResp resp) {
         tableModel.setRowCount(0);
 
         if (resp == null || !resp.isResult()) {
@@ -343,48 +246,32 @@ public class WorkRecordPanel extends JPanel {
             return;
         }
 
-        // 更新分页信息
         totalCount = resp.getCount();
         totalPages = (int) Math.ceil((double) totalCount / PAGE_SIZE);
-        if (totalPages == 0) totalPages = 1;
+        if (totalPages == 0) {
+            totalPages = 1;
+        }
 
-        // 更新页码显示
         pageLabel.setText(pageNo + "/" + totalPages);
-
-        // 更新按钮状态
         setButtonsEnabled(true);
+        statusLabel.setText("称重记录 共" + totalCount + "条");
 
-        // 更新状态标签
-        String oprnoDisplay;
-        if (isAllStation) {
-            oprnoDisplay = (queryOprno == null || queryOprno.isEmpty()) ? "所有工位" : queryOprno;
-        } else {
-            oprnoDisplay = oprno;
-        }
-        statusLabel.setText("工位:" + oprnoDisplay + " 共" + totalCount + "条");
-
-        // 填充表格数据
         List<WorkRecordData> list = resp.getList();
         if (list != null && !list.isEmpty()) {
             for (WorkRecordData data : list) {
-                Object[] row = {
-                        data.getSn(),
-                        data.getOprno(),
-                        data.getContent(),
+                tableModel.addRow(new Object[]{
+                        data.getLc(),
+                        data.getBc(),
+                        data.getType(),
+                        data.getZl(),
                         data.getUpdateBy(),
-                        data.getUpdateDate()
-                };
-                tableModel.addRow(row);
+                        data.getCreateDate()
+                });
             }
         }
-
-        // 刷新表格
         table.repaint();
     }
 
-    /**
-     * 更新UI显示错误信息
-     */
     private void updateUIError(String message) {
         statusLabel.setText("错误: " + message);
         pageLabel.setText("0/0");
@@ -393,11 +280,4 @@ public class WorkRecordPanel extends JPanel {
         btnNext.setEnabled(false);
         tableModel.setRowCount(0);
     }
-
-    /**
-     * 获取当前工位号
-     */
-    public String getOprno() {
-        return oprno;
-    }
 }

+ 21 - 0
src/com/mes/util/JdbcUtils.java

@@ -77,8 +77,29 @@ public class JdbcUtils {
 				")";
 		statement.executeUpdate(submitRecord);
 
+		// 炉次计数表(按天重置,从1开始)
+		ensureFurnaceTable(statement);
+
         statement.close();
     }
+
+	public static void ensureFurnaceTable() throws SQLException {
+		if (conn == null || conn.isClosed()) {
+			openConnection();
+		}
+		try (Statement statement = conn.createStatement()) {
+			ensureFurnaceTable(statement);
+		}
+	}
+
+	private static void ensureFurnaceTable(Statement statement) throws SQLException {
+		String furnaceCounter = "CREATE TABLE if not exists furnace_counter(\n" +
+				"   id INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
+				"   furnace_no INTEGER NOT NULL DEFAULT 1,\n" +
+				"   record_date VARCHAR(20) NOT NULL\n" +
+				")";
+		statement.executeUpdate(furnaceCounter);
+	}
     
     //插入数据
     public static boolean insertData(String gw, String gy, String bw, String message_type, String sn) {

+ 229 - 0
src/com/mes/util/ModbusWeightReader.java

@@ -0,0 +1,229 @@
+package com.mes.util;
+
+import com.github.xingshuangs.iot.protocol.common.buff.EByteBuffFormat;
+import com.github.xingshuangs.iot.protocol.modbus.service.ModbusTcp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * 通过 Modbus-TCP 读取称重净重(功能码 0x03,Float)
+ */
+public class ModbusWeightReader {
+
+    private static final Logger log = LoggerFactory.getLogger(ModbusWeightReader.class);
+
+    private static final String DEFAULT_IP = "192.168.1.150";
+    private static final int DEFAULT_PORT = 60000;
+    private static final int DEFAULT_UNIT_ID = 1;
+    private static final int DEFAULT_ADDRESS = 0;
+    /** 常见默认:大端 AB_CD;库自带 readFloat32() 默认其实是 BA_DC */
+    private static final EByteBuffFormat DEFAULT_FORMAT = EByteBuffFormat.DC_BA;
+
+    private final String ip;
+    private final int port;
+    private final int unitId;
+    private final int address;
+    private final EByteBuffFormat byteFormat;
+
+    private ModbusTcp modbus;
+    private volatile boolean connected;
+    private volatile String lastError;
+
+    public ModbusWeightReader() {
+        Properties pro = loadProperties();
+        this.ip = pro.getProperty("modbus.ip", DEFAULT_IP).trim();
+        this.port = parseInt(pro.getProperty("modbus.port"), DEFAULT_PORT);
+        this.unitId = parseInt(pro.getProperty("modbus.unit_id"), DEFAULT_UNIT_ID);
+        this.address = parseInt(pro.getProperty("modbus.weight_address"), DEFAULT_ADDRESS);
+        this.byteFormat = parseFormat(pro.getProperty("modbus.byte_order"), DEFAULT_FORMAT);
+        log.info("Modbus称重配置: ip={}, port={}, unitId={}, address={}, byteOrder={}",
+                ip, port, unitId, address, byteFormat);
+    }
+
+    public ModbusWeightReader(String ip, int port, int unitId, int address) {
+        this(ip, port, unitId, address, DEFAULT_FORMAT);
+    }
+
+    public ModbusWeightReader(String ip, int port, int unitId, int address, EByteBuffFormat byteFormat) {
+        this.ip = ip;
+        this.port = port;
+        this.unitId = unitId;
+        this.address = address;
+        this.byteFormat = byteFormat == null ? DEFAULT_FORMAT : byteFormat;
+    }
+
+    private static Properties loadProperties() {
+        Properties pro = new Properties();
+        try {
+            InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
+            if (is != null) {
+                BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                pro.load(br);
+                br.close();
+            }
+        } catch (Exception e) {
+            log.warn("读取Modbus配置失败,使用默认值: {}", e.getMessage());
+        }
+        return pro;
+    }
+
+    private static int parseInt(String value, int defaultValue) {
+        try {
+            if (value != null && !value.trim().isEmpty()) {
+                return Integer.parseInt(value.trim());
+            }
+        } catch (Exception ignored) {
+        }
+        return defaultValue;
+    }
+
+    private static EByteBuffFormat parseFormat(String value, EByteBuffFormat defaultValue) {
+        if (value == null || value.trim().isEmpty()) {
+            return defaultValue;
+        }
+        String v = value.trim().toUpperCase().replace('-', '_');
+        try {
+            return EByteBuffFormat.valueOf(v);
+        } catch (Exception e) {
+            log.warn("无法识别字节序 {}, 使用默认 {}", value, defaultValue);
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 读取 Float 净重(功能码 0x03,按配置字节序)
+     */
+    public synchronized Float readWeight() {
+        return readWeightFloat(byteFormat);
+    }
+
+    /**
+     * 读取 Float 净重,指定字节序(功能码 0x03),结果保留两位小数
+     */
+    public synchronized Float readWeightFloat(EByteBuffFormat format) {
+        try {
+            ensureConnected();
+            float value = modbus.readFloat32(address, format);
+            value = round2(value);
+            connected = true;
+            lastError = null;
+            return value;
+        } catch (Exception e) {
+            connected = false;
+            lastError = e.getMessage();
+            log.warn("Modbus读取Float(0x03)失败: {}", e.getMessage());
+            closeQuietly();
+            return null;
+        }
+    }
+
+    /** 四舍五入保留两位小数 */
+    public static float round2(float value) {
+        return Math.round(value * 100f) / 100f;
+    }
+
+    /**
+     * 读取原始 2 个保持寄存器(4 字节),用于排查字节序
+     */
+    public synchronized byte[] readRawBytes() {
+        try {
+            ensureConnected();
+            byte[] data = modbus.readHoldRegister(address, 2);
+            connected = true;
+            lastError = null;
+            return data;
+        } catch (Exception e) {
+            connected = false;
+            lastError = e.getMessage();
+            log.warn("Modbus读取原始寄存器失败: {}", e.getMessage());
+            closeQuietly();
+            return null;
+        }
+    }
+
+    /**
+     * 用同一组原始字节,按 4 种字节序解析 Float,便于对照
+     */
+    public static Map<String, Float> decodeAllFormats(byte[] raw) {
+        Map<String, Float> map = new LinkedHashMap<String, Float>();
+        if (raw == null || raw.length < 4) {
+            return map;
+        }
+        for (EByteBuffFormat format : EByteBuffFormat.values()) {
+            map.put(format.name(), decodeFloat(raw, format));
+        }
+        return map;
+    }
+
+    public static float decodeFloat(byte[] raw, EByteBuffFormat format) {
+        float value = com.github.xingshuangs.iot.protocol.common.buff.ByteReadBuff
+                .newInstance(raw, format)
+                .getFloat32();
+        return round2(value);
+    }
+
+    public static String toHex(byte[] raw) {
+        if (raw == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < raw.length; i++) {
+            if (i > 0) {
+                sb.append(' ');
+            }
+            sb.append(String.format("%02X", raw[i] & 0xFF));
+        }
+        return sb.toString();
+    }
+
+    private void ensureConnected() {
+        if (modbus == null) {
+            modbus = new ModbusTcp(unitId, ip, port);
+            modbus.setPersistence(true);
+            modbus.setEnableReconnect(true);
+            modbus.setConnectTimeout(2000);
+            modbus.setReceiveTimeout(2000);
+        }
+        if (!modbus.checkConnected()) {
+            modbus.connect();
+        }
+    }
+
+    private void closeQuietly() {
+        if (modbus != null) {
+            try {
+                modbus.close();
+            } catch (Exception ignored) {
+            }
+            modbus = null;
+        }
+    }
+
+    public synchronized void close() {
+        closeQuietly();
+        connected = false;
+    }
+
+    public boolean isConnected() {
+        return connected;
+    }
+
+    public String getLastError() {
+        return lastError;
+    }
+
+    public EByteBuffFormat getByteFormat() {
+        return byteFormat;
+    }
+
+    public String getEndpointDesc() {
+        return ip + ":" + port + " addr=" + address + " order=" + byteFormat;
+    }
+}

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

@@ -1,4 +1,12 @@
 mes.gw=OP240A
 mes.server_ip=127.0.0.1
 #mes.server_ip=192.168.17.170
-mes.line_sn=XT
+mes.line_sn=XT
+
+# Modbus-TCP 称重
+modbus.ip=192.168.1.150
+modbus.port=60000
+modbus.unit_id=1
+modbus.weight_address=0
+# Float字节序:AB_CD / BA_DC / CD_AB / DC_BA(显示-0.00时请换这个)
+modbus.byte_order=DC_BA