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

114 lines
6.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`.
## Verified on hardware (2026-06-16)
Captured a real scan (vendor-emulator logger on :3000). The reader **does scan, send, and beep** —
the earlier "no beep" was simply that no server was answering on :3000 with valid JSON. Real GET:
```
GET /qa/mcardsea.jsp?cardid=52020056&mjihao=1&cjihao=H05M2AFA&status=11&time=1781634494
from 10.0.10.7 (referer: http://www.fondvision.com — the OEM is Fondvision)
```
- **PATH carries the configured "server language" EXTENSION:** this unit is set to **JSP**, so it
GETs **`/qa/mcardsea.jsp`** — NOT `.php`. Our endpoint was registered at `.php` only → it would
have 404'd the real reader. **Fixed:** the route now registers `php/jsp/asp/aspx/cgi`.
- **`cjihao` = `H05M2AFA`** is the device **serial** — the value our endpoint keys the lane on. So
assign the reader with **`lane_devices.id = "H05M2AFA"`** (+ an access device on the same lane).
- **`mjihao` = 1** (device id). `cardid` = the scanned barcode (`52020056`). `status=11`.
- The reader **beeped on the vendor reply with `status:0`** — so it acts on the reply; `0` =
invalid/1-beep as documented. A matching permit/session will return `status:1` → 2-beep accept.
## Assignment (as-built 2026-06-16)
A dedicated **`gee-qr-reader`** driver ([[device-registry]], reader category) models the push reader:
its one config field is **`serial`** (the `cjihao` the device reports). The admin assigns it in the
[[first-run-setup|setup wizard]] like any device (normal UUID row id) and enters the serial. The QR
endpoint resolves the lane by **matching `config.serial` to the scan's `cjihao`** — not by row id —
so no DB hand-editing. Set the reader's server IP/port to this host in the **vendor tool**; assign +
enter its serial here.
- Verified via inject: assign `gee-qr-reader` {serial:"H05M2AFA"} on a lane w/ an access device →
a `.jsp` scan with that serial + a matching permit QR → `status:1` (2-beep accept) + open; re-scan
→ permit exit; unknown card → `status:0`; unassigned serial → `status:0` (no lane, graceful).
- Note `tcpip-reader` is the WRONG model for this device (host-connects-out, a stub) — use
`gee-qr-reader`.
## Open / next
- Re-test on hardware against the real app (now `.jsp`-aware + serial-resolved): scan → expect a
`status:1` 2-beep when the QR matches a permit/open session.
- `output` is replied as `0` (Access). Confirm on hardware whether the reader needs `1`/`2` (WG26/34)
to drive its access line, vs. `0`.