parent
commit
2bcae967f6
3 ha cambiato i file con 163 aggiunte e 2 eliminazioni
  1. +65
    -2
      python/Bag1.py
  2. +1
    -0
      python/README.md
  3. +97
    -0
      python/label_zpl.py

+ 65
- 2
python/Bag1.py Vedi File

@@ -35,7 +35,7 @@ DEFAULT_SETTINGS = {
"api_ip": "localhost",
"api_port": "8090",
"dabag_ip": "",
"dabag_port": "9100",
"dabag_port": "3008",
"laser_ip": "",
"laser_port": "9100",
"label_com": "COM3",
@@ -132,6 +132,47 @@ REFRESH_MS = 60 * 1000 # 60 seconds refresh when connected
PRINTER_CHECK_MS = 60 * 1000 # 1 minute when printer OK
PRINTER_RETRY_MS = 30 * 1000 # 30 seconds when printer failed
PRINTER_SOCKET_TIMEOUT = 3
DATAFLEX_SEND_TIMEOUT = 10 # seconds when sending ZPL to DataFlex


def _zpl_escape(s: str) -> str:
"""Escape text for ZPL ^FD...^FS (backslash and caret)."""
return s.replace("\\", "\\\\").replace("^", "\\^")


def generate_zpl_dataflex(batch_no: str, item_code: str, item_name: str) -> str:
"""
Generate ZPL for DataFlex label (53 mm media, 90° rotated).
Uses UTF-8 (^CI28) and fonts msjh.ttc / msjhbd.ttc for Chinese/English.
"""
desc = _zpl_escape((item_name or "—").strip())
code = _zpl_escape((item_code or "—").strip())
batch = _zpl_escape(batch_no.strip())
return f"""^XA
^PW420
^LL780
^PO N
^CI28
^FO70,70
^A@R,60,60,E:MSJH.TTC^FD{desc}^FS
^FO220,70
^A@R,50,50,E:MSJHBD.TTC^FD{code}^FS
^FO310,70
^A@R,45,45,E:MSJHBD.TTC^FD批次: {batch}^FS
^FO150,420
^BQN,2,6^FDQA,{batch_no}^FS
^XZ"""


def send_zpl_to_dataflex(ip: str, port: int, zpl: str) -> None:
"""Send ZPL to DataFlex printer via TCP. Raises on connection/send error."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(DATAFLEX_SEND_TIMEOUT)
try:
sock.connect((ip, port))
sock.sendall(zpl.encode("utf-8"))
finally:
sock.close()


def format_qty(val) -> str:
@@ -551,7 +592,29 @@ def main() -> None:
set_row_highlight(r, True)
selected_row_holder[0] = r
selected_jo_id_ref[0] = j.get("id")
if printer_var.get() == "標簽機":
if printer_var.get() == "打袋機 DataFlex":
ip = (settings.get("dabag_ip") or "").strip()
port_str = (settings.get("dabag_port") or "3008").strip()
try:
port = int(port_str)
except ValueError:
port = 3008
if not ip:
messagebox.showerror("打袋機", "請在設定中填寫打袋機 DataFlex 的 IP。")
else:
item_code = j.get("itemCode") or "—"
item_name = j.get("itemName") or "—"
zpl = generate_zpl_dataflex(b, item_code, item_name)
try:
send_zpl_to_dataflex(ip, port, zpl)
messagebox.showinfo("打袋機", f"已送出列印:批次 {b}")
except ConnectionRefusedError:
messagebox.showerror("打袋機", f"無法連線至 {ip}:{port},請確認印表機已開機且 IP 正確。")
except socket.timeout:
messagebox.showerror("打袋機", f"連線逾時 ({ip}:{port}),請檢查網路與連接埠。")
except OSError as err:
messagebox.showerror("打袋機", f"列印失敗:{err}")
elif printer_var.get() == "標簽機":
count = ask_label_count(root)
if count is not None:
if count == "C":


+ 1
- 0
python/README.md Vedi File

@@ -22,6 +22,7 @@ Set the backend base URL (optional, default below):
|--------|-------------|
| `Bag1.py` | **GUI**: date selector (default today) and job orders as buttons; click shows "Clicked on Job Order code XXXX item xxxx". Run: `python Bag1.py` |
| `fetch_job_orders.py` | CLI: fetches job orders by plan date from `GET /py/job-orders` |
| `label_zpl.py` | ZPL label generator (90° rotated, UTF-8 Chinese, QR). `generate_zpl(batch_no, item_code, chinese_desc)`, `send_zpl(zpl, host, port)`. Run: `python label_zpl.py` to print one test label. |

## Building Bag1 as a standalone .exe



+ 97
- 0
python/label_zpl.py Vedi File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""
ZPL label generator and TCP send for Zebra label printer (標簽機).
- Rotated 90° clockwise for ~53 mm media width
- UTF-8 (^CI28) for Chinese
- Large fonts, QR code mag 6

Standalone: python label_zpl.py
Or import generate_zpl() / send_zpl() and call from Bag1.
"""

import socket

# Default printer (override via args or Bag1 settings)
DEFAULT_PRINTER_IP = "192.168.17.27"
DEFAULT_PRINTER_PORT = 3008
SOCKET_TIMEOUT = 10


def generate_zpl(
batch_no: str,
item_code: str = "PP2238-02",
chinese_desc: str = "(餐廳用)凍咖啡底P+10(0.91L包)",
) -> str:
"""
Generates ZPL label:
- Rotated 90° clockwise to fit ~53 mm media width
- UTF-8 mode (^CI28) for correct Chinese display
- Larger fonts
- Bigger QR code (mag 6)
"""
return f"""^XA
^PW420 ^# Fits ~53 mm width (~420 dots @ 203 dpi)
^LL780 ^# Taller label after rotation + bigger elements
^PO N ^# Normal — change to ^POI if upside-down

^CI28 ^# Enable UTF-8 / Unicode for Chinese (critical fix for boxes)

^FO70,70
^A@R,60,60,E:SIMSUN.FNT^FD{chinese_desc}^FS
^# Very large Chinese text, rotated

^FO220,70
^A0R,50,50^FD{item_code}^FS
^# Larger item code

^FO310,70
^A0R,45,45^FDBatch: {batch_no}^FS
^# Larger batch text

^FO150,420
^BQN,2,6^FDQA,{batch_no}^FS
^# Bigger QR code (magnification 6), lower position for space

^XZ"""


def send_zpl(
zpl: str,
host: str = DEFAULT_PRINTER_IP,
port: int = DEFAULT_PRINTER_PORT,
timeout: float = SOCKET_TIMEOUT,
) -> None:
"""Send ZPL to printer over TCP. Raises on connection/send error."""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
sock.connect((host, port))
sock.sendall(zpl.encode("utf-8"))
sock.close()


# ────────────────────────────────────────────────
# Example usage — prints one label
# ────────────────────────────────────────────────

if __name__ == "__main__":
test_batch = "2025121209"
zpl = generate_zpl(test_batch)

print("Sending ZPL (90° rotated, UTF-8 Chinese, bigger QR):")
print("-" * 90)
print(zpl)
print("-" * 90)

try:
send_zpl(zpl)
print("Label sent successfully!")
print("→ Check Chinese — should show real characters (not 口口 or symbols)")
print("→ QR is now bigger (mag 6) — test scan with phone")
print("→ If upside-down: edit ^PO N → ^POI")
print("→ If still boxes: SimSun font may be missing — reinstall via Zebra Setup Utilities")
except ConnectionRefusedError:
print(f"Cannot connect to {DEFAULT_PRINTER_IP}:{DEFAULT_PRINTER_PORT} — printer off or wrong IP?")
except socket.timeout:
print("Connection timeout — check printer/network/port")
except Exception as e:
print(f"Error: {e}")

Caricamento…
Annulla
Salva