Files
mesh-catalog/modules/scan/README.md
T
jschoubben 0821718d7c
mesh/merge-gate head 0821718d7ca9 merged into trunk a67f01963f0d — pass: builds new: modules/scan, sent nowhere; no bus step; every machine composes with t…
mesh/repo-check head 0821718d7ca9 merged into trunk a67f01963f0d — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
issue 1440: the scan module's manifest, settings template and README
2026-10-20 20:13:58 +02:00

3.8 KiB

scan — scan a document, crop it, file it, say so

Built for issue 1440 as novox/hq ADR 0464 decides. It scans the glass of a network scanner (any scanner that speaks eSCL, also called AirScan or Mopria), cuts the document out of the lid's white, files it as a one-page PDF in a folder and says scan.added on the bus, so other modules can react. A scan happens when a tool is called: the module runs no process of its own, and it runs as the node's operator account.

What a scan does

  1. Asks the scanner for its glass size and puts one job to it (POST <scanner>/ScanJobs): the platen, the whole glass, at the configured resolution and colour mode, as a JPEG; it fetches the page once.
  2. Reads the resolution from the image's own JFIF density, never from the request: a scanner may answer a 300 dpi request with 200 dpi, and the page size and the event's millimetres follow what it gave.
  3. Crops: the lid's level is measured in the corner farthest from where documents lie, a pixel is document when it is darker than the lid, and the rows and columns holding enough of them give the box. No document, or one filling nearly the whole glass, keeps the whole image (cropped false), and so does crop false.
  4. Files a one-page PDF holding the JPEG: written whole beside its place as .scan-*.tmp and renamed to scan-YYYY-MM-DD-HHMMSS.pdf (local time); a taken name gets -2, -3, and no file is ever overwritten.
  5. Says scan.added once, after the rename, under the file's sha256 as its message id. It never carries the image.

A scanner that refuses or gives no page files nothing and says nothing on the bus; the tool's error says why.

Config

key meaning default
scanner the scanner's eSCL address, such as http://192.0.2.10/eSCL (required, trusted) none
scan-folder the absolute path the PDFs are filed in (required, trusted) none
resolution dpi asked: 100, 200, 300 or 600 300
color-mode color or gray color
crop cut the document out of the lid's white true

The values reach the code in one file, settings.json in the module's state directory, which the mesh writes from the config; the code reads it at every call. With scanner or scan-folder unset, every tool says which and writes and asks nothing.

The scan folder is not a manifest resource: a directory's path cannot take a config value (issue 1441), so the code makes it (mode 0755) when it files the first scan.

Tools

tool does
scan_now scans, crops, files and emits; answers the event's body
scan_status whether the scanner answers, its state (Idle or Processing) and its document feeder's state; a scanner that does not answer is answers: false with why. This is how its health is read: the module has no long-running resource to declare a health check on
scan_list the newest filed scans, newest first (limit, default 20). Read-only

The event

scan.added, on the wire mesh.mod.scan.event.scan.added; a consumer declares scan.scan.added. Its body: path, node, bytes, sha256, width-mm, height-mm, resolution (dpi, as the scanner gave it), color-mode, cropped, scanned-at (RFC 3339, UTC).

Files it does not know

Each directory or archive this module declares says how it treats a file it did not write (ADR 0422, ADR 0416).

  • ${dir:state}: everything in it is the program's; the mesh moves nothing out (ADR 0422 §2).
  • The scan folder (scan-folder) is declared by no manifest. The module writes only the scan-*.pdf files it files and their .scan-*.tmp while they are written; it never changes, moves or removes any other file there, and scan_list only reads.

Tests

The unit tests use the captured answers of a real scanner (cmd/scan/testdata/escl, captured 2026-10-20) served by a test HTTP server; no test reaches a scanner.