Browse Source

优化打印尺寸

hou 4 days ago
parent
commit
50550bda1e

+ 170 - 0
src/com/mes/component/ProductTypePanel.java

@@ -0,0 +1,170 @@
+package com.mes.component;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.LineBorder;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+
+public class ProductTypePanel extends JPanel {
+
+    public static final String TYPE_ZJ = "ZJ";
+    public static final String TYPE_SJ = "SJ";
+
+    private static final Color COLOR_SELECTED = new Color(24, 144, 255);
+    private static final Color COLOR_BG = new Color(245, 247, 250);
+    private static final Color COLOR_BORDER = new Color(217, 217, 217);
+    private static final Color COLOR_TEXT = new Color(89, 89, 89);
+
+    private final String switchPassword;
+    private String result = TYPE_ZJ;
+    private JButton zjButton;
+    private JButton sjButton;
+    private Runnable onChangeListener;
+    private boolean suppressPasswordCheck = false;
+
+    public ProductTypePanel(String switchPassword) {
+        this.switchPassword = switchPassword;
+        setLayout(new GridLayout(1, 2, 12, 0));
+        setOpaque(false);
+        setBorder(BorderFactory.createCompoundBorder(
+                new LineBorder(COLOR_BORDER, 1, true),
+                new EmptyBorder(8, 10, 8, 10)
+        ));
+        setBackground(COLOR_BG);
+
+        zjButton = createTypeButton("智界", TYPE_ZJ);
+        sjButton = createTypeButton("尚界", TYPE_SJ);
+        add(zjButton);
+        add(sjButton);
+        refreshButtonStyle();
+    }
+
+    private JButton createTypeButton(String title, String type) {
+        JButton button = new JButton(title) {
+            @Override
+            protected void paintComponent(Graphics g) {
+                Graphics2D g2 = (Graphics2D) g.create();
+                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                if (getModel().isPressed()) {
+                    g2.setColor(COLOR_SELECTED.darker());
+                } else if (type.equals(result)) {
+                    g2.setColor(isEnabled() ? COLOR_SELECTED : COLOR_SELECTED.brighter());
+                } else {
+                    g2.setColor(isEnabled() ? Color.WHITE : new Color(250, 250, 250));
+                }
+                g2.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
+                g2.dispose();
+                super.paintComponent(g);
+            }
+        };
+        button.setName(type);
+        button.setFont(new Font("微软雅黑", Font.BOLD, 26));
+        button.setFocusPainted(false);
+        button.setBorderPainted(false);
+        button.setContentAreaFilled(false);
+        button.setPreferredSize(new Dimension(180, 52));
+        button.addActionListener(e -> selectType(type, e));
+        return button;
+    }
+
+    private void selectType(String type, ActionEvent e) {
+        if (!isEnabled()) {
+            return;
+        }
+        if (type.equals(result)) {
+            return;
+        }
+        if (!suppressPasswordCheck && !verifySwitchPassword()) {
+            refreshButtonStyle();
+            return;
+        }
+        result = type;
+        refreshButtonStyle();
+        if (onChangeListener != null) {
+            onChangeListener.run();
+        }
+    }
+
+    private boolean verifySwitchPassword() {
+        JPasswordField passwordField = new JPasswordField(15);
+        JPanel panel = new JPanel(new BorderLayout(8, 8));
+        panel.add(new JLabel("请输入切换密码:"), BorderLayout.NORTH);
+        panel.add(passwordField, BorderLayout.CENTER);
+
+        int option = JOptionPane.showConfirmDialog(
+                SwingUtilities.getWindowAncestor(this),
+                panel,
+                "切换生产类型",
+                JOptionPane.OK_CANCEL_OPTION,
+                JOptionPane.PLAIN_MESSAGE
+        );
+        if (option != JOptionPane.OK_OPTION) {
+            return false;
+        }
+        if (!switchPassword.equals(new String(passwordField.getPassword()))) {
+            JOptionPane.showMessageDialog(
+                    SwingUtilities.getWindowAncestor(this),
+                    "密码错误,无法切换生产类型",
+                    "提示",
+                    JOptionPane.WARNING_MESSAGE
+            );
+            return false;
+        }
+        return true;
+    }
+
+    private void refreshButtonStyle() {
+        styleButton(zjButton, TYPE_ZJ.equals(result));
+        styleButton(sjButton, TYPE_SJ.equals(result));
+        repaint();
+    }
+
+    private void styleButton(JButton button, boolean selected) {
+        if (!button.isEnabled()) {
+            button.setForeground(selected ? Color.WHITE : new Color(191, 191, 191));
+            return;
+        }
+        if (selected) {
+            button.setForeground(Color.WHITE);
+        } else {
+            button.setForeground(COLOR_TEXT);
+        }
+        button.repaint();
+    }
+
+    public void setOnChangeListener(Runnable onChangeListener) {
+        this.onChangeListener = onChangeListener;
+    }
+
+    public void setResult(String type) {
+        suppressPasswordCheck = true;
+        try {
+            if (TYPE_SJ.equals(type)) {
+                result = TYPE_SJ;
+            } else {
+                result = TYPE_ZJ;
+            }
+            refreshButtonStyle();
+        } finally {
+            suppressPasswordCheck = false;
+        }
+    }
+
+    public String getResult() {
+        return result;
+    }
+
+    public String getTypeName() {
+        return TYPE_SJ.equals(result) ? "尚界" : "智界";
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        zjButton.setEnabled(enabled);
+        sjButton.setEnabled(enabled);
+        refreshButtonStyle();
+        repaint();
+    }
+}

