No description
  • Python 94.5%
  • Shell 5.5%
Find a file
2026-05-18 14:09:25 +02:00
driver_sanei_sk1-311 update driver 2026-05-08 14:39:59 +02:00
.env.example update driver 2026-05-08 14:39:59 +02:00
.gitignore aggiunte a .gitignore più correzione path installazione ticket-printer.service 2026-05-18 14:09:25 +02:00
env_loader.py update driver 2026-05-08 14:39:59 +02:00
health_check.py update driver 2026-05-08 14:39:59 +02:00
install_printer.sh matching name 2026-05-15 15:37:20 +02:00
printer.py update driver 2026-05-08 14:39:59 +02:00
qr_handler.py update driver 2026-05-08 14:39:59 +02:00
README.md update driver 2026-05-08 14:39:59 +02:00
redis_listener.py update driver 2026-05-08 14:39:59 +02:00
requirements.txt Initial commit 2026-05-08 12:57:16 +02:00
test_redis_integration.py update driver 2026-05-08 14:39:59 +02:00
test_stampante.py update driver 2026-05-08 14:39:59 +02:00
ticket-printer.service aggiunte a .gitignore più correzione path installazione ticket-printer.service 2026-05-18 14:09:25 +02:00

Sistema di Stampa Ticket - Sanei SK1-311

Sistema di stampa per ticket termici su stampante Sanei SK1-311 via USB. Progetto sviluppato per sostituire CUPS che presenta problemi con i driver obsoleti della stampante, utilizzando comunicazione USB diretta tramite driver Python personalizzato.

📋 Indice

Caratteristiche

  • Stampa diretta USB: Comunicazione diretta con la stampante Sanei SK1-311 senza passare per CUPS
  • Generazione ticket: Creazione automatica di ticket con numero di coda, QR code e loghi personalizzati
  • Gestione QR Code: Generazione e decodifica di QR code per i ticket
  • Download logo dinamico: Scaricamento automatico dei loghi aziendali da URL
  • Driver ESC/POS: Implementazione completa del protocollo ESC/POS per stampanti termiche
  • Integrazione Redis: Listener pub/sub per ricezione e stampa automatica ticket in tempo reale
  • Health-check HTTP: Endpoint /health e /stats per monitoraggio servizio
  • Servizio systemd: Auto-start al boot con restart automatico
  • Retry automatico: Riconnessione automatica a Redis con backoff esponenziale

🔧 Requisiti

Sistema Operativo

  • Linux (testato su Ubuntu/Debian)
  • Python 3.8 o superiore

Dipendenze di Sistema

# libusb per la comunicazione USB
sudo apt install libusb-1.0-0-dev

# Ghostscript per il rendering PDF (opzionale ma raccomandato)
sudo apt install ghostscript

# Font TrueType per la generazione dei ticket
sudo apt install fonts-dejavu fonts-liberation

Python

Tutte le dipendenze Python sono specificate in requirements.txt.

📦 Installazione

1. Clona o scarica il progetto

cd /percorso/progetto/migrazione_cups_sanei

2. Crea un ambiente virtuale (raccomandato)

python3 -m venv venv
source venv/bin/activate

3. Installa le dipendenze

pip install -r requirements.txt

4. Installa il driver SK1 (modalità editable)

pip install -e driver_sanei_sk1-311/

Questo installerà il driver in modalità "editable", permettendo modifiche al codice senza doverlo reinstallare.

5. Configura i permessi USB

Per evitare errori di permesso (Access denied), crea una regola udev:

# Identifica VID/PID della stampante
lsusb | grep -i seiko

# Crea file di regole udev
sudo nano /etc/udev/rules.d/99-sk1.rules

Aggiungi questa riga (sostituisci XXXX e YYYY con VID e PID):

SUBSYSTEM=="usb", ATTR{idVendor}=="XXXX", ATTR{idProduct}=="YYYY", MODE="0666"

Oppure usa il manufacturer:

SUBSYSTEM=="usb", ATTRS{manufacturer}=="Seiko Instruments", MODE="0666"

Ricarica le regole:

sudo udevadm control --reload-rules
sudo udevadm trigger

In alternativa, aggiungi l'utente al gruppo lp:

sudo usermod -a -G lp $USER
# Logout e login per applicare le modifiche

⚙️ Configurazione

File .env

Crea un file .env nella root del progetto con le seguenti variabili:

# Timeout per il download dei loghi (in secondi)
TIMEOUT_LOGO=10

# Dominio base per i loghi
DOMAIN=https://esempio.com

# ID del sito cliente (per identificazione)
CUSTOMER_SITE_ID=123

