fix(devices): K200L status parser reads a fault's Yes, which the board wraps in <FONT color=#ff0000>
Build & push images / images (push) Successful in 2m55s
Build & push images / images (push) Successful in 2m55s
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 <FONT color=#ff0000>Yes</FONT> 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
This commit is contained in:
@@ -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 <FONT color=#ff0000> (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 Open</TD><TD style=\"width: 23px\">No ", "Cover Is Open</TD><TD style=\"width: 23px\"><FONT color=#ff0000>Yes</FONT> ")
|
||||
.replace("Paper End</TD><TD style=\"width: 23px\">No ", "Paper End</TD><TD style=\"width: 23px\"><FONT color=#ff0000>Yes</FONT> ")
|
||||
.replace("Printer Off-Line</TD><TD style=\"width: 23px\">No ", "Printer Off-Line</TD><TD style=\"width: 23px\"><FONT color=#ff0000>Yes</FONT> ");
|
||||
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 End</TD><TD style=\"width: 23px\">No ", "Paper End</TD><TD style=\"width: 23px\"><B><FONT color=\"#ff0000\">Yes </FONT></B>")
|
||||
.replace("Printer Off-Line</TD><TD style=\"width: 23px\">No ", "Printer Off-Line</TD>\r\n<TD style=\"width: 23px\">\r\n<font>Yes</font>\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("<TD>Paper Near End</TD>", "<TD><B>Paper Near End</B></TD>");
|
||||
expect(parseStatusPage(page).paperNearEnd).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("k200lDriver.readStatus over TCP", () => {
|
||||
|
||||
@@ -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 `<TD>label</TD>
|
||||
* <TD>Yes|No</TD>` pair (the board pads the value with spaces). Returns only the
|
||||
* <TD>Yes|No</TD>` 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[^>]*>([^<]*?)<\/TD>\s*<TD[^>]*>([^<]*?)<\/TD>/gi;
|
||||
const rowRe = /<TR[^>]*>\s*<TD[^>]*>([\s\S]*?)<\/TD>\s*<TD[^>]*>([\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;
|
||||
|
||||
Reference in New Issue
Block a user