server: GEE/Dingtian QR reader endpoint + synchronous ReadOutcome
The reader HTTP-GETs on each scan and beeps/acts on our JSON reply (host-in-the-
loop, synchronous). New route GET/POST /qa/mcardsea.php parses the SDK query,
runs the scan through the read dispatcher (permit match -> permit flow; else
transient exit), and replies the SDK verdict: status 1=valid (beep 2x) /
0=invalid (beep 1x), output, time-sync.
Refactored the read flows to return a ReadOutcome {accepted, direction, reason}
so the reply reflects the real accept/reject decision (ReadDispatcher.dispatch,
ExitFlow.handleAt, PermitFlow.run). Fire-and-forget readers ignore it.
Reader's lane is keyed off its serial (cjihao) as lane_devices.id for now;
endpoint is public (reader has no auth, on the device subnet).
Verified via inject: valid permit QR -> status:1 + open; re-scan -> permit exit;
unknown QR -> status:0; barrier-less lane -> status:0.
This commit is contained in:
@@ -56,9 +56,25 @@ barrier. ([[device-input-flow]] is the analogous push pattern; this one also ret
|
||||
open questions are **moot** — it's HTTP. Wiegand is the reader's *output line* on a valid read
|
||||
(the reply `output` field), not the host transport.
|
||||
|
||||
## Next
|
||||
## As-built (2026-06-16)
|
||||
|
||||
A backend route (like `routes/devices.ts` for the Dingtian push) that parses the GET, **decides**
|
||||
(reuse the permit/exit lookup), replies the JSON verdict, and emits on the `read` bus. The
|
||||
[[device-registry]] `reader` entry can model it for [[first-run-setup]] (server IP/port are set in
|
||||
the vendor tool; the app side is the endpoint). Build-ready — protocol fully known.
|
||||
- **Endpoint** `GET/POST /qa/mcardsea.php` (`apps/server/src/routes/qr-reader.ts`, public — the
|
||||
reader has no auth, sits on the device subnet). Parses `cardid/mjihao/cjihao/status/time`, runs
|
||||
the scan through the **read dispatcher** (permit match → permit flow; else transient exit), and
|
||||
replies the **SDK verdict**: `status` 1=valid(beep 2×)/0=invalid(beep 1×), `output` 0, `time`.
|
||||
- The read flows were refactored to **return a `ReadOutcome` { accepted, direction, reason }** so the
|
||||
endpoint's reply reflects the real accept/reject (the dispatcher decides AND opens the barrier via
|
||||
the flows). A fire-and-forget reader ignores the outcome.
|
||||
- **Lane mapping:** the endpoint keys the reader's `lane_devices` id off the device **serial
|
||||
(`cjihao`)** for now — so assign the reader with `lane_devices.id = <serial>`. Refine when the
|
||||
setup wizard models the reader's server-side identity properly.
|
||||
- Verified via inject: valid permit QR → `status:1` + open; re-scan → permit exit (still valid);
|
||||
unknown QR → `status:0`; reader on a barrier-less lane → `status:0`.
|
||||
|
||||
## Open / to confirm on hardware
|
||||
|
||||
- A **live scan** still hadn't reached the server during bring-up (no beep). With the real endpoint
|
||||
now replying the verdict, re-test: scan → expect a beep + a GET in the server log. If still
|
||||
nothing, it's the reader's scan/trigger/mode (not the server).
|
||||
- **Reader→lane identity:** confirm what the device actually sends as `cjihao`/`mjihao` and align the
|
||||
`lane_devices` assignment (the wizard doesn't yet capture the reader's serial as its id).
|
||||
|
||||
Reference in New Issue
Block a user