Skip to content

davecampbell/ups-label-system

Repository files navigation

UPS Label Printing System - Development Simulator

A complete dockerized simulation environment for the Panther Predator Tamp Print & Apply system. This simulator allows full development and testing of the label printing workflow without requiring the actual hardware.

Dashboard Screenshot


Table of Contents


System Overview

This system simulates a complete warehouse label printing and application workflow:

  1. Barcode Scan → Tracking code received
  2. Label Data Fetch → IT-01 API provides label information
  3. ZPL Generation → Label formatted for Zebra printer
  4. Print → Label printed to tamp pad
  5. Apply → Robotic applicator stamps label on box
  6. Monitor → Real-time status via MQTT and dashboard

What This Simulator Represents

The simulator recreates the actual production environment with mock services:

  • Real System: UL-01 Vision Scanner → IT-01 API → Zebra ZE521-6 Print Engine → Panther Predator PLC → Electric Applicator
  • Simulator: HTTP Trigger → Mock IT-01 → Mock Zebra → Mock Panther PLC → Simulated Timing

All communication protocols, timing, and I/O mapping match the real hardware specifications.


Sub-Systems & Architecture

1. External Systems (Blue in diagram)

UL-01 Vision System

  • Real Hardware: Barcode scanner that reads tracking codes from boxes on conveyor
  • Simulator: HTTP POST endpoint at http://localhost:8000/api/v1/trigger
  • Communication: Sends 13-character UPS tracking codes (starts with "1Z")

IT-01 Label Data Service

  • Real Hardware: Backend service that provides shipping label information
  • Simulator: Mock FastAPI service at port 8001
  • Communication: HTTP POST with tracking code → Returns JSON with label data and ZPL

2. Python Controller (Orange in diagram)

The main orchestration service that coordinates the entire workflow:

  • HTTP Server: Receives barcode events
  • IT-01 Client: Fetches label data via HTTP
  • Zebra Client: Sends ZPL commands via TCP socket (port 9100)
  • Panther Client: Controls applicator via Modbus TCP (port 502)
  • MQTT Publisher: Broadcasts real-time events
  • Modbus Server: Exposes status to NextPLC HMI (port 5021)

3. Print & Apply Hardware (Green in diagram)

Zebra ZE521-6 Print Engine

  • Real Hardware: 6-inch industrial thermal transfer printer (203 DPI)
  • Simulator: Mock TCP server that receives and logs ZPL commands
  • Communication: Raw TCP on port 9100 (industry standard for Zebra)

Panther Predator PLC

  • Real Hardware: 52 I/O point PLC controlling electric servo applicator
  • Simulator: Mock Modbus TCP server simulating all I/O registers
  • Communication: Modbus TCP on port 502 (or EtherNet/IP as alternative)

Electric Applicator

  • Real Hardware: 48-inch stroke electric tamp with servo motor
  • Simulator: Timing simulation (2-second cycle with realistic states)

4. Monitoring & Control (Additional)

Web Dashboard

  • Purpose: Real-time visualization and manual control
  • Technology: FastAPI + HTMX + Alpine.js + MQTT WebSocket
  • Features: Status LEDs, event log, job history, manual triggers

MQTT Broker

  • Purpose: Event-driven real-time updates
  • Technology: Eclipse Mosquitto 2.0
  • Topics: Job events, system status, metrics

Docker Containers

1. ups-controller (Main Service)

Image: ups-label-system-controller Ports:

  • 8000 - HTTP REST API
  • 5021 - Modbus TCP Server

Purpose: Main orchestration service

Key Components:

services/controller/
├── main.py              # FastAPI app and lifespan management
├── controller.py        # Orchestration logic and workflow
├── config.py            # Environment-based configuration
├── models.py            # Pydantic data models
├── modbus_server.py     # Modbus TCP server for HMI
├── mqtt_client.py       # MQTT event publisher
└── clients/
    ├── it01_client.py   # IT-01 HTTP client
    ├── zebra_client.py  # ZPL printer client (port 9100)
    └── panther_client.py # Modbus client for PLC