# Altre configurazioni opzionali...

Il file .env viene caricato automaticamente all'avvio tramite il modulo env_loader.py.

📁 Struttura del Progetto

migrazione_cups_sanei/
│
├── README.md                    # Questo file
├── requirements.txt             # Dipendenze Python del progetto principale
├── .env                         # Variabili d'ambiente (da creare)
│
├── env_loader.py               # Caricamento automatico variabili d'ambiente
├── printer.py                  # Modulo principale per generazione e stampa ticket
├── qr_handler.py               # Gestione QR code (generazione, decodifica, ridimensionamento)
├── redis_listener.py           # Listener Redis pub/sub con auto-reconnect
├── health_check.py             # Server HTTP health-check e statistiche
├── test_stampante.py           # Script di test per verificare il funzionamento
├── test_redis_integration.py   # Test integrazione Redis end-to-end
├── ticket-printer.service      # Unit systemd per auto-start
├── install_service.sh          # Script installazione servizio systemd
│
└── driver_sanei_sk1-311/       # Driver USB per stampante Sanei SK1-311
    ├── README.md               # Documentazione dettagliata del driver
    ├── pyproject.toml          # Configurazione packaging del driver
    ├── requirements.txt        # Dipendenze del driver
    ├── CHANGELOG.md            # Changelog del driver
    ├── base_test.py            # Test base del driver
    │
    └── src/
        └── sk1_driver/         # Package del driver
            ├── __init__.py     # Inizializzazione e export principali
            ├── cli.py          # Interfaccia command-line
            ├── sk1.py          # Classe principale SK1Printer
            ├── command_builder.py  # Builder per comandi ESC/POS
            ├── usb_utils.py    # Utilities per comunicazione USB
            ├── pdf_utils.py    # Rendering PDF per stampa
            ├── image_ops.py    # Operazioni su immagini
            └── temp_utils.py   # Gestione file temporanei

Componenti Principali

1. env_loader.py

Carica automaticamente le variabili d'ambiente dal file .env all'import del modulo. Supporta fallback a .env.cups se .env non esiste.

2. printer.py

Modulo principale che gestisce:

  • Generazione dell'immagine del ticket (usando PIL/Pillow)
  • Download dei loghi da URL remoti
  • Rendering del testo con font TrueType
  • Integrazione QR code
  • Invio comandi alla stampante tramite il driver SK1

Funzioni principali:

  • stampa_ticket(ticket_data): Funzione principale per stampare un ticket
  • scarica_logo(logo_url): Download logo da URL
  • draw_centered_text(): Rendering testo centrato
  • get_font(size): Ottiene font TrueType

3. qr_handler.py

Gestione completa dei QR code:

  • genera_qr_code(data): Genera un QR code da stringa
  • decodifica_qr_code(qr_data): Decodifica QR da base64
  • ridimensiona_qr_code(qr_image, dimensione): Ridimensiona QR code

4. test_stampante.py

Script standalone per testare la stampante con un ticket di esempio. Verifica:

  • Presenza della stampante USB
  • Permessi corretti
  • Funzionamento completo della stampa

5. driver_sanei_sk1-311/

Driver Python completo per la stampante Sanei SK1-311:

  • Implementazione protocollo ESC/POS
  • Comunicazione USB diretta tramite PyUSB
  • Supporto per stampa testo, immagini e PDF
  • Ottimizzazioni per prestazioni (single-write, chunking)
  • CLI per comandi rapidi

Consultare driver_sanei_sk1-311/README.md per documentazione dettagliata.

<EFBFBD> Integrazione Redis

Il sistema utilizza Redis pub/sub per ricevere e stampare automaticamente i ticket in tempo reale.

Come Funziona

Server Applicativo       Redis           Raspberry Pi
      │                    │                    │
      │── PUBLISH ────────>│                    │
      │   ticket:created   │── messaggio ──────>│ redis_listener.py
      │   {ticket JSON}    │                    │   ├─ filtra customer_site_id
      │                    │                    │   ├─ genera immagine ticket
      │                    │                    │   └─ stampa via USB (SK1-311)

Configurazione .env

# Redis
REDIS_HOST=10.0.11.4
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_CHANNEL=ticket:created

# Filtro: solo ticket con questo site_id vengono stampati
CUSTOMER_SITE_ID=87

# Heartbeat log ogni N secondi
HEARTBEAT_INTERVAL=30

# Porta health-check HTTP (default: 8080)
HEALTH_CHECK_PORT=8080

Formato Messaggio Redis

