zplCloud Blog
Etiketten mit zplCloud und Azure Service Bus drucken - ZPL oder JSON-Payloads
Zwei Payload-Formen, eine Queue: ein JSON-Record, der ein Design füllt, oder rohes ZPL in einem Envelope. PeekLock, damit nichts verloren geht und nichts doppelt druckt.
Die Queue ist bereits der Auslöser
Wenn deine Systeme ohnehin über Azure Service Bus sprechen, ist der Druckauftrag eine Nachricht wie jede andere. Eine Data-Streaming-Subscription vom Typ Service Bus konsumiert eine Queue oder eine Topic-Subscription und druckt jede Nachricht - kein Polling-Job, keine Druck-Middleware, kein Windows-Druckserver zwischen Bus und Drucker.
Interessant wird es dadurch, dass eine Nachricht zweierlei sein kann, und zplCloud entscheidet das je Nachricht:
- Ein JSON-Record - die Objekt-Keys füllen die Bindings eines Label-Designs. Richtig, wenn der Sender die Daten kennt, nicht das Layout.
- Ein ZPL-Envelope - ein JSON-Objekt, das fertiges ZPL transportiert. Richtig, wenn der Sender bereits ZPL erzeugt und es nur noch auf einen Drucker braucht.
Beides läuft über dieselbe Subscription; einen Modus stellst du nicht ein.
Subscription einrichten
| Feld | Wert |
|---|---|
| Typ | Service Bus |
| Verbindungskonfiguration | der Namespace-Connection-String |
| Topic | Queue-Name oder topic/Subscriptions/subscriptionName |
| Label-Design | das Design für JSON-Records |
| Drucker | der Standard-Zieldrucker |
Der Connection-String wird AES-verschlüsselt gespeichert.
Queue oder Topic entscheidet das Feld Topic per Konvention: Enthält der Wert /Subscriptions/, wird er als topic/Subscriptions/subscriptionName behandelt, sonst als Queue-Name. Alternativ lässt du das Feld leer und schreibst EntityPath=printer in den Connection-String.
Subscriptions werden nicht automatisch angelegt. Die Topic-Subscription einmal im Portal, im Service Bus Explorer oder im Emulator anlegen (topic.1/Subscriptions/subscription.1); zplCloud konsumiert sie nur.
Anders als die Kafka-Integration läuft Service Bus ausschließlich cloudseitig - der Consumer sitzt im Backend und verbindet sich ausgehend. Eine On-Prem-Agent-Variante gibt es nicht, weil ein Service-Bus-Namespace ohnehin aus dem Internet erreichbar ist.
Payload A - ein JSON-Record für ein Design
{ "ean": "4006381333930", "qty": 2, "dest": "Rampe 4" }
Keys werden über den Namen den Bindings des Designs zugeordnet. Keys ohne Binding werden ignoriert, Bindings ohne Key rendern leer.
Ist der Body überhaupt kein JSON, wird der gesamte Text als $message gebunden - genug für ein einfeldriges Label.
Ein Renderfehler benennt das verursachende Feld, statt dir eine SDK-Meldung hinzuwerfen:
ZPL-Renderfehler bei qty: Wert 'zwei' → ...
Dahinter steckt meist ein Typ- oder Längenproblem: ein numerisches Feld bekommt ein Wort, oder ein Wert ist länger, als das Feld fassen kann.
Payload B - rohes ZPL im Envelope
Wenn der Sender schon ZPL hat, verpackst du es:
{
"schemaVersion": 1,
"zpl": "^XA^FO50,50^A0N,40,40^FDAus dem Bus^FS^XZ",
"printer": "*"
}
Die Regeln, wann das als Envelope gilt, sind bewusst streng:
- der Body muss ein JSON-Objekt sein,
- er muss eine Eigenschaft
zplals nicht-leeren String haben, - und er muss zusätzlich
printer(String) oderschemaVersion(Zahl) mitbringen.
Die letzte Bedingung ist die wichtige. Ein normaler Daten-Record, der zufällig ein Feld zpl enthält, wird nicht abgefangen und roh an den Drucker geschickt - er läuft weiterhin über den Design-Pfad. Wer das Envelope-Verhalten will, sagt es ausdrücklich über printer oder schemaVersion.
Im Envelope-Modus wird das Design gar nicht angefasst: Das ZPL geht Byte für Byte an den Drucker.
Drucker je Nachricht wählen
Das printer im Envelope überschreibt für diese Nachricht den Drucker der Subscription. Zwei Sentinels senden an alle:
"*"oder"all"- jeder Drucker im Workspace, Weblink wie Remote-Agent-Drucker.
Jedes Ziel zählt als eigenes Label gegen dein Kontingent - ein Broadcast an acht Drucker sind also acht Labels. Für einen Schichtwechsel-Aushang ist das genau richtig; für ein Etikett je Auftrag ist es ein teurer Fehler.
Zustellsemantik: PeekLock, und was bei einem Druckfehler passiert
Hier entscheidet sich, ob eine Queue-Integration vertrauenswürdig ist. Der Receiver läuft im PeekLock-Modus mit PrefetchCount = 0 - Empfangen ist nicht Löschen, und im Prozess werden keine Nachrichten gepuffert. Ein Stopp oder Neustart der Subscription verschluckt oder verdoppelt deshalb nie einen Schwung.
| Ergebnis | Was mit der Nachricht passiert |
|---|---|
| Gedruckt | Complete - die Nachricht verlässt den Bus |
| Druckfehler, Zustellung < 3 | Abandon - erneute Zustellung, davor 10 s Backoff |
| Druckfehler, Zustellung ≥ 3 | Dead-Letter mit Grund Print-Fehler (MaxDeliveryCount erreicht) und dem Fehler als Beschreibung |
Zwei Konsequenzen, die man klar sagen sollte:
- Ein Druckfehler stoppt den Consumer nicht. Anders als beim Kafka-Pfad läuft die Subscription weiter und wiederholt; nur die einzelne Nachricht eskaliert in die Dead-Letter-Queue.
- Erfolg heißt „die Bytes sind am Drucker-Socket angekommen". Ein Zebra quittiert ein ZPL-Label nie; auf eine Bestätigung zu warten hieße 20 Sekunden Blockade und ein doppelter Ausdruck.
Completepassiert, sobald das Senden geklappt hat - die Debug-Spalte zeigt dafürsent (no response).
Weil fehlgeschlagene Nachrichten ins Dead-Letter wandern statt ewig zu retryen, ist die DLQ deine Liste der Etiketten, die nicht herausgekommen sind. Genau damit kann man arbeiten.
Keepalive im Leerlauf
Kommt 60 Sekunden lang nichts, schickt die Subscription ein Wake-Kommando an den Zieldrucker. Link-OS-Drucker gehen in einen Energiesparzustand; ohne das zahlt die erste Nachricht nach einer ruhigen Phase die Aufwachzeit. Das Keepalive läuft als Fire-and-forget, damit es die Empfangsschleife nicht blockiert und keinen Stau erzeugt.
Was du im Betrieb siehst
Jede Subscription hält einen Ringpuffer der letzten 50 Nachrichten mit Wertvorschau, Druckergebnis und einem End-to-End-Trace:
bus=12ms design=3ms zpl=18ms print=64ms total=85ms
bus ist die Zeit zwischen Enqueue und Empfang - das ist die Latenz der Queue, nicht deine. Die Debug-Spalte hält zusätzlich Drucker, ZPL-Bytezahl, HTTP-Status und die Rohantwort fest.
Fehlgeschlagene Nachrichten landen außerdem in einem persistenten Fehlerlog, aber nur bei der ersten Zustellung. Ohne diese Bremse würden die Service-Bus-eigenen Retries dieselbe schlechte Nachricht dreimal ablegen.
Kontingente
Jedes gestreamte Label zählt als Render gegen den Tarif des Subscription-Eigentümers. Beim Aufbrauchen erscheinen zwei unterschiedliche Meldungen:
Render-Limit erreicht (…/… Labels diesen Monat, Tarif …)- das monatliche Render-Budget,Streaming-Kontingent erreicht (…)- speziell die Streaming-Grenze.
Starter enthält Streaming zum Testen, begrenzt auf 100 gestreamte Labels pro Monat. Pro enthält 2 Streaming-Endpunkte (Kafka oder Service Bus), jeder weitere kostet 10 € pro Monat. Siehe Preisseite.
Welchen Payload solltest du senden?
| JSON-Record | ZPL-Envelope | |
|---|---|---|
| Der Sender muss kennen | die Feldnamen | das komplette Label-Layout |
| Layout-Änderung | Design bearbeiten, Sender bleiben unverändert | jeder Sender muss neu ausgerollt werden |
| Drucker je Nachricht | Drucker der Subscription | printer, inkl. *-Broadcast |
| Fonts, Barcodes, RFID | erledigt das Design | deine Verantwortung |
Sende Records, wenn du kannst. Das Design bleibt für die Leute editierbar, denen das Etikett gehört, und eine Layout-Änderung erzwingt kein Release in dem System, das die Nachrichten veröffentlicht. Envelopes sind richtig, wenn das ZPL aus einem System kommt, das du nicht kontrollierst - oder wenn eine Nachricht mehrere Drucker gleichzeitig erreichen muss.