Environment Variables:

  • IT01_URL - IT-01 API endpoint
  • ZEBRA_HOST - Zebra printer IP
  • ZEBRA_PORT - Zebra printer port (default 9100)
  • PANTHER_HOST - Panther PLC IP
  • PANTHER_MODBUS_PORT - Modbus port (default 502)
  • MQTT_BROKER - MQTT broker host
  • MQTT_ENABLED - Enable MQTT publishing

Health Check: GET /health


2. ups-mock-it01 (Label Data Service)

Image: ups-label-system-mock-it01 Port: 8001

Purpose: Simulates the IT-01 label data service

Provides:

  • Realistic label data (recipient, sender, service level)
  • Pre-generated ZPL code for 6"x4" labels
  • Barcode encoding (Code 128)
  • Configurable response timing

API Endpoint:

POST http://localhost:8001/api/label
{
  "code": "1Z999AA10123456"
}

Response:

{
  "tracking_number": "1Z999AA10123456",
  "label_data": {
    "recipient": { "name": "...", "address": "..." },
    "sender": { "name": "...", "address": "..." },
    "service_level": "Ground",
    "weight": "5.2 lbs"
  },
  "rendering": {
    "format": "ZPL",
    "data": "^XA^FO50,50^A0N..."
  }
}

3. ups-mock-zebra (Print Engine)

Image: ups-label-system-mock-zebra Port: 9100

Purpose: Simulates Zebra ZE521-6 print engine

Simulates:

  • TCP socket server on port 9100
  • Receives ZPL commands
  • Logs all print jobs
  • Simulates print timing (500ms)

ZPL Commands Accepted:

  • ^XA...^XZ - Label format
  • ~HS - Host status query
  • Any standard ZPL/ZPL II commands

4. ups-mock-panther (PLC Simulator)

Image: ups-label-system-mock-panther Port: 5020 (maps to 502 internally)

Purpose: Simulates Panther Predator PLC via Modbus TCP

Register Map (matches real hardware):

Input Registers (Read by Controller):

  • Reg 0 - Status bits (Print Engine Power, Error, Online, Fault)
  • Reg 1 - Applicator bits (Cycle Complete, Home, LOTAR)
  • Reg 2-3 - Applicator Cycle Time (ms)
  • Reg 4-5 - Print Cycle Time (ms)
  • Reg 6-7 - Error Code

Holding Registers (Written by Controller):

  • Reg 0 - Command bits (Trigger 1, Trigger 2, Reset, Bypass)
  • Reg 1-2 - Product Height (optional)

Simulated Behavior:

  1. Receives TRIGGER 1 signal (holding register 0, bit 0)
  2. Clears "Applicator Home" status
  3. Waits 500ms (label printing simulation)
  4. Sets LOTAR bit (Label On Tamp And Ready)
  5. Waits 500ms (tamp extension)
  6. Clears LOTAR
  7. Waits 500ms (tamp retraction)
  8. Sets "Applicator Home" and "Cycle Complete"
  9. Reports cycle time (~2000ms)

5. ups-dashboard (Web UI)

Image: ups-label-system-dashboard Port: 8080

Purpose: Real-time monitoring and control interface

See: Dashboard section below


6. ups-mqtt (Message Broker)

Image: eclipse-mosquitto:2.0 Ports:

  • 1883 - MQTT protocol
  • 9001 - WebSocket protocol

Purpose: Event-driven messaging for real-time updates

Topics Published (by controller):

  • ups/jobs/started - Job initiation
  • ups/jobs/status - Status transitions
  • ups/jobs/completed - Job completion
  • ups/jobs/failed - Job failures
  • ups/system/status - System health
  • ups/system/metrics - Performance data

Dashboard