Il server applicativo deve pubblicare un JSON sul canale ticket:created:

{
  "ticket_id": "TKT-20260206-001",
  "ticket_number": 42,
  "queue_name": "Sportello Anagrafe",
  "queue_code": "A",
  "site_name": "Comune di Milano",
  "customer_site_id": "87",
  "official_logo_url": "/uploads/logo.png",
  "qr_code": null,
  "waiting_count": 3,
  "date": "06/02/2026",
  "time": "14:30"
}

Avvio Manuale

source venv/bin/activate
python redis_listener.py

Installazione come Servizio (auto-start al boot)

sudo bash install_service.sh

Comandi di gestione:

sudo systemctl status  ticket-printer   # Stato
sudo systemctl restart ticket-printer   # Riavvio
sudo systemctl stop    ticket-printer   # Arresto
journalctl -u ticket-printer -f         # Log in tempo reale

Health-Check e Monitoring

# Stato di salute
curl http://localhost:8080/health
# {"status": "ok", "redis": true, "printer": true, ...}

# Statistiche stampa
curl http://localhost:8080/stats
# {"uptime": "2:30:15", "tickets_ok": 47, "tickets_err": 0, ...}

Stati possibili:

  • ok: Redis connesso e stampante disponibile
  • degraded: solo uno dei due attivo
  • down: nessuno dei due attivo

Test Integrazione

# Terminal 1: avvia il listener
python redis_listener.py

# Terminal 2: esegui test end-to-end
python test_redis_integration.py

Il test verifica: connessione Redis, pubblicazione ticket, filtro site_id, health-check e statistiche.

Comportamento Retry

  • Connessione iniziale: massimo 5 tentativi con backoff esponenziale (1s, 2s, 4s, 8s, 16s)
  • Disconnessione durante l'ascolto: riconnessione automatica infinita
  • Servizio systemd: riavvio automatico dopo 10s in caso di crash

<EFBFBD>🚀 Utilizzo

Test Rapido della Stampante

Esegui lo script di test per verificare che tutto funzioni:

python test_stampante.py

Questo script:

  1. Verifica che la stampante sia collegata
  2. Crea un ticket di test
  3. Lo stampa
  4. Fornisce feedback sul risultato

Uso Programmatico

from printer import stampa_ticket
from datetime import datetime

# Prepara i dati del ticket
ticket_data = {
    'ticket_id': 'ABC-123',
    'ticket_number': 42,
    'queue_name': 'Sportello 1',
    'queue_code': 'A',
    'site_name': 'Ufficio Centrale',
    'customer_site_id': '001',
    'official_logo_url': '/path/to/logo.png',  # URL relativo o None
    'qr_code': 'dati_qr_in_base64',  # Base64 o None
    'waiting_count': 5,
    'date': datetime.now().strftime('%d/%m/%Y'),
    'time': datetime.now().strftime('%H:%M')
}

# Stampa il ticket
success = stampa_ticket(ticket_data)

if success:
    print("Ticket stampato con successo!")
else:
    print("Errore durante la stampa")

Uso del Driver direttamente

from sk1_driver import SK1Printer, find_sk1_devices

# Trova la stampante
devices = find_sk1_devices()
if not devices:
    raise SystemExit("Nessuna stampante SK1 trovata")

# Crea istanza del printer
printer = SK1Printer(left_mm=5, top_mm=5)

try:
    # Apri connessione
    printer.open(device=devices[0]["device"])
    printer.init()

    # Stampa testo
    printer.print_text("Ciao Mondo!", bold=True, size="large", align="center")
    printer.feed(2)

    # Taglia carta
    printer.cut(full=False)

finally:
    printer.close()

Comandi CLI del Driver

Il driver include anche una CLI per operazioni rapide:

# Lista stampanti collegate
sk1-cli list

# Stampa di test
sk1-cli test --left-mm 5 --top-mm 5

# Stampa un PDF
sk1-cli print-pdf documento.pdf --width-mm 80 --left-mm 5

# Stampa un'immagine
sk1-cli print-image logo.png --width-mm 60 --align center

# Pulisci file temporanei
sk1-cli clean-temp

🔍 Funzionamento

Flusso di Stampa Ticket

  1. Ricezione Dati: I dati del ticket arrivano (es. tramite Redis pub/sub o chiamata diretta)
  2. Caricamento Configurazione: env_loader carica variabili d'ambiente
  3. Preparazione Immagine:
    • Crea canvas bianco (576px larghezza, ~203 DPI)
    • Scarica logo se presente
    • Genera o decodifica QR code
    • Renderizza testo (numero coda, informazioni, data/ora)
  4. Conversione per Stampa:
    • Converte immagine in bianco/nero 1-bit
    • Ottimizza per stampante termica
  5. Invio alla Stampante:
    • Apre connessione USB
    • Invia comandi ESC/POS di inizializzazione
    • Trasmette immagine del ticket
    • Taglia la carta
    • Chiude connessione

