Initial Version PoC
This commit is contained in:
137
client.py
Normal file
137
client.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
import paho.mqtt.client as mqtt
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from datetime import date
|
||||
from brother_ql.raster import BrotherQLRaster
|
||||
from brother_ql.conversion import convert
|
||||
from brother_ql.backends.helpers import send
|
||||
from templates import TEMPLATES
|
||||
|
||||
# Load configuration from environment
|
||||
MQTT_HOST = os.getenv('MQTT_HOST', 'localhost')
|
||||
MQTT_PORT = int(os.getenv('MQTT_PORT', 1883))
|
||||
MQTT_TOPIC_SUB = os.getenv('MQTT_TOPIC_SUB', 'vet/labels/print')
|
||||
MQTT_TOPIC_PUB_ERRORS = os.getenv('MQTT_TOPIC_PUB_ERRORS', 'vet/labels/errors')
|
||||
MQTT_TOPIC_HEARTBEAT = os.getenv('MQTT_TOPIC_HEARTBEAT', 'vet/labels/heartbeat')
|
||||
PRINTER_DEVICE = os.getenv('PRINTER_DEVICE', '/dev/usb/lp0')
|
||||
PRINTER_MODEL = os.getenv('PRINTER_MODEL', 'QL-800')
|
||||
LABEL_SIZE_DEFAULT = os.getenv('LABEL_SIZE_DEFAULT', '29x90')
|
||||
|
||||
def print_label(image, printer=PRINTER_DEVICE, model=PRINTER_MODEL, label=LABEL_SIZE_DEFAULT):
|
||||
"""Print the label directly using brother_ql module"""
|
||||
try:
|
||||
qlr = BrotherQLRaster(model)
|
||||
qlr.exception_on_warning = True
|
||||
|
||||
# Convert the PIL image to instructions
|
||||
instructions = convert(qlr=qlr, images=[image], label=label, cut=True)
|
||||
|
||||
# Send to printer
|
||||
status = send(instructions=instructions, printer_identifier=printer, backend_identifier='linux_kernel', blocking=True)
|
||||
|
||||
return status
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
print(f"Connected to MQTT broker at {MQTT_HOST}:{MQTT_PORT} with result code {rc}")
|
||||
if rc == 0:
|
||||
print(f"Subscribing to topic: {MQTT_TOPIC_SUB}")
|
||||
client.subscribe(MQTT_TOPIC_SUB)
|
||||
print("Subscription successful")
|
||||
else:
|
||||
print(f"Failed to connect, result code: {rc}")
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
try:
|
||||
raw_payload = msg.payload.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
print(f"Received non-UTF-8 message on topic '{msg.topic}': {msg.payload}")
|
||||
return
|
||||
|
||||
print(f"Raw message received on topic '{msg.topic}': {raw_payload}")
|
||||
try:
|
||||
payload = json.loads(raw_payload)
|
||||
print(f"Parsed payload: {payload}")
|
||||
|
||||
template_id = payload.get('template_id', 'vet_label')
|
||||
label_size = payload.get('label_size', LABEL_SIZE_DEFAULT)
|
||||
variables = payload.get('variables', {})
|
||||
test = payload.get('test', False)
|
||||
|
||||
print(f"Processing: template_id={template_id}, label_size={label_size}, test={test}")
|
||||
|
||||
# For now, only support one template
|
||||
if template_id not in TEMPLATES:
|
||||
raise ValueError(f"Unknown template_id: {template_id}")
|
||||
|
||||
template_func = TEMPLATES[template_id]
|
||||
|
||||
# TODO: Adjust dimensions based on label_size if needed
|
||||
# For simplicity, using fixed dimensions
|
||||
|
||||
filename = f"/app/output/label_{template_id}.png" if test else None
|
||||
image = template_func(variables, filename=filename)
|
||||
|
||||
if test:
|
||||
print(f"Test mode: PNG saved as {filename}")
|
||||
else:
|
||||
print("Printing label...")
|
||||
status = print_label(image, label=label_size)
|
||||
print(f"Print status: {status}")
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
error_msg = f"Invalid JSON in message: {e}. Raw payload: {raw_payload}"
|
||||
print(error_msg)
|
||||
client.publish(MQTT_TOPIC_PUB_ERRORS, json.dumps({"error": error_msg, "topic": msg.topic}))
|
||||
except Exception as e:
|
||||
error_msg = f"Error processing message: {str(e)}"
|
||||
print(error_msg)
|
||||
client.publish(MQTT_TOPIC_PUB_ERRORS, json.dumps({"error": error_msg, "original_payload": raw_payload}))
|
||||
|
||||
def heartbeat(client):
|
||||
while True:
|
||||
try:
|
||||
heartbeat_msg = {
|
||||
"status": "alive",
|
||||
"timestamp": time.time(),
|
||||
"host": MQTT_HOST,
|
||||
"port": MQTT_PORT
|
||||
}
|
||||
client.publish(MQTT_TOPIC_HEARTBEAT, json.dumps(heartbeat_msg))
|
||||
print(f"Published heartbeat: {heartbeat_msg}")
|
||||
time.sleep(30) # Publish every 30 seconds
|
||||
except Exception as e:
|
||||
print(f"Error publishing heartbeat: {e}")
|
||||
time.sleep(5) # Retry sooner on error
|
||||
|
||||
def main():
|
||||
client = mqtt.Client()
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
|
||||
print(f"Attempting to connect to MQTT broker at {MQTT_HOST}:{MQTT_PORT}")
|
||||
client.connect(MQTT_HOST, MQTT_PORT, 60)
|
||||
|
||||
# Start the network loop in a background thread
|
||||
client.loop_start()
|
||||
|
||||
# Start heartbeat thread
|
||||
heartbeat_thread = threading.Thread(target=heartbeat, args=(client,))
|
||||
heartbeat_thread.daemon = True
|
||||
heartbeat_thread.start()
|
||||
|
||||
print("Client is running. Press Ctrl+C to stop.")
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1) # Keep main thread alive
|
||||
except KeyboardInterrupt:
|
||||
print("Shutting down...")
|
||||
client.loop_stop()
|
||||
client.disconnect()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user