The web dashboard provides real-time monitoring and control of the label printing system.

Access: http://localhost:8080

Features

1. System Status Panel (Top Left)

  • 6 LED Indicators with color coding:
    • Green: System Ready, IT-01 Connected, Panther PLC, Print Engine
    • Yellow: Job In Progress
    • Red: Error State

2. Control Panel (Top Right)

  • Manual Trigger:
    • Tracking code input (13 characters, starts with "1Z")
    • Validation feedback
    • Trigger print/apply job button
  • System Controls:
    • Reset errors
    • Emergency stop (placeholder)

3. Performance Metrics (Middle Left)

  • Jobs completed today
  • Total jobs completed
  • Last cycle time (milliseconds)
  • System uptime (minutes)

4. Job History Chart (Middle Right)

  • Chart.js placeholder for trend visualization
  • Ready for jobs/hour metrics

5. Recent Jobs List (Bottom Left)

  • Last 10 jobs with:
    • Job ID and tracking code
    • Status (completed/failed)
    • Cycle time
    • Timestamp
    • Error messages (if failed)

6. Event Log (Bottom Right)

  • Real-time scrolling log of all system events
  • Color-coded messages:
    • Blue (Info): Job status, subscriptions
    • Green (Success): Completions, connections
    • Yellow (Warning): Reconnections
    • Red (Error): Failures
    • Gray (System): Status updates
  • Shows timestamps and MQTT topic names
  • Auto-scrolls, keeps last 100 entries

Technology Stack

  • Backend: FastAPI (Python) - Proxies requests to controller
  • Frontend: HTMX (auto-refresh) + Alpine.js (interactivity)
  • Real-time: MQTT WebSocket client
  • Styling: Industrial SCADA theme (dark with green LEDs)
  • Charts: Chart.js

System Diagram

graph TD
    UL01[UL-01 Barcode Scanner]
    IT01[IT-01 Label Data API]

    HTTP[HTTP Server FastAPI]
    IT01Client[IT-01 Client]
    ZPLGen[ZPL Generator]
    ZebraClient[Zebra Client]
    PantherClient[Panther Client]

    Zebra[Zebra ZE521-6 Print Engine]
    Panther[Panther Predator PLC]
    Applicator[Electric Applicator]

    UL01 -->|HTTP POST Tracking Code| HTTP
    HTTP -->|Queue Job| IT01Client
    IT01Client -->|Request Label Data| IT01
    IT01 -->|JSON Response| IT01Client
    IT01Client -->|Label Data| ZPLGen

    ZPLGen -->|ZPL Commands| ZebraClient
    ZPLGen -->|Ready Signal| PantherClient

    ZebraClient -->|TCP Port 9100| Zebra
    Zebra -->|Print to Tamp| Applicator

    PantherClient -->|Modbus TCP 502| Panther
    Panther -->|Control Servo| Applicator
    Applicator -->|Status Feedback| Panther
    Panther -->|I/O Status| PantherClient

    classDef external fill:#e1f5ff,stroke:#0066cc,stroke-width:2px
    classDef controller fill:#fff4e6,stroke:#ff9800,stroke-width:2px
    classDef hardware fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

    class UL01,IT01 external
    class HTTP,IT01Client,ZPLGen,ZebraClient,PantherClient controller
    class Zebra,Panther,Applicator hardware
Loading

Communication Protocols

Connection Protocol Port/Method Data Format
UL-01 → Python HTTP POST Port 8000 JSON (tracking code)
Python → IT-01 HTTP POST Port 8001 JSON (label request)
IT-01 → Python HTTP Response - JSON (label data + ZPL)
Python → Zebra Raw TCP Port 9100 ZPL (print commands)
Python → Panther Modbus TCP Port 502 Binary I/O (triggers/status)
Controller → Dashboard MQTT + HTTP 1883/8000 JSON events + REST API

Real Hardware Integration Guide

