From b1a793302d571efe14dba27562d03a6073c64191 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Mon, 7 Sep 2026 11:22:14 +1000 Subject: [PATCH] Printer Debug 2 --- README.md | 16 +++++++ package.json | 3 +- scripts/print-test.mjs | 105 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 scripts/print-test.mjs diff --git a/README.md b/README.md index e98d84c..94445e5 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,22 @@ different and only one will suit how you hang them. type and the colour option. It is not itself a saved setting — the three fields it fills are what get stored, which is why it can appear to "revert" when the same dimensions describe two rolls. +**Diagnostics from the command line.** When the console is not enough: + +```bash +docker compose exec visitor-signin node scripts/print-test.mjs # render and print +docker compose exec visitor-signin node scripts/print-test.mjs --dry # render only +docker compose exec visitor-signin node scripts/print-test.mjs --label 62x100 +``` + +It prints the settings it is using, the exact `brother_ql` command, and the printer's full reply. +`--label` and `--rotate` override the saved settings for one run, so alternatives can be tried +without saving anything. + +**Continuous versus die-cut matters.** `62` means a continuous roll cut to length; `62x100` means +pre-cut labels. Sending one id to the other kind of roll is refused as a wrong roll, and this is +the most common cause of that error after the two-colour setting. + **Diagnostics.** The site card has a **Diagnostics** button showing exactly what would be sent, including the `brother_ql` command line, so it can be run by hand on the host. A failed test print shows the printer's own words rather than a summary. diff --git a/package.json b/package.json index 7550009..0e2576e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "start": "node src/server.js", "dev": "node --watch src/server.js", "version": "node scripts/version.mjs", - "gen-secret": "node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"" + "gen-secret": "node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"", + "print-test": "node scripts/print-test.mjs" }, "engines": { "node": ">=20" diff --git a/scripts/print-test.mjs b/scripts/print-test.mjs new file mode 100644 index 0000000..ef1c4fc --- /dev/null +++ b/scripts/print-test.mjs @@ -0,0 +1,105 @@ +/** + * Renders a badge and prints it, showing everything on the way through. + * + * For working out why a printer will not accept a job, without going through the + * kiosk or the admin console. Run inside the container: + * + * docker compose exec visitor-signin node scripts/print-test.mjs + * docker compose exec visitor-signin node scripts/print-test.mjs --label 62x100 + * docker compose exec visitor-signin node scripts/print-test.mjs --dry + * + * Options: + * --site N which site to use (default: the first one) + * --label ID override the roll id sent to the printer, without saving it + * --rotate DEG override the rotation, without saving it + * --dry render only, do not print + */ +import fs from 'node:fs'; +import { execFileSync } from 'node:child_process'; +import db from '../src/db.js'; +import config from '../src/config.js'; +import * as printer from '../src/printer.js'; + +function arg(name, fallback = null) { + const i = process.argv.indexOf(`--${name}`); + return i > -1 && process.argv[i + 1] && !process.argv[i + 1].startsWith('--') + ? process.argv[i + 1] + : fallback; +} +const has = (name) => process.argv.includes(`--${name}`); + +const siteId = Number(arg('site', 0)); +const site = siteId + ? db.prepare('SELECT * FROM sites WHERE id = ?').get(siteId) + : db.prepare('SELECT * FROM sites ORDER BY id LIMIT 1').get(); + +if (!site) { + console.error('No sites exist yet.'); + process.exit(1); +} + +if (arg('label')) site.printer_label = arg('label'); +if (arg('rotate')) site.printer_rotate = Number(arg('rotate')); + +const label = printer.labelFor(site); +const target = `tcp://${site.printer_host}:${site.printer_port || 9100}`; + +console.log(''); +console.log(` site ${site.name}`); +console.log(` printer ${site.printer_model || 'QL-820NWB'} at ${target}`); +console.log(` server printing ${site.printer_enabled ? 'on' : 'OFF — the kiosk would print instead'}`); +console.log(` badge ${site.badge_width_mm} x ${site.badge_height_mm} mm, rotate ${site.printer_rotate || 0}`); +console.log(` roll setting ${site.printer_label || '(unset)'} -> --label ${label}`); +console.log(` red requested ${Boolean(site.badge_accent)}, will print ${printer.accentWillPrintRed(site)}`); + +const png = await printer.renderBadgePng(printer.sampleVisit(site), site); +const file = '/tmp/print-test.png'; +fs.writeFileSync(file, png); +console.log(` rendered ${png.readUInt32BE(16)} x ${png.readUInt32BE(20)} dots -> ${file}`); + +if (!site.printer_host) { + console.error('\n No printer address set for this site. Set one in Admin -> Sites -> Edit.'); + process.exit(1); +} + +if (has('dry')) { + console.log('\n --dry given, so nothing was sent.\n'); + process.exit(0); +} + +const args = [ + '--backend', 'network', + '--model', site.printer_model || 'QL-820NWB', + '--printer', target, + 'print', + '--label', label, + file, +]; + +console.log(''); +console.log(` running: ${config.printing.command} ${args.join(' ')}`); +console.log(''); + +try { + const out = execFileSync(config.printing.command, args, { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: config.printing.timeoutMs, + }); + console.log(out.trim() || ' (no output)'); + console.log('\n Sent. If nothing came out, the printer rejected it silently — check its display.\n'); +} catch (err) { + console.error(' FAILED\n'); + console.error(`${err.stdout || ''}${err.stderr || ''}`.trim() || err.message); + console.error(''); + console.error(' Roll ids this printer understands:'); + console.error(' 62 62 mm continuous, black only'); + console.error(' 62red 62 mm continuous, black and red (DK-22251)'); + console.error(' 62x100 62 x 100 mm DIE-CUT (pre-cut labels, not a continuous roll)'); + console.error(' 62x29 62 x 29 mm die-cut'); + console.error(''); + console.error(' A continuous roll sent a die-cut id, or the reverse, is refused as a wrong roll.'); + console.error(' Try: node scripts/print-test.mjs --label 62x100'); + console.error(''); + process.exit(1); +}