Files
parking_solution/wiki/entities/gee-qr-er80.md
T
julian 392d44d842 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.
2026-06-16 12:12:09 +02:00

81 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: entity
tags: [parking, hardware, readers, qr]
sources: [gee-qr-er80]
updated: 2026-06-16
status: open
---
# GEE-QR-ER80 (QR access reader)
The project's **QR-code reader** (GEE NFC LIMITED). A static optical scanner for **QR /
DataMatrix / 1D barcode**, optional ID/IC card. This is the **[[ticket-encoding|QR ticket]]
scanner** the design called for — read at the pay station and exit lane — and a path for **QR
[[permit]]** credentials. On hand: variant **`-Q-W`** (QR scanner; Wiegand/RS-232/RS-485).
(See [[gee-qr-er80|datasheet summary]] / `raw/`.)
## What it is (and isn't)
- **Optical, not RFID-prox.** Earlier we *assumed* "ER80-EM" = a 125 kHz EM4100 card reader — the
datasheet corrects that: it's a **QR/barcode scanner**. The `-EM` in the original label was a
mis-id; the real model is **GEE-QR-ER80**. Optional `D`/`C` variants add ID/IC card, but the unit
on hand is **QR-only** (`-Q`).
- **Multi-interface** (Wiegand 26/34, RS-232, RS-485, USB, TCP/IP); the `-W` variant exposes
**Wiegand + RS-232/RS-485**.
## How it integrates — HTTP-GET push, server replies the verdict (confirmed via SDK)
The protocol is settled by the **[[qrcode-sdk|QRCode SDK v1.6.5]]** (not serial as first guessed).
The reader is configured (Windows tool `QRCode_v1_6_5.exe`) with a **server IP/port** and a "server
language" (only picks the URL path, e.g. `/qa/mcardsea.php`). **On each scan it HTTP-GETs the host:**
```
GET /qa/mcardsea.php?cardid=<QR>&mjihao=<devId>&cjihao=<devSN>&status=<2 chars>&time=<utc>
```
`cardid` = scanned data; `status` low digit = **direction (1=in / 0=out)**. The host replies JSON
`{data:[{cardid,cjihao,mjihao,status,time,output}],code:0}` where reply **`status` 1=valid (beep
2×) / 0=invalid (beep 1×)**, **`output` 0=Access/1=WG26/2=WG34**, `time` syncs the clock.
This is **host-in-the-loop and SYNCHRONOUS**: the GET *is* the access query and **our reply is the
decision** — it drives the reader's beep + output. So unlike a fire-and-forget reader, the endpoint
must decide (valid/invalid, direction from `status`) and reply, then also emit a `DeviceReadEvent`
on the `read` bus for the entry/exit/permit flows ([[parking-session]], [[permit]]) to open the
barrier. ([[device-input-flow]] is the analogous push pattern; this one also returns a verdict.)
> **This explains the "no beep":** feedback comes from the server's JSON reply, not locally. A
> non-JSON / missing reply ⇒ no beep even though the scan worked. So "no beep" ≠ "didn't scan."
- Pushes over plain **HTTP** to our `10.0.10.x` host (on the device subnet); no serial wiring, no
Wiegand-decode hardware. Suits the host-in-the-loop model; autonomy is moot anyway
([[dingtian-relay]] has no onboard ACL).
- **Linux-supported**, 4–15 VDC, default IP `192.168.1.99` — fits the [[disk-os-hardening|appliance]].
## Resolved (2026-06-16)
- Protocol = HTTP GET poll + JSON verdict (above). The earlier "serial/Wiegand, find the baud"
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.
## As-built (2026-06-16)
- **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).