When transitioning from simulator to actual Panther Predator hardware, you'll need to configure these specific details.

Modbus TCP Register Map

The Panther Predator PLC exposes I/O via Modbus TCP on port 502 (unit ID typically 1).

Input Registers (Panther → Controller)

Read these registers to monitor system status:

Register Bits/Range Description
0 Status Flags Print Engine Status
Bit 0 Print Engine Power (1 = powered)
Bit 1 Print Engine Error (1 = error)
Bit 2 Ribbon Low
Bit 3 Label Out
Bit 4 Online/Data Ready
Bit 7 Panther Fault
1 Status Flags Applicator Status
Bit 0 Cycle Complete (1 = done)
Bit 1 Applicator Home (1 = home position)
Bit 3 LOTAR - Label On Tamp And Ready
2-3 32-bit int Applicator Cycle Time (milliseconds)
4-5 32-bit int Print Cycle Time (milliseconds)
6-7 32-bit int Current Error Code
8 16-bit int Scanner Status (if equipped)

Holding Registers (Controller → Panther)

Write these registers to send commands:

Register Bits/Range Description
0 Command Flags Control Commands
Bit 0 TRIGGER 1 - Initiate print/apply cycle
Bit 1 TRIGGER 2 - Optional second trigger
Bit 2 RESET - Clear errors
Bit 3 BYPASS - Bypass current cycle
Bit 5 Product Height Submit
1-2 32-bit int Product Height (millimeters, optional)

Critical Notes

  1. LOTAR Sensor: Optional hardware. If not installed, you must trigger the applicator immediately after sending ZPL to the print engine (no wait for LOTAR).

  2. Trigger Timing: Set Trigger 1 bit HIGH to start cycle, then set LOW. The PLC detects the rising edge.

  3. Error Recovery: Always read Register 0 Bit 7 (Panther Fault). If set, read error code from Registers 6-7, then write Reset bit (Register 0 Bit 2).

  4. Cycle Completion: Poll Register 1 Bit 0 (Cycle Complete) until it goes HIGH. Typical cycle time: 1-3 seconds.


Zebra ZE521-6 Print Engine

The Zebra print engine accepts ZPL (Zebra Programming Language) commands via multiple interfaces.

Communication Options

  1. Ethernet (Recommended):

    • Port: 9100 (raw TCP socket)
    • Protocol: Send ZPL as plain text
    • IP Configuration: Set via front panel or web interface
  2. Serial (RS-232):

    • Baud rate: 9600-115200
    • Data bits: 8, Parity: None, Stop bits: 1
    • Hardware flow control recommended
  3. USB:

    • Appears as printer device
    • Can send ZPL directly via print spool

ZPL Format for 6" x 4" Labels

The IT-01 service provides ZPL, but if generating manually:

^XA                          # Start format
^FO50,50^A0N,40,40^FDShipping Label^FS   # Text field
^FO50,100^GB700,3,3^FS      # Horizontal line
^FO50,120^A0N,30,30^FDTracking: 1Z999AA10123456^FS
^FO50,160^A0N,25,25^FDTo: John Doe^FS
^FO50,190^A0N,25,25^FD123 Main Street^FS
^FO50,220^A0N,25,25^FDLouisville, KY 40292^FS
^FO50,270^GB700,3,3^FS      # Horizontal line
^FO50,370^BY3^BCN,100,Y,N^FD1Z999AA10123456^FS  # Barcode
^XZ                          # End format

Status Queries

Query printer status with ~HS command:

# Send status query
sock.send(b"~HS")
response = sock.recv(1024)
# Parse response for errors, ribbon status, etc.

Configuration

  • Label Size: 6.00" x 4.00" (landscape)
  • Resolution: 203 DPI (standard)
  • Print Mode: Thermal transfer (ribbon required)
  • Media Type: Die-cut labels
  • Darkness: Adjust via ~SD command or front panel

Control State Machine

The system follows this state machine for each print/apply job:

