Skip to main content

Architecture

Data flow

                         ┌──────────────────────────────────────────────┐
print PDF w/ cut paths │ Open Graphtec Server │
──────────────────────► │ │
POST /print/prepare │ cut_extract ──► marks.compute_layout ──┐ │
or inbox/print/ │ (ISO 19593-1 / │ │ │
│ CutContour) ▼ ▼ │
marked print PDF ◄─┤ overlay (pikepdf) GP-GL + TB │
│ │ │
cut-only PDF │ pdf_to_gpgl ────────────────────────► │ │
──────────────────────► │ ▼ │
/jobs/* or inbox/cut/ │ jobs.db │
│ │ │
│ DataLinkServerWorker (one per cutter) │ │
│ ESC.d1 poll ─ barcode ─ job list ─────┘ │
└───────────────┬──────────────────┬───────────┘
│ TCP 9100 │ webhooks
Graphtec cutter(s) your systems

Modules (server/app/)

ModuleResponsibility
main.pyFastAPI app: endpoints, worker lifecycle, auth
config.pyEnvironment-driven settings incl. multi-cutter parsing
protocol.pyData Link wire protocol (ESC.d1–d6), retry discipline, job-name/status validation, cutter query commands
dls_worker.pyPer-cutter polling state machine, standby handling, event log, startup handshake
marks.pyBarcode/mark layout math, CODE39, print overlay, TB-prefixed GP-GL builder — the single source of geometry truth
converters/cut_extract.pyContent-stream walker extracting cut paths (processing steps + spot colors)
converters/pdf_to_gpgl.pyVector-PDF → GP-GL for cut-only ingestion
print_prepare.pyOrchestrates extraction → layout → validation → overlay → job
print_worker.py / ingest_worker.pyHot-folder workers
storage.pySQLite job library + barcode counter
webhook.pyOutbound event notifications
static/index.htmlThe status UI (single file, no build step)

Storage

One SQLite database (jobs.db, WAL mode). The jobs table stores each job complete — name, barcode, registration vector, and the full GP-GL command sequence as a BLOB — so a job is self-contained and served byte-exact. A counters table backs barcode minting. Jobs persist until deleted via DELETE /jobs/{id}.

Design principles

  • One geometry brain. Everything that must agree — printed marks, barcode, registration vector, TB distances, cut coordinates — is computed in one place (marks.py) in one pass. There is no second implementation to drift.
  • Never rewrite customer artwork. Print preparation overlays; it does not re-render. Extraction reads; it does not modify.
  • The cutter is polled, never trusted to push. All liveliness (status, events, webhooks) is synthesized server-side from the poll loop.
  • Fail loudly and precisely. Rejections (zone intrusions, missing cut paths, scale mismatches) carry the exact reason; hot-folder failures leave a .error.txt.
  • Workers never die silently. Poll loops are exception-proof; errors surface in status and the activity log while polling continues.

Concurrency model

FastAPI serves HTTP on the event loop (CPU-heavy PDF work is pushed to threads). Each cutter has a daemon polling thread; each hot folder has one. SQLite access is serialized behind a lock. Per-cutter flock lock files prevent two processes from driving the same machine.