+ 16 - 2
src/com/mes/ui/MesClient.java

@@ -48,6 +48,7 @@ public class MesClient extends JFrame {
     public static final int PRODUCT_SN_LENGTH = 32;
     public static final String PREFIX_ZJ = "000020021815-01000016";  //智界
     public static final String PREFIX_SJ = "000020022029-01000016";  //尚界
+    public static final boolean PRODUCT_TYPE_SWITCH_ENABLED = false;
     public static final String PRODUCT_TYPE_SWITCH_PASSWORD = "503833";
     public static final String PRODUCT_TYPE_ZJ = ProductTypePanel.TYPE_ZJ;
     public static final String PRODUCT_TYPE_SJ = ProductTypePanel.TYPE_SJ;
@@ -333,7 +334,11 @@ public class MesClient extends JFrame {
         MesClient.f_scan_data_bt_1.setEnabled(true);
 //		DataUtil.stopWork(sessionid);
         updateProductTypePanelEnabled(true);
-        MesClient.setMenuStatus("当前生产类型:" + getProductTypeName() + ",请扫工件码",0); //修改
+        if (PRODUCT_TYPE_SWITCH_ENABLED) {
+            MesClient.setMenuStatus("当前生产类型:" + getProductTypeName() + ",请扫工件码",0);
+        } else {
+            MesClient.setMenuStatus("请扫工件码",0);
+        }
 //		product_result_text.setText("");
 //		work_status_text.setText("");
 //        MesClient.setMenuStatus("涂胶已超过10分钟,已失效",-1);
@@ -859,6 +864,7 @@ public class MesClient extends JFrame {
 
         indexPanelC = new JPanel(new BorderLayout());
 
+        if (PRODUCT_TYPE_SWITCH_ENABLED) {
         //新增的面板
         JPanel indexPanelProductType = new JPanel();
         indexPanelProductType.setLayout(null);
@@ -883,7 +889,11 @@ public class MesClient extends JFrame {
         productTypePanel.setOnChangeListener(() -> {
             if (work_status == 0) {
                 saveProductTypeToDb();
-                setMenuStatus("当前生产类型:" + getProductTypeName() + ",请扫工件码", 0);
+                if (PRODUCT_TYPE_SWITCH_ENABLED) {
+            setMenuStatus("当前生产类型:" + getProductTypeName() + ",请扫工件码",0);
+        } else {
+            setMenuStatus("请扫工件码",0);
+        }
             }
             refreshProductTypeCurrentLabel();
         });
@@ -898,6 +908,7 @@ public class MesClient extends JFrame {
         indexPanelProductType.add(productTypeCurrentLabel);
 
         tabbedPane.addTab("生产类型", new ImageIcon(MesClient.class.getResource("/bg/menu_setting.png")), productTypeScrollPane, null);
+        }
 
         tabbedPane.addTab("开班点检", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), indexPanelC, null);
 
@@ -930,6 +941,9 @@ public class MesClient extends JFrame {
         if (sn.length() != PRODUCT_SN_LENGTH) {
             return "工件码长度须为32位,当前" + sn.length() + "位";
         }
+        if (!PRODUCT_TYPE_SWITCH_ENABLED) {
+            return null;
+        }
         String prefix = getProductSnPrefix();
         if (!sn.startsWith(prefix)) {
             return "工件码前缀与当前类型(" + getProductTypeName() + ")不匹配,当前工件码为" + getProductTypeNameBySn(sn);

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

@@ -1,8 +1,5 @@
 package com.mes.util;
 
-import com.sun.org.slf4j.internal.Logger;
-import com.sun.org.slf4j.internal.LoggerFactory;
-
 import java.sql.*;
 
 public class JdbcUtils {

+ 76 - 19
src/com/mes/util/PrintUtil.java

@@ -14,21 +14,34 @@ import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
 /**
- * TSC TTP-244 Pro 标签打印。
+ * 得力 Deli DL-730C 标签打印。
  * <p>
- * 使用 TSPL 指令直打,配合打印机 Tear(撕纸)模式:打完送出标签,
- * 下一张前回抽,并由 SIZE/GAP 保证回抽后仍对准缝隙。
- * <p>
- * 打印机 Post-Print Action 请保持为 Tear。
+ * 使用 TSPL 指令直打,配合打印机 Tear(撕纸)模式。
+ * 标签纸规格:100mm × 80mm。
  */
 public class PrintUtil {
     public static final Logger log = LoggerFactory.getLogger(PrintUtil.class);
 
-    private static final float LABEL_WIDTH_MM = 35f;
-    private static final float LABEL_HEIGHT_MM = 25f;
-    /** 标签间隙 */
+    private static final String PRINTER_NAME = "Deli DL-730C(NEW)";
+    /** 标签宽 100mm、高 80mm */
+    private static final float LABEL_WIDTH_MM = 100f;
+    private static final float LABEL_HEIGHT_MM = 80f;
+    /** 标签间隙,常见 2~3mm,不对时再调 */
     private static final float LABEL_GAP_MM = 2f;
 
+    /**
+     * DL-730C 为 203DPI:1mm ≈ 8 dots;100×80mm ≈ 800×640 dots。
+     */
+    private static final int DOTS_PER_MM = 8;
+    private static final int LABEL_WIDTH_DOTS = Math.round(LABEL_WIDTH_MM * DOTS_PER_MM);
+    private static final int LABEL_HEIGHT_DOTS = Math.round(LABEL_HEIGHT_MM * DOTS_PER_MM);
+    /** 实测微调:整体略往右、往下挪(dots,约 1mm=8) */
+    private static final int SHIFT_X_DOTS = 48;
+    private static final int SHIFT_Y_DOTS = 40;
+    /** 文字相对整块再往右;二维码相对整块再往左 */
+    private static final int TEXT_SHIFT_X_DOTS = 40;
+    private static final int QR_SHIFT_X_DOTS = -40;
+
     public static void printLabel(String barCode) {
         log.info("sn:" + barCode);
         PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
@@ -37,15 +50,16 @@ public class PrintUtil {
             return;
         }
 
-        String printerName = "TTP-244 Pro";
         PrintService selectedService = Arrays.stream(services)
-                .filter(s -> s.getName().equals(printerName))
+                .filter(s -> s.getName().equals(PRINTER_NAME) || s.getName().contains("DL-730C"))
                 .findFirst()
                 .orElse(null);
         if (selectedService == null) {
-            log.info(printerName + ":Printer not found.");
+            log.info(PRINTER_NAME + ":Printer not found. available={}",
+                    Arrays.toString(Arrays.stream(services).map(PrintService::getName).toArray()));
             return;
         }
+        log.info("use printer: {}", selectedService.getName());
 
         try {
             byte[] tspl = buildTsplCommands(barCode).getBytes(Charset.forName("GB18030"));
@@ -65,12 +79,35 @@ public class PrintUtil {
     }
 
     /**
-     * TTP-244 Pro 为 203DPI:1mm ≈ 8 dots。
-     * 文字在上、二维码在下(两行)。
-     * 标签约 280x200 dots(35x25mm)。
+     * 文字 + 二维码整块水平、垂直居中。
      */
     private static String buildTsplCommands(String barCode) {
         String data = sanitizeTsplText(barCode);
+        int len = Math.max(data.length(), 1);
+
+        // 内置字体 "3"=16×24;SN 较长时略缩小
+        int textMul = len > 18 ? 2 : 3;
+        int charWidth = 16 * textMul;
+        int textHeight = 24 * textMul;
+        int textWidth = len * charWidth;
+
+        // 203DPI 下 cell=10,二维码约 25~33mm,在 100×80 上大小合适
+        int qrCell = 10;
+        int qrModules = estimateQrModules(len);
+        int qrSize = qrModules * qrCell;
+
+        int gapBetween = 48;
+        int blockWidth = Math.max(textWidth, qrSize);
+        int blockHeight = textHeight + gapBetween + qrSize;
+
+        int blockX = Math.max(0, (LABEL_WIDTH_DOTS - blockWidth) / 2) + SHIFT_X_DOTS;
+        int blockY = Math.max(0, (LABEL_HEIGHT_DOTS - blockHeight) / 2) + SHIFT_Y_DOTS;
+
+        int textX = blockX + Math.max(0, (blockWidth - textWidth) / 2) + TEXT_SHIFT_X_DOTS;
+        int textY = blockY;
+        int qrX = Math.max(0, blockX + Math.max(0, (blockWidth - qrSize) / 2) + QR_SHIFT_X_DOTS);
+        int qrY = blockY + textHeight + gapBetween;
+
         StringBuilder sb = new StringBuilder();
         sb.append("SIZE ").append(fmt(LABEL_WIDTH_MM)).append(" mm,").append(fmt(LABEL_HEIGHT_MM)).append(" mm\r\n");
         sb.append("GAP ").append(fmt(LABEL_GAP_MM)).append(" mm,0\r\n");
@@ -79,16 +116,36 @@ public class PrintUtil {
         sb.append("OFFSET 0 mm\r\n");
         sb.append("SET TEAR ON\r\n");
         sb.append("CLS\r\n");
-        // 第一行:条码文字
-        sb.append("TEXT 36,36,\"2\",0,1,1,\"").append(data).append("\"\r\n");
-        // 第二行:二维码
-        sb.append("QRCODE 100,68,L,4,A,0,\"").append(data).append("\"\r\n");
+        sb.append("TEXT ").append(textX).append(",").append(textY)
+                .append(",\"3\",0,").append(textMul).append(",").append(textMul)
+                .append(",\"").append(data).append("\"\r\n");
+        sb.append("QRCODE ").append(qrX).append(",").append(qrY)
+                .append(",L,").append(qrCell).append(",A,0,\"")
+                .append(data).append("\"\r\n");
         sb.append("PRINT 1,1\r\n");
         String cmd = sb.toString();
-        log.info("TSPL:\n{}", cmd);
+        log.info("TSPL dpiApprox={}, dots={}x{}, text=({},{}), qr=({},{}), qrSize={}\n{}",
+                DOTS_PER_MM * 25, LABEL_WIDTH_DOTS, LABEL_HEIGHT_DOTS, textX, textY, qrX, qrY, qrSize, cmd);
         return cmd;
     }
 
+    /** 按字母数字长度粗估 QR 版本模组边长 */
+    private static int estimateQrModules(int alphanumericLen) {
+        if (alphanumericLen <= 14) {
+            return 21;
+        }
+        if (alphanumericLen <= 25) {
+            return 25;
+        }
+        if (alphanumericLen <= 40) {
+            return 29;
+        }
+        if (alphanumericLen <= 60) {
+            return 33;
+        }
+        return 37;
+    }
+
     private static String sanitizeTsplText(String text) {
         if (text == null) {
             return "";