┌─────────────┐
│    IDLE     │ ◄──────────────────────────┐
│ (Waiting)   │                            │
└──────┬──────┘                            │
       │ Tracking Code Received            │
       ▼                                   │
┌─────────────┐                            │
│  FETCHING   │                            │
│ LABEL DATA  │                            │
└──────┬──────┘                            │
       │ IT-01 Response                    │
       ▼                                   │
┌─────────────┐                            │
│  PRINTING   │                            │
│ (Send ZPL)  │                            │
└──────┬──────┘                            │
       │ ZPL Sent                          │
       ▼                                   │
┌─────────────┐                            │
│ WAIT_LOTAR  │ ◄── (Only if sensor       │
│ (Optional)  │      installed)            │
└──────┬──────┘                            │
       │ LOTAR = 1 or Timeout              │
       ▼                                   │
┌─────────────┐                            │
│  APPLYING   │                            │
│(Trigger PLC)│                            │
└──────┬──────┘                            │
       │ Cycle Complete = 1                │
       ▼                                   │
┌─────────────┐                            │
│  COMPLETED  │ ───────────────────────────┘
│ (Log & Done)│
└─────────────┘
       │ Error at any stage
       ▼
┌─────────────┐
│    ERROR    │
│ (Log & Retry│ ───┐
│  or Reset)  │    │ Reset Command
└─────────────┘    │
       ▲           │
       └───────────┘

State Descriptions

  1. IDLE: System ready, waiting for barcode scan
  2. FETCHING: Requesting label data from IT-01
  3. PRINTING: Sending ZPL to Zebra print engine
  4. WAIT_LOTAR: Waiting for label to be ready on tamp (optional)
  5. APPLYING: Applicator cycle in progress
  6. COMPLETED: Job successful, return to IDLE
  7. ERROR: Fault detected, log error, reset if possible

Timeout Values

Configure these in .env:

LOTAR_TIMEOUT=10      # Max wait for LOTAR signal (seconds)
CYCLE_TIMEOUT=30      # Max wait for applicator cycle (seconds)
IT01_TIMEOUT=10       # Max wait for IT-01 response (seconds)

Network Configuration

When deploying to production network:

┌───────────────────────────────────────────────────┐
│           Production Network (Example)            │
│                192.168.1.0/24                     │
├───────────────────────────────────────────────────┤
│                                                   │
│  Controller Service:  192.168.1.50:8000          │
│  Zebra Print Engine:  192.168.1.100:9100         │
│  Panther PLC:         192.168.1.101:502          │
│  Dashboard:           192.168.1.50:8080          │
│  MQTT Broker:         192.168.1.50:1883          │
│                                                   │
│  External IT-01:      (configured URL)            │
└───────────────────────────────────────────────────┘

Update .env file with real IP addresses:

# Zebra Print Engine
ZEBRA_HOST=192.168.1.100
ZEBRA_PORT=9100

# Panther PLC
PANTHER_HOST=192.168.1.101
PANTHER_MODBUS_PORT=502

# IT-01 Service
IT01_URL=http://it01.production.local

Quick Start

Prerequisites

  • Docker and Docker Compose installed
  • Ports 8000, 8080, 5021, 9001, 1883 available

Running the Simulator

  1. Clone or navigate to project directory:

    cd ~/projects/ups-label-system
  2. Start all services:

    docker-compose up -d
  3. Verify services are running:

    docker-compose ps

    You should see 6 containers:

    • ups-controller
    • ups-dashboard
    • ups-mqtt
    • ups-mock-it01
    • ups-mock-zebra
    • ups-mock-panther
  4. Access the dashboard:

    http://localhost:8080
    
  5. Trigger a test job:

    Via Dashboard:

    • Enter tracking code: 1Z999AA901234
    • Click "Trigger Print Job"
    • Watch Event Log for real-time updates

    Via API:

    curl -X POST http://localhost:8000/api/v1/trigger \
      -H "Content-Type: application/json" \
      -d '{"code": "1Z999AA901234"}'
  6. View logs:

    # All services
    docker-compose logs -f
    
    # Specific service
    docker-compose logs -f controller
    docker-compose logs -f mock-panther
  7. Stop the system:

    docker-compose down

