From 88f9c53fda3d68c103b294a2f835df9d4b4cfc3e Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Wed, 9 Sep 2026 12:57:58 +0200 Subject: [PATCH] fix(devices): K200L status parser reads a fault's Yes, which the board wraps in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First live run on park-lab with the cover open reported "unexpected status page (missing coverOpen, paperEnd, offline)" — exactly the three fault cells. The board writes a fault as Yes and a clear row as a bare padded No; the parser accepted only tag-free cells. Cell text is now read with inner tags stripped (row-anchored match). Tests pin the verbatim captured markup plus other shapes. Live after the fix: degraded "cover open, paper out, printer off-line"; cover closed → ready. Wiki: the markup on the K200L page; Periphery "not loaded after reboot" (unit never enabled → `systemctl --user enable --now periphery`) as a §7a gotcha in the provisioning runbook; log. Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU --- .../devices/src/drivers/printer-k200l.test.ts | 20 +++++++++++++++++ packages/devices/src/drivers/printer-k200l.ts | 22 ++++++++++++++----- wiki/decisions/appliance-provisioning.md | 6 +++++ wiki/entities/k200l-printer.md | 6 +++++ wiki/log.md | 11 ++++++++++ 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/devices/src/drivers/printer-k200l.test.ts b/packages/devices/src/drivers/printer-k200l.test.ts index 4476b00..8beac03 100644 --- a/packages/devices/src/drivers/printer-k200l.test.ts +++ b/packages/devices/src/drivers/printer-k200l.test.ts @@ -57,6 +57,26 @@ describe("parseStatusPage", () => { it("leaves unknown pages empty rather than guessing", () => { expect(parseStatusPage(INDEX)).toEqual({}); }); + it("reads a fault's Yes, which the board wraps in (captured live, cover open)", () => { + // Verbatim from the unit on 2026-09-09 with the cover open: the three fault cells carry + // markup the No cells don't — the first parser rejected them ("missing coverOpen, + // paperEnd, offline" on the booth) while the No cells parsed. + const page = boardPage() + .replace("Cover Is OpenNo ", "Cover Is OpenYes ") + .replace("Paper EndNo ", "Paper EndYes ") + .replace("Printer Off-LineNo ", "Printer Off-LineYes "); + expect(parseStatusPage(page)).toEqual({ coverOpen: true, cutterError: false, paperEnd: true, paperNearEnd: false, offline: true }); + }); + it("tolerates other markup shapes around a value", () => { + const page = boardPage() + .replace("Paper EndNo ", "Paper EndYes ") + .replace("Printer Off-LineNo ", "Printer Off-Line\r\n\r\nYes\r\n"); + expect(parseStatusPage(page)).toEqual({ coverOpen: false, cutterError: false, paperEnd: true, paperNearEnd: false, offline: true }); + }); + it("reads labels wrapped in markup too", () => { + const page = boardPage({ nearEnd: "Yes" }).replace("Paper Near End", "Paper Near End"); + expect(parseStatusPage(page).paperNearEnd).toBe(true); + }); }); describe("k200lDriver.readStatus over TCP", () => { diff --git a/packages/devices/src/drivers/printer-k200l.ts b/packages/devices/src/drivers/printer-k200l.ts index a1998aa..395a65e 100644 --- a/packages/devices/src/drivers/printer-k200l.ts +++ b/packages/devices/src/drivers/printer-k200l.ts @@ -107,21 +107,33 @@ export function parseRawReply(raw: string): { status: number; body: string } { return { status: Number(m[1]), body }; } +/** A cell's visible text: inner tags stripped (the board wraps a "Yes" in markup the + * "No" cells don't carry), entities and padding normalised, lowercased. */ +function cellText(inner: string): string { + return inner + .replace(/<[^>]*>/g, "") + .replace(/ /gi, " ") + .replace(/\s+/g, " ") + .trim() + .toLowerCase(); +} + /** * Parse the status table into boolean flags. Each fault is a `label - * Yes|No` pair (the board pads the value with spaces). Returns only the + * Yes|No` pair (the board pads the value with spaces, and may wrap a fault's + * "Yes" in its own tags — 2026-09-09, seen live as "missing coverOpen, paperEnd, + * offline" with the cover open, i.e. exactly the Yes cells). Returns only the * recognised fields; a missing field stays undefined so the caller can detect an * unexpected page (fail safe, not a false "ok"). */ export function parseStatusPage(html: string): StatusFlags { const out: StatusFlags = {}; - const rowRe = /]*>([^<]*?)<\/TD>\s*]*>([^<]*?)<\/TD>/gi; + const rowRe = /]*>\s*]*>([\s\S]*?)<\/TD>\s*]*>([\s\S]*?)<\/TD>/gi; let m: RegExpExecArray | null; while ((m = rowRe.exec(html))) { if (m[1] === undefined || m[2] === undefined) continue; - const label = m[1].replace(/ /gi, " ").replace(/\s+/g, " ").trim().toLowerCase(); - const value = m[2].replace(/ /gi, " ").trim().toLowerCase(); - const key = STATUS_FIELDS[label]; + const key = STATUS_FIELDS[cellText(m[1])]; + const value = cellText(m[2]); if (key && (value === "yes" || value === "no")) out[key] = value === "yes"; } return out; diff --git a/wiki/decisions/appliance-provisioning.md b/wiki/decisions/appliance-provisioning.md index 8fee786..2c11a91 100644 --- a/wiki/decisions/appliance-provisioning.md +++ b/wiki/decisions/appliance-provisioning.md @@ -314,6 +314,12 @@ sudo loginctl enable-linger admin # so the user service starts at boot witho **Verify:** `systemctl --user status periphery` → active; the server **`park-buzi`** appears and goes **OK/green** in Core → Servers. Then **delete the onboarding key**. +> **After a reboot: `Unit periphery.service not loaded` (park-lab, 2026-09-09).** The unit +> existed but had never been **enabled**, so nothing started it at boot and `reset-failed` / +> `restart` had nothing to act on. Fix: `systemctl --user daemon-reload && systemctl --user enable +> --now periphery`. Add `enable --now` to the install sequence above whenever the installer's own +> enable did not stick (check with `systemctl --user is-enabled periphery` before leaving). + **➜ Next step is §7b below — the Stack itself is not deployed yet.** A green Server in Core just means the agent connected; it runs nothing until you add the Registry/Git accounts and deploy. diff --git a/wiki/entities/k200l-printer.md b/wiki/entities/k200l-printer.md index 216aa11..8a637d9 100644 --- a/wiki/entities/k200l-printer.md +++ b/wiki/entities/k200l-printer.md @@ -67,6 +67,12 @@ Paper Near End Yes/No Printer Off-Line Yes/No ``` +A fault is written as **`Yes`** while a clear row is a bare, space-padded +`No` — the first parser only accepted tag-free cells, so with the cover open the booth showed +*"unexpected status page (missing coverOpen, paperEnd, offline)"*: exactly the Yes cells. Fixed +the same day (cell text is read with inner tags stripped); the verbatim markup is pinned in the +driver's tests. + **Same rows, same `labelYes|No` shape as the Rongta board's `/prn_stat.htm`** ([[printer-status-monitoring]]) — only the path differs, which is why nobody found it in July (the Rongta driver looked for `/prn_stat.htm`, got nothing, and the unit was filed as "serves no diff --git a/wiki/log.md b/wiki/log.md index 3d9b430..088b8d7 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -3297,3 +3297,14 @@ factory reset), radar (idle level → activeLow), printers (K200L / Rongta / Cas USB; "Test" probes, print a card to verify), the on-site order of work, and the gaps still unrecorded (Cashino/Rongta factory addresses, the reader tool screens, camera activation, where the site record lives). Linked from [[appliance-provisioning]] and [[k200l-printer]]; indexed. + +## [2026-09-09] fix | K200L status parser — a fault's "Yes" is wrapped in +First live run on park-lab (driver `k200l`, LAN, cover open) showed *degraded — unexpected status +page (missing coverOpen, paperEnd, offline)*: precisely the three Yes cells. Captured the raw page +with the cover open: the board writes `Yes` for a fault and a bare +padded `No` otherwise; the parser accepted only tag-free cells. Fix: cell text is read with inner +tags stripped (row-anchored regex); tests pin the verbatim markup plus other shapes (devices 79). +Live after the fix: degraded "cover open, paper out, printer off-line"; closed → ready. User: +"we are good using the network with this printer." Also: park-lab Periphery was "not loaded" +after a reboot — the unit had never been enabled; `systemctl --user enable --now periphery` +recorded as a §7a gotcha on [[appliance-provisioning]]. Pages: [[k200l-printer]].