qr-reader: gee-qr-reader driver — assign in wizard, resolve lane by serial
The QR reader is a push device and the setup wizard assigns random-UUID ids, so
'id = serial' can't be set via the UI. Add a dedicated gee-qr-reader driver
(reader category) with a single 'serial' config field; the admin assigns it
normally and enters the device's serial (its cjihao).
The QR endpoint now resolves the lane by matching lane_devices.config.serial to
the scan's cjihao (instead of row id == cjihao), so no DB hand-editing. An
unassigned serial resolves to no lane -> status:0, gracefully.
Verified via inject through the real /api/setup/assign: assign {serial:
H05M2AFA} -> .jsp scan with a matching permit QR -> status:1 (accept) + open;
re-scan -> permit exit; unknown card -> status:0; unassigned serial -> status:0.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import { eq, laneDevices, type Db } from "@parking/db";
|
||||
import type { DeviceReadEvent } from "../device-events.js";
|
||||
import type { ReadDispatcher } from "../read-dispatch.js";
|
||||
|
||||
@@ -25,24 +26,39 @@ interface ReaderQuery {
|
||||
time?: string;
|
||||
}
|
||||
|
||||
export async function qrReaderRoutes(app: FastifyInstance, dispatcher: ReadDispatcher): Promise<void> {
|
||||
export async function qrReaderRoutes(
|
||||
app: FastifyInstance,
|
||||
db: Db,
|
||||
dispatcher: ReadDispatcher,
|
||||
): Promise<void> {
|
||||
// Resolve the lane_devices row whose config.serial matches the reader's reported
|
||||
// serial (cjihao). The row id is a normal UUID; the serial is config the admin
|
||||
// enters when assigning the gee-qr-reader. Returns the row id, or null if no
|
||||
// reader is assigned for that serial. (Small device set → scan in JS.)
|
||||
const readerRowIdForSerial = (serial: string): string | null => {
|
||||
if (!serial) return null;
|
||||
const rows = db.select().from(laneDevices).where(eq(laneDevices.category, "reader")).all();
|
||||
const match = rows.find((r) => r.enabled && (r.config as { serial?: string }).serial === serial);
|
||||
return match?.id ?? null;
|
||||
};
|
||||
|
||||
// No auth: the reader is a machine on the isolated device subnet and offers no
|
||||
// auth on its side. Public route, like the Dingtian input push.
|
||||
const handler = async (req: { query: ReaderQuery }) => {
|
||||
const q = req.query;
|
||||
const cardid = (q.cardid ?? "").trim();
|
||||
const mjihao = q.mjihao != null ? Number(q.mjihao) : 0;
|
||||
const serial = (q.cjihao ?? "").trim();
|
||||
|
||||
// The device id we map to a lane is the configured reader's lane_devices id.
|
||||
// The reader sends its own mjihao/cjihao; the admin records that as the device's
|
||||
// config so we can resolve it. For now we key the read on the device serial
|
||||
// (cjihao) as the lane_devices id — see wiki note; refine when assignment lands.
|
||||
const deviceId = (q.cjihao ?? "").trim() || String(mjihao);
|
||||
// Map the reader's serial → its assigned lane_devices row id (the dispatcher
|
||||
// resolves the lane from that row). If unassigned, deviceId stays the serial so
|
||||
// the dispatcher simply finds no lane and rejects (status:0) — never crashes.
|
||||
const deviceId = readerRowIdForSerial(serial) ?? serial;
|
||||
|
||||
let accepted = false;
|
||||
if (cardid) {
|
||||
const read: DeviceReadEvent = {
|
||||
driverId: "gee-qr-er80",
|
||||
driverId: "gee-qr-reader",
|
||||
deviceId,
|
||||
value: cardid,
|
||||
kind: "qr",
|
||||
|
||||
@@ -117,7 +117,7 @@ export async function buildServer(opts: BuildOptions = {}): Promise<FastifyInsta
|
||||
// GEE/Dingtian QR reader: it HTTP-GETs on each scan and beeps/acts on our JSON
|
||||
// verdict (host-in-the-loop, synchronous). Routes the read through the dispatcher
|
||||
// and replies the SDK verdict. See wiki/entities/gee-qr-er80.md, qrcode-sdk.md.
|
||||
await qrReaderRoutes(app, readDispatcher);
|
||||
await qrReaderRoutes(app, db, readDispatcher);
|
||||
|
||||
// Pay station (pay-on-foot): quote an open session against the active tariff +
|
||||
// take payment → signed `payment` event. See wiki/concepts/tariff.md.
|
||||
|
||||
Reference in New Issue
Block a user