Expected Output

When you trigger a job, you'll see:

  1. Dashboard Event Log:

    [15:30:45] ups/jobs/started   Job #1 started: 1Z999AA901234
    [15:30:45] ups/jobs/status    Job #1 status: fetching_label
    [15:30:45] ups/jobs/status    Job #1 status: printing
    [15:30:45] ups/jobs/status    Job #1 status: applying
    [15:30:47] ups/jobs/completed Job #1 completed in 2106ms
    
  2. Recent Jobs Panel: Shows completed job with cycle time

  3. Metrics Panel: Increments job counters

  4. Controller Logs: Full workflow details


Development

Project Structure

ups-label-system/
├── docker-compose.yml          # Service orchestration
├── .env                        # Configuration (create from .env.example)
├── label-sim-dashboard.png    # Dashboard screenshot
│
├── services/
│   ├── controller/             # Main orchestration service
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   ├── main.py            # FastAPI app
│   │   ├── controller.py      # Workflow orchestration
│   │   ├── config.py          # Configuration
│   │   ├── models.py          # Data models
│   │   ├── modbus_server.py   # Modbus TCP server
│   │   ├── mqtt_client.py     # MQTT publisher
│   │   └── clients/
│   │       ├── it01_client.py
│   │       ├── zebra_client.py
│   │       └── panther_client.py
│   │
│   ├── dashboard/             # Web UI
│   │   ├── Dockerfile
│   │   ├── main.py           # FastAPI proxy
│   │   ├── static/css/
│   │   └── templates/
│   │
│   ├── mock-it01/            # Mock label service
│   ├── mock-zebra/           # Mock print engine
│   └── mock-panther/         # Mock PLC
│
├── services/mqtt/            # MQTT broker config
│   └── config/
│       └── mosquitto.conf
│
└── README.md                 # This file

Common Tasks

Rebuild after code changes:

docker-compose up -d --build

View specific service logs:

docker-compose logs -f controller
docker-compose logs -f dashboard
docker-compose logs -f mqtt

Check service health:

curl http://localhost:8000/health  # Controller
curl http://localhost:8080/health  # Dashboard

Monitor MQTT messages:

# Install mosquitto clients
brew install mosquitto  # macOS
apt-get install mosquitto-clients  # Linux

# Subscribe to all topics
mosquitto_sub -h localhost -p 1883 -t "ups/#" -v

Access API documentation:

http://localhost:8000/docs  # Controller API (Swagger)

Testing Individual Components

Test IT-01 Mock:

curl -X POST http://localhost:8001/api/label \
  -H "Content-Type: application/json" \
  -d '{"code": "1Z999AA101234"}' | jq

Test Zebra Mock (requires nc or telnet):

echo "^XA^FO50,50^A0N,40,40^FDTest^FS^XZ" | nc localhost 9100

Test Modbus Server (requires pymodbus library):

from pymodbus.client import ModbusTcpClient

client = ModbusTcpClient('localhost', 5021)
client.connect()

# Read controller status
result = client.read_input_registers(0, 10)
print(result.registers)

client.close()

Documentation

Full technical documentation is maintained in Obsidian:

📁 ~/Documents/ObsidianVault/PROJECTS/UPS-PRINTER/

Key Documents:

  • implementation-01.md - Complete implementation guide
  • ideas.md - Architecture and design decisions
  • system_diagram.md - System architecture diagrams
  • EQUIPT-DOCS/doc-summaries.md - Hardware specifications

License

Internal development project - not for public distribution.

© 2025 - UPS Label Printing System Simulator

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published