67 lines
3.9 KiB
Markdown
67 lines
3.9 KiB
Markdown
# brother — 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 |
|
|
|---|---|
|
|
| `scanner.scan` | scans, crops, files and emits; answers the event's body |
|
|
| `scanner.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 |
|
|
| `scanner.list` | the newest filed scans, newest first (`limit`, default 20). Read-only |
|
|
|
|
## The event
|
|
|
|
`scan.added`, on the wire `mesh.mod.brother.event.scan.added`; a consumer declares `brother.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 `scanner.list` only reads.
|
|
|
|
## Tests
|
|
|
|
The unit tests use the captured answers of a real scanner (`cmd/brother/testdata/escl`, captured 2026-10-20) served
|
|
by a test HTTP server; no test reaches a scanner.
|