Explorar el Código

update the bag printer python as Bag2

master
Fai Luk hace 4 días
padre
commit
2610db9764
Se han modificado 8 ficheros con 1393 adiciones y 255 borrados
  1. +1379
    -0
      python/Bag2/Bag2.py
  2. +9
    -0
      python/Bag2/bag2_settings.json
  3. +5
    -0
      python/Bag2/requirements.txt
  4. +0
    -54
      python/README.md
  5. +0
    -20
      python/build_exe.bat
  6. +0
    -80
      python/fetch_job_orders.py
  7. +0
    -97
      python/label_zpl.py
  8. +0
    -4
      python/requirements-build.txt

+ 1379
- 0
python/Bag2/Bag2.py
La diferencia del archivo ha sido suprimido porque es demasiado grande
Ver fichero


+ 9
- 0
python/Bag2/bag2_settings.json Ver fichero

@@ -0,0 +1,9 @@
{
"api_ip": "10.10.0.81",
"api_port": "8090",
"dabag_ip": "192.168.17.27",
"dabag_port": "3008",
"laser_ip": "192.168.17.10",
"laser_port": "45678",
"label_com": "TSC TTP-246M Pro"
}

+ 5
- 0
python/Bag2/requirements.txt Ver fichero

@@ -0,0 +1,5 @@
# Python dependencies for FPSMS backend integration
requests>=2.28.0
pyserial>=3.5
Pillow>=9.0.0
qrcode[pil]>=7.0

+ 0
- 54
python/README.md Ver fichero

@@ -1,54 +0,0 @@
# Python scripts for FPSMS backend

This folder holds Python programs that integrate with the FPSMS backend (e.g. calling the public `/py` API).

## Setup

```bash
cd python
pip install -r requirements.txt
```

## Configuration

Set the backend base URL (optional, default below):

- Environment: `FPSMS_BASE_URL` (default: `http://localhost:8090/api` — includes context path `/api`)
- Or edit the default in each script.

## Scripts

| Script | Description |
|--------|-------------|
| `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

To distribute Bag1 to customer PCs **without giving them source code or requiring Python**:

1. On your development PC (with Python installed), open a terminal in the `python` folder.
2. Install build dependencies:

```bash
pip install -r requirements-build.txt
```

3. Run the build script:

```bash
build_exe.bat
```

Or run PyInstaller directly:

```bash
pyinstaller --onefile --windowed --name Bag1 Bag1.py
```

4. The executable is created at `dist\Bag1.exe`. Copy **only** `Bag1.exe` to the customer computer and run it; no Python or source code is needed. The app will save its settings (`bag1_settings.json`) in the same folder as the exe.

## Adding new scripts

Add new `.py` files here and list them in this README. Use `requirements.txt` for any new dependencies.

+ 0
- 20
python/build_exe.bat Ver fichero

@@ -1,20 +0,0 @@
@echo off
REM Build Bag1.exe (single file, no console window).
REM Run from this folder: build_exe.bat
REM Requires: pip install -r requirements-build.txt

set SCRIPT=Bag1.py
set NAME=Bag1

if not exist "%SCRIPT%" (
echo Error: %SCRIPT% not found. Run from the python folder.
exit /b 1
)

pip install -r requirements-build.txt
pyinstaller --onefile --windowed --name "%NAME%" "%SCRIPT%"

echo.
echo Done. Exe is in: dist\%NAME%.exe
echo Copy dist\%NAME%.exe to the customer PC and run it (no Python needed).
pause

+ 0
- 80
python/fetch_job_orders.py Ver fichero

@@ -1,80 +0,0 @@
#!/usr/bin/env python3
"""
Fetch job orders from FPSMS backend by plan date.
Uses the public API GET /py/job-orders (no login required).

Usage:
python fetch_job_orders.py # today
python fetch_job_orders.py 2026-02-24 # specific date
python fetch_job_orders.py --date 2026-02-24
"""

import argparse
import os
import sys
from datetime import date
from typing import List, Optional

import requests

DEFAULT_BASE_URL = os.environ.get("FPSMS_BASE_URL", "http://localhost:8090/api")


def fetch_job_orders(base_url: str, plan_start: Optional[date]) -> List[dict]:
"""Call GET /py/job-orders and return the JSON list."""
url = f"{base_url.rstrip('/')}/py/job-orders"
params = {}
if plan_start is not None:
params["planStart"] = plan_start.isoformat()
resp = requests.get(url, params=params, timeout=30)
resp.raise_for_status()
return resp.json()


def main() -> None:
parser = argparse.ArgumentParser(description="Fetch job orders from FPSMS by plan date")
parser.add_argument(
"date",
nargs="?",
type=str,
default=None,
help="Plan date (yyyy-MM-dd). Default: today",
)
parser.add_argument(
"--date",
dest="date_alt",
type=str,
default=None,
help="Plan date (yyyy-MM-dd)",
)
parser.add_argument(
"--base-url",
type=str,
default=DEFAULT_BASE_URL,
help=f"Backend base URL (default: {DEFAULT_BASE_URL})",
)
args = parser.parse_args()

plan_str = args.date_alt or args.date
if plan_str:
try:
plan_start = date.fromisoformat(plan_str)
except ValueError:
print(f"Invalid date: {plan_str}. Use yyyy-MM-dd.", file=sys.stderr)
sys.exit(1)
else:
plan_start = date.today()

try:
data = fetch_job_orders(args.base_url, plan_start)
except requests.RequestException as e:
print(f"Request failed: {e}", file=sys.stderr)
sys.exit(1)

print(f"Job orders for planStart={plan_start} ({len(data)} items)")
for jo in data:
print(f" id={jo.get('id')} code={jo.get('code')} itemCode={jo.get('itemCode')} itemName={jo.get('itemName')} reqQty={jo.get('reqQty')}")


if __name__ == "__main__":
main()

+ 0
- 97
python/label_zpl.py Ver fichero

@@ -1,97 +0,0 @@
#!/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}")

+ 0
- 4
python/requirements-build.txt Ver fichero

@@ -1,4 +0,0 @@
# Install with: pip install -r requirements-build.txt
# Used only for building the .exe (PyInstaller).
-r requirements.txt
pyinstaller>=6.0.0

Cargando…
Cancelar
Guardar