Protocollo ESC/POS

Il driver implementa i comandi standard ESC/POS:

  • ESC @: Inizializzazione stampante
  • GS L: Impostazione margine sinistro (hardware)
  • ESC J: Feed linee con precisione
  • **ESC ***: Stampa immagini in modalità bit
  • GS V: Taglio carta (full o partial)

Ottimizzazioni

  • Single-write mode: Invio dell'intera immagine in un'unica operazione USB per massima velocità
  • Band optimization: Salto automatico delle bande bianche
  • Chunking intelligente: Suddivisione dati in chunk ottimali per USB
  • Hardware margins: Uso di margini hardware invece di padding software

Gestione Errori

Il sistema include gestione robusta degli errori:

  • Retry automatici: In caso di disconnessione USB
  • Fallback: Da single-write a chunked mode se necessario
  • Timeout configurabili: Per operazioni USB e download
  • Logging dettagliato: Per debugging

🧪 Test

Test Manuale Completo

# 1. Verifica che la stampante sia visibile
lsusb | grep -i seiko

# 2. Verifica permessi
sk1-cli list

# 3. Test di stampa semplice
sk1-cli test

# 4. Test con script Python
python test_stampante.py

Test di Resilienza (Listener/Servizio)

Esegui questi test con il listener attivo (manuale o via systemd):

Scenario Come simularlo Cosa ti aspetti
Crash sudo kill -9 $(pgrep -f redis_listener.py) Riparte entro 10s (systemd)
Rete giu sudo ip link set wlan0 down per 30s, poi sudo ip link set wlan0 up Listener resta vivo, riprende da solo
Reboot soft sudo reboot Riparte al boot
Hard reboot Stacca corrente, riattacca Riparte al boot, log mostra wait per time-sync

Test del Driver Standalone

cd driver_sanei_sk1-311
python base_test.py

Verifica Configurazione

# Verifica che le variabili d'ambiente siano caricate
python -c "import env_loader; import os; print(os.environ.get('DOMAIN'))"

🔧 Risoluzione Problemi

Stampante non trovata

# Verifica collegamento USB
lsusb | grep -i seiko

# Verifica modulo kernel
lsmod | grep usb

# Se usblp è caricato, rimuovilo
sudo modprobe -r usblp

Errore "Access denied" (Errno 13)

# Verifica permessi
ls -l /dev/bus/usb/*/*

# Riconfigura udev (vedi sezione Installazione)
sudo nano /etc/udev/rules.d/99-sk1.rules

# Oppure aggiungi utente al gruppo
sudo usermod -a -G lp $USER

Errore "Resource busy"

# Rimuovi driver kernel usblp
sudo modprobe -r usblp

# Verifica che nessun altro processo stia usando la stampante
lsof | grep usb

Font non trovati

# Installa font
sudo apt install fonts-dejavu fonts-liberation

# Verifica installazione
fc-list | grep -i dejavu

Download logo fallito

  • Verifica la variabile DOMAIN nel file .env
  • Verifica che TIMEOUT_LOGO sia sufficientemente alto
  • Controlla la connessione internet
  • Verifica che l'URL del logo sia corretto

Stampa lenta

  • Usa modalità single-write (default)
  • Riduci DPI dei PDF se necessario
  • Verifica che Ghostscript sia installato per rendering veloce
  • Controlla i timeout USB (--usb-timeout-ms)

File temporanei accumulati

# Pulisci file temporanei PDF
sk1-cli clean-temp

# Pulisci e rimuovi directory
sk1-cli clean-temp --remove-dir

📚 Riferimenti

📝 Note

  • Il sistema è ottimizzato per stampanti termiche da 80mm (72mm area stampabile)
  • DPI standard: 203 (8 dots/mm)
  • Supporta solo Linux per la comunicazione USB diretta
  • CUPS non è utilizzato in questo progetto per problemi con driver obsoleti
  • Il driver gestisce automaticamente il detach del kernel driver se necessario

🤝 Supporto

Per problemi o domande:

  1. Consulta la sezione Risoluzione Problemi
  2. Verifica i log dettagliati con --verbose nella CLI
  3. Controlla la documentazione del driver per dettagli tecnici

Versione: 2.0 Ultimo aggiornamento: Febbraio 2026