Files
parking_solution/wiki/concepts/printer-roles-failover.md
T
julian b2a0471b08 Rongta 80mm printer: driver, role-based failover, live status monitoring
Add the rongta PrinterDevice driver (ESC/POS over raw TCP 9100) and the
device-agnostic pieces around it:

- Roles + failover: each printer declares a role (entry-dispenser/booth-
  receipt) and failoverRank; printer-routing.ts picks the best healthy printer
  and falls back outside->booth for entry tickets (never the reverse).
- Live status: MonitorableDevice.readStatus()/PrinterStatus capability. The
  Rongta driver scrapes the device's own /prn_stat.htm (Cover/Cutter/Paper
  End/Near End/Off-Line) rather than hand-decoding DLE EOT, whose reply bytes
  on this clone don't match the canonical ESC/POS bit layout (verified on
  hardware) -- avoids a false-healthy. Maps to ready/degraded/offline, fail
  safe on an unreachable or unexpected page.
- Server PrinterMonitor polls enabled printers (PRINTER_POLL_MS, default 5s),
  caches latest, emits "printer-status" on change. Exposed via
  GET /api/printers/status and an SSE stream for the booth UI.

Verified against 10.0.10.6: ready when healthy, offline when unreachable
(no throw), bus emits on change and suppresses unchanged reads.

Wiki: new rongta-printer entity, printer-roles-failover and
printer-status-monitoring concepts; BOM/index/log updated.
2026-06-14 20:26:45 +02:00

2.4 KiB

type, tags, sources, updated
type tags sources updated
concept
parking
printer
device
reliability
2026-06-14

Printer roles & failover

A lane runs more than one printer, and the system knows each one's job so it can fail over automatically. This is a reliability decision, not a threat-model one: an entry ticket must still print when the outside dispenser jams or drops off the network.

Roles

Each printer instance (a lane_devices row, category printer) declares a role in its config:

  • entry-dispenser — outside, at the lane. Prints the entry ticket the driver takes.
  • booth-receipt — inside the booth. Prints receipts at exit/payment, AND serves as the backup for entry tickets.

It also declares a failoverRank (higher = preferred within a role) to order multiple printers of the same role deterministically (ties broken by id).

Failover rule (asymmetric, on purpose)

For an entry ticket (wantRole = entry-dispenser): try the entry dispensers (best rank first), then fall back to the booth printer. So a driver still gets a ticket when the outside unit is offline — the operator hands it over from the booth.

The reverse is deliberately not done: a receipt never prints on the outside dispenser. Receipts are a booth-only job; an entry dispenser falling back to print receipts makes no physical sense.

Where the logic lives

  • The driver (rongta) is role-agnostic — role/rank are just config; the transport doesn't care. Keeps device-adapter-pattern swappable.
  • Selection is pure logic in packages/devices/printer-routing.ts: orderForRole() ranks candidates; printWithFailover() attempts the print down the list and throws NoPrinterAvailableError only when every candidate fails.
  • It attempts the print directly rather than racing a healthCheck first — the print is the real reachability test, and a health probe that passes can still be followed by a failed print.

Open: the all-printers-down policy

When printWithFailover exhausts every candidate, what should entry do — raise the barrier with no paper ticket (the plate/lpr-camera is the independent record), or hold? That policy belongs to the entry flow (device-input-flow, fail-state-safety), not the printer layer, and is not yet decided. The signed event (append-only-event-chain) is created regardless of whether paper prints.