Files
parking_solution/wiki/entities/dingtian-dt008-reader.md
T
julian f6e35bbebf
Build desktop / desktop (push) Successful in 4m16s
Build & push images / images (push) Successful in 2m46s
CI / check (push) Successful in 38s
fix(reader): correct the QR reader's identity — Dingtian DT-008, not "GEE"
An early wrong assumption named the QR/RFID access reader "GEE" /
"GEE/Fondvision" / "GEE-QR-ER80" (and summarized a raw GEE PDF as its
datasheet). There is no GEE device — it's the Dingtian DT-008
(dingtian-tech.com/en_us/qr_code_reader.html), the same vendor as the relay
board, which is why it integrates the identical HTTP-GET-push way.

Code:
- Driver symbol geeQrReaderDriver → dingtianQrReaderDriver; label →
  "Dingtian DT-008 QR/RFID reader (HTTP push)"; comments/description rewritten
  to the real DT-008 facts (Wiegand 26/34, TCP/IP, USB, RS485 — not RS-232;
  QR/barcode + ID/IC/NFC — not DataMatrix/1D).
- Persisted driverId "gee-qr-reader" → "dingtian-qr-reader" (the registry
  lookup key + the row created on assign in qr-reader.ts).
- Migration 0015 rewrites existing devices.driver_id rows so configured readers
  keep resolving (applied to the dev DB — 2 rows; the booth applies it on boot).
  Behaviour is unchanged: naming + the persisted id only.

Wiki + memory:
- Renamed entities/gee-qr-er80.md → dingtian-dt008-reader.md and
  sources/gee-qr-er80.md → dingtian-dt008.md; rewrote both to the real DT-008
  product-page specs while KEEPING all the verified-on-hardware protocol facts
  (cjihao serial, .jsp path, Connection: close). Fixed every cross-reference +
  "GEE" mention in 6 other pages. Memory gee-reader-serial-binding →
  dingtian-reader-serial-binding. The only surviving "GEE" mentions are
  deliberate naming-correction notes, the raw PDF filename, and the
  append-only log history.

Full workspace build/lint/test green; dev DB readers verified resolving to the
registered dingtian-qr-reader driver.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
2026-06-28 17:39:15 +02:00

131 lines
8.0 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, dingtian]
sources: [dingtian-dt008]
updated: 2026-06-28
status: open
---
# Dingtian DT-008 (QR/RFID access reader)
The project's **QR / barcode / RFID reader** — Dingtian **DT-008**, the **same vendor as the
[[dingtian-relay|relay board]]** (which is why it integrates the same HTTP-push way). A static
optical + card scanner for **QR / barcode (simple)** and **ID/IC/NFC cards**. This is the
**[[ticket-encoding|QR ticket]]** scanner (read at the pay station / exit lane) and a path for **QR
or RFID [[subscription]]** credentials. Product page: dingtian-tech.com/en_us/qr_code_reader.html.
(See [[dingtian-dt008|spec summary]] / `raw/`.)
> **Naming correction (2026-06-28).** Through most of this project this reader was wrongly called
> **"GEE" / "GEE/Fondvision" / "GEE-QR-ER80"** — a bad early assumption. There is no GEE device; it
> is the **Dingtian DT-008**. The driver id was renamed `gee-qr-reader` → `dingtian-qr-reader`
> (migration 0015 rewrites existing rows). All *protocol/behaviour* facts below were observed on the
> real hardware and remain correct — only the device identity was wrong.
## What it is
- **QR/barcode + card.** Reads **QR / simple barcode** AND **ID/IC/NFC cards** (~0.2 s, 0–10 cm).
(An earlier note guessed "ER80-EM = 125 kHz EM4100 prox" — that was part of the same mis-id.)
- **Interfaces: Wiegand 26/34, TCP/IP, USB, RS485.** (NOT RS-232 — that was a fictional-datasheet
claim.) For our integration it talks **HTTP over TCP/IP**; Wiegand is the reader's *output line*
on a valid read, not the host transport.
- **Power** 9–16 V DC, 800 mA; **86×86×42 mm**; −30…70 °C. Fits the [[disk-os-hardening|appliance]].
## 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]], [[subscription]]) 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).
- Default IP `192.168.1.99` (set its server IP/port to this host in the vendor tool).
## 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.
- **Reader identity:** the endpoint matches the device **serial (`cjihao`)** against each reader's
`config.serial` to find its `devices` row; the dispatcher then resolves the relay that row is
**bound** to (`config.controllerId` + `relay`) and opens it. See [[entry-exit-points]].
- 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 matches against the
reader's `config.serial`. So assign the reader with **`config.serial = "H05M2AFA"`** and bind it
to a controller relay.
- **`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 **`dingtian-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), enters the serial, and binds
it to a controller relay. The QR endpoint resolves the reader 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 + bind it here.
- Verified via inject: assign `dingtian-qr-reader` {serial:"H05M2AFA"} bound to an access relay →
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 relay, graceful).
- Note `tcpip-reader` is the WRONG model for this device (host-connects-out, a stub) — use
`dingtian-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`.
## ⚠️ Reply MUST set `Connection: close` (verified on hardware)
The reader sends `Connection: keep-alive` but **only acts on the verdict (beep/output) once the TCP
socket CLOSES**. Fastify's default keeps the connection alive → the reader waits out a **~10 s
keep-alive timeout before beeping**, even though the server replied in ~15 ms. Every vendor demo
replies **`Connection: close`** and shuts the socket. Fix: the endpoint sets
`reply.header("connection","close")`. Symptom if regressed: correct accept/reject but a ~10 s lag
before the beep. (The request arrives fast; the delay is entirely the reader waiting for close.)