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
This commit is contained in:
@@ -30,7 +30,7 @@ export interface DeviceReadEvent {
|
||||
/**
|
||||
* The decision a read produced. Returned by the read flows so a SYNCHRONOUS reader
|
||||
* (e.g. the QR reader, whose HTTP reply drives its beep + output) can answer the
|
||||
* device. A fire-and-forget reader simply ignores it. See wiki/entities/gee-qr-er80.md.
|
||||
* device. A fire-and-forget reader simply ignores it. See wiki/entities/dingtian-dt008-reader.md.
|
||||
*/
|
||||
export interface ReadOutcome {
|
||||
/** Was the vehicle admitted/exited (barrier opened)? Drives the reader's beep. */
|
||||
|
||||
@@ -4,10 +4,10 @@ import type { DeviceReadEvent } from "../device-events.js";
|
||||
import type { ReadDispatcher } from "../read-dispatch.js";
|
||||
import type { CredentialCapture } from "../credential-capture.js";
|
||||
|
||||
// GEE/Dingtian QR reader endpoint. The reader is configured (vendor tool) with our
|
||||
// host as its "server"; on each scan it sends an HTTP GET and BEEPS/acts based on
|
||||
// Dingtian DT-008 QR/RFID reader endpoint. The reader is configured (vendor tool) with
|
||||
// our host as its "server"; on each scan it sends an HTTP GET and BEEPS/acts based on
|
||||
// our JSON reply — host-in-the-loop and synchronous. Protocol from the QRCode SDK
|
||||
// v1.6.5; see wiki/sources/qrcode-sdk.md and wiki/entities/gee-qr-er80.md.
|
||||
// v1.6.5; see wiki/sources/qrcode-sdk.md and wiki/entities/dingtian-dt008-reader.md.
|
||||
//
|
||||
// reader → GET /qa/mcardsea.php?cardid=<QR>&mjihao=<devId>&cjihao=<devSN>&status=<2ch>&time=<utc>
|
||||
// server → {"data":[{cardid,cjihao,mjihao,status,time,output}],"code":0,"message":""}
|
||||
@@ -35,7 +35,7 @@ export async function qrReaderRoutes(
|
||||
): 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
|
||||
// enters when assigning the dingtian-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;
|
||||
@@ -52,7 +52,7 @@ export async function qrReaderRoutes(
|
||||
// drive output) once the socket CLOSES — every vendor demo replies
|
||||
// `Connection: close` and shuts the socket. Without it the reader waits out a
|
||||
// ~10 s keep-alive timeout before beeping. So force-close the connection.
|
||||
// See wiki/sources/qrcode-sdk.md, entities/gee-qr-er80.md.
|
||||
// See wiki/sources/qrcode-sdk.md, entities/dingtian-dt008-reader.md.
|
||||
reply.header("connection", "close");
|
||||
const cardid = (q.cardid ?? "").trim();
|
||||
const mjihao = q.mjihao != null ? Number(q.mjihao) : 0;
|
||||
@@ -75,7 +75,7 @@ export async function qrReaderRoutes(
|
||||
accepted = true; // beep "ok" so the operator knows the card was read
|
||||
} else {
|
||||
const read: DeviceReadEvent = {
|
||||
driverId: "gee-qr-reader",
|
||||
driverId: "dingtian-qr-reader",
|
||||
deviceId,
|
||||
value: cardid,
|
||||
kind: "qr",
|
||||
|
||||
@@ -234,10 +234,10 @@ export async function buildServer(opts: BuildOptions = {}): Promise<FastifyInsta
|
||||
// reader's live flow. Single-shot + TTL. See credential-capture.ts.
|
||||
const credentialCapture = new CredentialCapture();
|
||||
|
||||
// GEE/Dingtian QR reader: it HTTP-GETs on each scan and beeps/acts on our JSON
|
||||
// Dingtian DT-008 QR/RFID reader: it HTTP-GETs on each scan and beeps/acts on our JSON
|
||||
// verdict (host-in-the-loop, synchronous). The capture service can intercept a read
|
||||
// on an armed reader for enrollment; otherwise the read routes through the
|
||||
// dispatcher. See wiki/entities/gee-qr-er80.md, qrcode-sdk.md.
|
||||
// dispatcher. See wiki/entities/dingtian-dt008-reader.md, qrcode-sdk.md.
|
||||
await qrReaderRoutes(app, db, readDispatcher, credentialCapture);
|
||||
|
||||
// Shifts (manned mode): explicit open/close → signed shift_open / shift_z_report
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- The QR/RFID access reader was misnamed "GEE" on a wrong early assumption; it's actually a
|
||||
-- Dingtian DT-008 (same vendor as the relay board). The driver was renamed
|
||||
-- `gee-qr-reader` → `dingtian-qr-reader`. `devices.driver_id` is the persisted registry
|
||||
-- lookup key, so rewrite existing reader rows or they'd fail to resolve a driver. Idempotent.
|
||||
UPDATE `devices` SET `driver_id` = 'dingtian-qr-reader' WHERE `driver_id` = 'gee-qr-reader';
|
||||
@@ -106,6 +106,13 @@
|
||||
"when": 1781885700000,
|
||||
"tag": "0014_user_font_scale",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "6",
|
||||
"when": 1781885800000,
|
||||
"tag": "0015_dingtian_qr_reader_driverid",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execFile } from "node:child_process";
|
||||
|
||||
// Unprivileged ICMP liveness check for PUSH-only devices that expose no TCP port —
|
||||
// e.g. the Dingtian/GEE QR readers, which GET our backend on each scan but listen on
|
||||
// e.g. the Dingtian DT-008 QR readers, which GET our backend on each scan but listen on
|
||||
// nothing. For those a TCP connect probe (what cameras/printers use) has nothing to
|
||||
// connect to; ICMP echo is the only honest "powered + on-network" signal.
|
||||
//
|
||||
|
||||
@@ -7,7 +7,7 @@ import { stubAccessDriver } from "./access-stub.js";
|
||||
import { dahuaDriver, hikvisionDriver } from "./camera.js";
|
||||
import { cashinoDriver } from "./printer-cashino.js";
|
||||
import { rongtaDriver } from "./printer-rongta.js";
|
||||
import { geeQrReaderDriver, tcpipReaderDriver, wiegandReaderDriver } from "./reader.js";
|
||||
import { dingtianQrReaderDriver, tcpipReaderDriver, wiegandReaderDriver } from "./reader.js";
|
||||
|
||||
let registered = false;
|
||||
|
||||
@@ -19,7 +19,7 @@ export function registerBuiltinDrivers(): void {
|
||||
registry.register(stubAccessDriver);
|
||||
registry.register(wiegandReaderDriver);
|
||||
registry.register(tcpipReaderDriver);
|
||||
registry.register(geeQrReaderDriver);
|
||||
registry.register(dingtianQrReaderDriver);
|
||||
registry.register(hikvisionDriver);
|
||||
registry.register(dahuaDriver);
|
||||
registry.register(rongtaDriver);
|
||||
@@ -31,7 +31,7 @@ export {
|
||||
stubAccessDriver,
|
||||
wiegandReaderDriver,
|
||||
tcpipReaderDriver,
|
||||
geeQrReaderDriver,
|
||||
dingtianQrReaderDriver,
|
||||
hikvisionDriver,
|
||||
dahuaDriver,
|
||||
rongtaDriver,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
const icmpPing = vi.fn<(host: string, timeoutMs?: number) => Promise<boolean>>();
|
||||
vi.mock("./icmp.js", () => ({ icmpPing: (...a: [string, number?]) => icmpPing(...a) }));
|
||||
|
||||
const { geeQrReaderDriver } = await import("./reader.js");
|
||||
const { dingtianQrReaderDriver } = await import("./reader.js");
|
||||
|
||||
afterEach(() => {
|
||||
icmpPing.mockReset();
|
||||
@@ -17,26 +17,26 @@ afterEach(() => {
|
||||
describe("QR reader healthCheck (ICMP liveness)", () => {
|
||||
it("with an IP that replies → ready", async () => {
|
||||
icmpPing.mockResolvedValue(true);
|
||||
const r = geeQrReaderDriver.create({ serial: "H05M2AFA", host: "10.0.10.7" });
|
||||
const r = dingtianQrReaderDriver.create({ serial: "H05M2AFA", host: "10.0.10.7" });
|
||||
expect(await r.healthCheck()).toEqual({ status: "ready", detail: "ping 10.0.10.7" });
|
||||
expect(icmpPing).toHaveBeenCalledWith("10.0.10.7");
|
||||
});
|
||||
|
||||
it("with an IP that does NOT reply → offline (this is the bug fix)", async () => {
|
||||
icmpPing.mockResolvedValue(false);
|
||||
const r = geeQrReaderDriver.create({ serial: "H05M2AFA", host: "10.0.10.7" });
|
||||
const r = dingtianQrReaderDriver.create({ serial: "H05M2AFA", host: "10.0.10.7" });
|
||||
expect(await r.healthCheck()).toEqual({ status: "offline", detail: "no ping reply from 10.0.10.7" });
|
||||
});
|
||||
|
||||
it("with NO IP → degraded (never a false 'ready')", async () => {
|
||||
const r = geeQrReaderDriver.create({ serial: "H05M2AFA" });
|
||||
const r = dingtianQrReaderDriver.create({ serial: "H05M2AFA" });
|
||||
const h = await r.healthCheck();
|
||||
expect(h.status).toBe("degraded");
|
||||
expect(icmpPing).not.toHaveBeenCalled(); // nothing to ping
|
||||
});
|
||||
|
||||
it("exposes an optional host field for monitoring", () => {
|
||||
const hostField = geeQrReaderDriver.configFields.find((f) => f.key === "host");
|
||||
const hostField = dingtianQrReaderDriver.configFields.find((f) => f.key === "host");
|
||||
expect(hostField).toBeDefined();
|
||||
expect(hostField!.required).toBe(false); // operation is push-by-serial; IP is monitor-only
|
||||
});
|
||||
|
||||
@@ -75,18 +75,19 @@ export const tcpipReaderDriver: ReaderDriver = {
|
||||
create: (c) => new StubReader("tcpip-reader", c),
|
||||
};
|
||||
|
||||
// GEE/Fondvision QR access reader (e.g. GEE-QR-ER80). A PUSH device: on each scan
|
||||
// it HTTP-GETs our backend (/qa/mcardsea.<ext>) carrying its serial (cjihao); the
|
||||
// backend resolves the lane by matching that serial to this device's `serial`
|
||||
// config, decides, and replies the verdict (drives the beep). No host-side
|
||||
// connection — the adapter is a stub; the real integration is the HTTP endpoint
|
||||
// (apps/server routes/qr-reader.ts). See wiki/entities/gee-qr-er80.md.
|
||||
export const geeQrReaderDriver: ReaderDriver = {
|
||||
id: "gee-qr-reader",
|
||||
// Dingtian DT-008 QR/RFID access reader (same vendor as the relay board). Scans QR/barcode
|
||||
// + ID/IC/NFC cards. A PUSH device: on each scan it HTTP-GETs our backend (/qa/mcardsea.<ext>)
|
||||
// carrying its serial (cjihao); the backend resolves the lane by matching that serial to this
|
||||
// device's `serial` config, decides, and replies the verdict (drives the beep). No host-side
|
||||
// connection — the adapter is a stub; the real integration is the HTTP endpoint (apps/server
|
||||
// routes/qr-reader.ts). Interfaces: Wiegand 26/34, TCP/IP, USB, RS485. See
|
||||
// wiki/entities/dingtian-dt008-reader.md.
|
||||
export const dingtianQrReaderDriver: ReaderDriver = {
|
||||
id: "dingtian-qr-reader",
|
||||
category: "reader",
|
||||
label: "GEE/Fondvision QR reader (HTTP push)",
|
||||
label: "Dingtian DT-008 QR/RFID reader (HTTP push)",
|
||||
description:
|
||||
"QR/barcode access reader that HTTP-pushes each scan to the backend. Set its server IP/port to this host in the vendor tool; enter its serial here so scans resolve to this lane.",
|
||||
"Dingtian DT-008 QR/barcode/RFID access reader that HTTP-pushes each scan to the backend. Set its server IP/port to this host in the vendor tool; enter its serial here so scans resolve to this lane.",
|
||||
transports: ["tcp-ip"],
|
||||
configFields: [
|
||||
{
|
||||
@@ -105,5 +106,5 @@ export const geeQrReaderDriver: ReaderDriver = {
|
||||
help: "Optional: the reader's IP, used ONLY to monitor it (ping). Scans still resolve by serial. Leave blank to skip liveness monitoring.",
|
||||
},
|
||||
],
|
||||
create: (c) => new StubReader("gee-qr-reader", c),
|
||||
create: (c) => new StubReader("dingtian-qr-reader", c),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export {
|
||||
stubAccessDriver,
|
||||
wiegandReaderDriver,
|
||||
tcpipReaderDriver,
|
||||
geeQrReaderDriver,
|
||||
dingtianQrReaderDriver,
|
||||
hikvisionDriver,
|
||||
dahuaDriver,
|
||||
rongtaDriver,
|
||||
|
||||
@@ -33,7 +33,7 @@ talks only to the adapter interfaces ([[device-adapter-pattern]]), never a drive
|
||||
> an **unprivileged ICMP ping** (`drivers/icmp.ts`, shells `/bin/ping` in SOCK_DGRAM mode — no
|
||||
> CAP_NET_RAW, no native dep; the booth compose sets `net.ipv4.ping_group_range`). Reply → `ready`,
|
||||
> no reply → `offline`; **no IP set → `degraded` ("set IP to monitor")**, never a false green.
|
||||
> Verified on hardware: pinged the real readers on the device VLAN. See [[gee-qr-er80]].
|
||||
> Verified on hardware: pinged the real readers on the device VLAN. See [[dingtian-dt008-reader]].
|
||||
|
||||
Both collapse to one **traffic-light**: `ready | degraded | offline`, plus a `detail` string. Fail
|
||||
**toward "there's a problem"**, never false-healthy: a probe that throws or times out reads
|
||||
|
||||
@@ -22,9 +22,9 @@ There are **two populations** of users, and they map to **two integration paths*
|
||||
| [[wiegand]] reader → UHPPOTE port | The controller | Controller (onboard card list) | **Yes** — works if host down |
|
||||
| Pure TCP/IP reader | Host only | Host, then UDP `open` to relay | No — host on critical path |
|
||||
| [[lpr-camera|LPR]] / QR scanner | Host only | Host | No |
|
||||
| **[[gee-qr-er80]] QR reader (serial)** | Host only | Host (reads serial → `read` bus) | No |
|
||||
| **[[dingtian-dt008-reader|Dingtian DT-008]] QR reader** | Host only | Host (reads serial → `read` bus) | No |
|
||||
|
||||
> Concrete host-side reader on hand: the **[[gee-qr-er80]]** (QR over RS-232/RS-485). Note autonomy
|
||||
> Concrete host-side reader on hand: the **[[dingtian-dt008-reader|Dingtian DT-008]]** (QR/RFID over HTTP push). Note autonomy
|
||||
> is moot here anyway — the current relay ([[dingtian-relay]]) has **no onboard card list**, so even
|
||||
> a Wiegand reader would be host-decided. So we take the serial/QR path straight to the host's
|
||||
> `read` bus.
|
||||
|
||||
@@ -80,9 +80,9 @@ must have:
|
||||
- The **scanner is a device behind an adapter** ([[device-adapter-pattern]]): a new `ReaderDevice`
|
||||
kind (QR/barcode imager) — likely the same `IdentitySource = "ticket"` / `"qr"` path. Keeps the
|
||||
app device-agnostic; hardware model is procurement ([[bom]], [[open-questions]]).
|
||||
- **On hand:** the **[[gee-qr-er80]]** QR access reader (`-Q-W`: QR scanner, Wiegand/RS-232/RS-485,
|
||||
- **On hand:** the **[[dingtian-dt008-reader|Dingtian DT-008]]** QR/RFID reader (Wiegand/TCP-IP/USB/RS485,
|
||||
Linux-supported) — the concrete scanner for this path. A serial `ReaderDevice` adapter feeds the
|
||||
`read` bus; pending the reader's RS-232 frame/baud (see [[gee-qr-er80]] open questions).
|
||||
`read` bus; via HTTP-GET push (see [[dingtian-dt008-reader]]).
|
||||
|
||||
## Ticketless alternative (plate as the ticket)
|
||||
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
---
|
||||
type: entity
|
||||
tags: [parking, hardware, readers, qr]
|
||||
sources: [gee-qr-er80]
|
||||
updated: 2026-06-16
|
||||
tags: [parking, hardware, readers, qr, dingtian]
|
||||
sources: [dingtian-dt008]
|
||||
updated: 2026-06-28
|
||||
status: open
|
||||
---
|
||||
|
||||
# GEE-QR-ER80 (QR access reader)
|
||||
# Dingtian DT-008 (QR/RFID 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
|
||||
[[subscription]]** credentials. On hand: variant **`-Q-W`** (QR scanner; Wiegand/RS-232/RS-485).
|
||||
(See [[gee-qr-er80|datasheet summary]] / `raw/`.)
|
||||
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/`.)
|
||||
|
||||
## What it is (and isn't)
|
||||
> **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.
|
||||
|
||||
- **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**.
|
||||
## 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)
|
||||
|
||||
@@ -48,7 +55,7 @@ barrier. ([[device-input-flow]] is the analogous push pattern; this one also ret
|
||||
- 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]].
|
||||
- Default IP `192.168.1.99` (set its server IP/port to this host in the vendor tool).
|
||||
|
||||
## Resolved (2026-06-16)
|
||||
|
||||
@@ -93,18 +100,18 @@ from 10.0.10.7 (referer: http://www.fondvision.com — the OEM is Fondvision)
|
||||
|
||||
## Assignment (as-built 2026-06-16)
|
||||
|
||||
A dedicated **`gee-qr-reader`** driver ([[device-registry]], reader category) models the push reader:
|
||||
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 `gee-qr-reader` {serial:"H05M2AFA"} bound to an access relay →
|
||||
- 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
|
||||
`gee-qr-reader`.
|
||||
`dingtian-qr-reader`.
|
||||
|
||||
## Open / next
|
||||
|
||||
@@ -237,7 +237,7 @@ LPR/ANPR plate identity** (the plate binding below):
|
||||
(a chosen value could be guessable or collide). The UI sends a blank QR credential; the server
|
||||
mints the value and returns it (so the UI can print it). **An RF credential, by contrast, carries
|
||||
the physical card id, so it is operator-entered.**
|
||||
- **Reader output = TCP/IP full string** (decided 2026-06-18, the [[gee-qr-er80|host-in-the-loop
|
||||
- **Reader output = TCP/IP full string** (decided 2026-06-18, the [[dingtian-dt008-reader|host-in-the-loop
|
||||
QR reader]] path): the reader delivers the whole decoded string, so the code length is free
|
||||
(unguessable token). *If a site ever wires the reader as **Wiegand 26/34** instead, a scanned
|
||||
QR truncates to a 24-/32-bit number — the generated code would then have to be a numeric id in
|
||||
@@ -255,7 +255,7 @@ LPR/ANPR plate identity** (the plate binding below):
|
||||
- **RF tag / chip / card — selectable later, NOT live yet.** An RFID/proximity credential, read
|
||||
**host-side** (reader → host → `pulseOpen`). **LIVE since 2026-06-18** — the operator selects RFID
|
||||
and **reads the card off a physical reader** (see "Enrolling a card" below) rather than typing the
|
||||
number. The GEE readers are combo QR + RFID (ID/IC/NFC), so the same device captures both. A
|
||||
number. The Dingtian DT-008 readers are combo QR + RFID (ID/IC/NFC), so the same device captures both. A
|
||||
Wiegand-out reader keeps a future autonomous path open ([[entry-exit-readers]]); the
|
||||
[[dingtian-relay]] has no onboard card list.
|
||||
- **Plate (LPR/ANPR) — matching is BUILT, the live SOURCE is the one missing wire.** When plate-bound
|
||||
@@ -296,7 +296,7 @@ traffic, so capture must **borrow one reader briefly without blocking the other*
|
||||
A subscription is a one-to-many aggregate: it may hold **several credentials at once** — e.g. a QR
|
||||
**and** an RFID card (and later NFC). Each is its own `subscription_credentials` row; any of them
|
||||
resolves the same subscription at the barrier. (NFC works today as an `rf` credential on the combo
|
||||
GEE reader; a distinct `nfc` `kind` is a small future labelling-only addition.)
|
||||
Dingtian DT-008 reader; a distinct `nfc` `kind` is a small future labelling-only addition.)
|
||||
|
||||
Crucially, **entry and exit are NOT bound to the same credential.** Originally the session was keyed
|
||||
by the exact credential value read, so you had to leave with whatever you arrived with — an
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ Counts: 4 sources · 19 entities · 45 concepts · 7 decision records.
|
||||
|
||||
## Sources
|
||||
- [[parking-system-architecture]] — design notes: stack, threat model, devices, UHPPOTE, ESP32, readers, BOM, open decisions.
|
||||
- [[gee-qr-er80]] — datasheet: GEE QR access reader (QR/DM/1D; Wiegand/RS-232/485/USB/TCP; Linux).
|
||||
- [[dingtian-dt008]] — Dingtian DT-008 product page: QR/RFID access reader (QR/barcode + ID/IC/NFC; Wiegand/TCP-IP/USB/RS485; HTTP-GET push).
|
||||
- [[qrcode-sdk]] — QRCode SDK v1.6.5: the reader's HTTP-GET-poll protocol + JSON verdict (beep/output).
|
||||
- [[parksql2017-legacy-schema]] — predecessor SQL Server schema (Albanian market): legacy tariff/discount/membership/shift/fiscal model; confirms blocks, adds time-windows + categories, lacks postpaid sponsors.
|
||||
|
||||
@@ -40,7 +40,7 @@ Counts: 4 sources · 19 entities · 45 concepts · 7 decision records.
|
||||
- [[atecc608]] — secure element; non-extractable signing key (host events + controller auth).
|
||||
- [[wiegand]] — reader standard feeding the controller directly (autonomous permit-holder path).
|
||||
- [[lpr-camera]] — edge-AI plate recognition; host-side casual-identity source.
|
||||
- [[gee-qr-er80]] — QR access reader on hand; host-side serial → `read` bus (the QR-ticket scanner).
|
||||
- [[dingtian-dt008-reader]] — Dingtian DT-008 QR/RFID reader on hand; host-side serial → `read` bus (the QR-ticket scanner).
|
||||
- [[zkteco-controller]] — ❌ rejected/historical; aux-input path was a contender, not pursued.
|
||||
- [[dingtian-relay]] — ✅ CHOSEN access controller; decoupled inputs solve the button blocker (driver verified on hardware); spare relays drive aux outputs (`setAux`).
|
||||
- [[hikvision-radar]] — vehicle-presence radar on a Dingtian input; the entry presence gate (per-input active-level caveat).
|
||||
|
||||
+17
@@ -1884,3 +1884,20 @@ snapshots from it before I caught it. The signed ledger was untouched (snapshots
|
||||
advisory; `PRAGMA integrity_check: ok`, ledger_events/sessions/subscriptions intact) and it was dev
|
||||
not prod — but it violated the never-touch-the-live-DB rule. Lesson: pass the scratch path
|
||||
explicitly + guard-refuse any non-scratch path BEFORE any destructive op (the corrected harness does).
|
||||
|
||||
## [2026-06-28] fix | The QR reader is a Dingtian DT-008, NOT "GEE" (correct an early mis-id)
|
||||
A wrong early assumption named the QR/RFID access reader "GEE" / "GEE/Fondvision" / "GEE-QR-ER80"
|
||||
(and summarized a `raw/GEE-QR-ER80….pdf` as its datasheet). There is no GEE device — it's the
|
||||
**Dingtian DT-008** (https://www.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. Corrected across the
|
||||
codebase + wiki: driver symbol `geeQrReaderDriver`→`dingtianQrReaderDriver`, **persisted driverId
|
||||
`gee-qr-reader`→`dingtian-qr-reader`** (migration **0015** rewrites the existing `devices.driver_id`
|
||||
rows so readers keep resolving — applied to the dev DB, 2 rows; the booth applies it on boot), label
|
||||
"Dingtian DT-008 QR/RFID reader (HTTP push)", and all server/test/comment refs. Wiki: renamed
|
||||
`entities/gee-qr-er80.md`→`entities/dingtian-dt008-reader.md` and `sources/gee-qr-er80.md`→
|
||||
`sources/dingtian-dt008.md`, rewrote both to the REAL DT-008 specs (Wiegand26/34 + TCP/IP + USB +
|
||||
RS485 — NOT RS-232; QR/barcode + ID/IC/NFC — NOT DataMatrix/1D; 9–16 V/800 mA) while keeping all the
|
||||
verified-on-hardware protocol facts (cjihao serial, `.jsp` path, `Connection: close`). Memory
|
||||
`gee-reader-serial-binding`→`dingtian-reader-serial-binding`. The only surviving "GEE" mentions are
|
||||
deliberate naming-correction notes + the raw PDF filename. Behaviour unchanged — naming + the
|
||||
persisted id only. build/lint/test green.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
type: source
|
||||
tags: [parking, hardware, readers, qr, dingtian, datasheet]
|
||||
sources: [dingtian-dt008]
|
||||
updated: 2026-06-28
|
||||
---
|
||||
|
||||
# Source: Dingtian DT-008 QR Code Reader (product page)
|
||||
|
||||
Vendor product page for the **Dingtian DT-008** QR/barcode/RFID access reader —
|
||||
https://www.dingtian-tech.com/en_us/qr_code_reader.html. This is the reader the project
|
||||
actually has on hand for the [[ticket-encoding|QR ticket]] / RFID-[[subscription]] paths.
|
||||
Entity: [[dingtian-dt008-reader]]. Same vendor as the [[dingtian-relay|Dingtian relay board]].
|
||||
|
||||
> **Supersedes a mis-identified source.** The project originally summarized
|
||||
> `raw/GEE-QR-ER80 QR Code Access Control Reader.pdf` as if the reader were a "GEE-QR-ER80"
|
||||
> (GEE NFC LIMITED). That was a wrong early assumption — the device is the Dingtian DT-008.
|
||||
> Several facts in that PDF (RS-232, DataMatrix/1D, a Linux SDK) do **not** match the real
|
||||
> hardware and were dropped. The HTTP-push protocol facts, however, were later **verified on the
|
||||
> actual device** (see [[dingtian-dt008-reader]] "Verified on hardware").
|
||||
|
||||
## Key takeaways (DT-008 product page)
|
||||
|
||||
- **Scans:** QR / barcode (simple) **and** ID/IC/NFC cards. Read ~**0.2 s**, range **0–10 cm**.
|
||||
- **Interfaces:** **Wiegand 26/34, TCP/IP, USB, RS485.** (For our integration: HTTP over TCP/IP.)
|
||||
- **Power:** 9–16 V DC, 800 mA. **Size:** 86×86×42 mm. **Temp:** −30…70 °C.
|
||||
- **Wiegand pinout:** Red(power), Black(GND), Green(D0), White(D1), Blue(LED), Yellow(BEEP).
|
||||
- **Reporting:** "QR Code HTTP GET" — the reader HTTP-GETs a configured host on each scan; the host
|
||||
replies the verdict (drives beep/output). Vendor ships a **QR Code config tool** + HTTP-GET demo
|
||||
code (C++/C#/PHP) and an SDK (`QRCode_sdk.zip`). See [[qrcode-sdk]] for the protocol we implement.
|
||||
|
||||
## How we use it
|
||||
|
||||
Host-in-the-loop, synchronous HTTP: reader GETs `/qa/mcardsea.<ext>` with its serial (`cjihao`);
|
||||
our endpoint (`apps/server/src/routes/qr-reader.ts`) decides and replies the SDK verdict, and emits
|
||||
a `read` event for the access flows. Full integration + the on-hardware findings (the `.jsp` path
|
||||
extension, `Connection: close` requirement, serial resolution) are in [[dingtian-dt008-reader]].
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
type: source
|
||||
tags: [parking, hardware, readers, qr, datasheet]
|
||||
sources: [gee-qr-er80]
|
||||
updated: 2026-06-16
|
||||
---
|
||||
|
||||
# Source: GEE-QR-ER80 QR Code Access Control Reader (datasheet)
|
||||
|
||||
Vendor datasheet (GEE NFC LIMITED, ©2007–2019) for the **GEE-QR-ER80** — a static
|
||||
**QR-code access-control reader**, optional ID/IC card. The reader the project has
|
||||
on hand for the [[ticket-encoding|QR ticket]] path. Raw:
|
||||
`raw/GEE-QR-ER80 QR Code Access Control Reader.pdf` (3 pages). Entity: [[gee-qr-er80]].
|
||||
|
||||
## Key takeaways
|
||||
|
||||
- **Optical scanner**, not a prox-card reader: reads **QR, DataMatrix, 1D barcode** (static).
|
||||
Optional add-ons for **IC card UID / ID card**.
|
||||
- **Multi-interface:** **Wiegand 26/34, RS-232, RS-485, USB, TCP/IP** — selectable by variant.
|
||||
- **Power:** 4–15 VDC, < 800 mA. **Read direction:** 360°. Built-in scanner LED.
|
||||
- **OS:** Windows XP/7/8/10 **and Linux** (explicit) — fits the [[disk-os-hardening|Linux appliance]].
|
||||
- **Wiring (Wiegand/RS-232/485 variant):** VCC(+12V), GND, **D0/D1** (Wiegand), **TX/R+ , RX/R-**
|
||||
(RS-232 / RS-485), plus **LED** and **BEEP** control lines (host can drive feedback).
|
||||
- **Order code** `GEE-QR-ER80-<scanner>-<interface>`: `Q`=QR scanner / `D`=ID reader / `C`=IC reader;
|
||||
`W`=WG·RS232·RS485 / `U`=USB / `T`=RJ45 (TCP/IP). **On hand: `-Q-W`** (QR scanner; Wiegand/RS-232/RS-485).
|
||||
|
||||
## Section map
|
||||
|
||||
- p1 — overview, physical + feature table (interfaces, power, read direction).
|
||||
- p2 — supported types (QR/DM/1D + optional IC/ID), OS, environment; **wire definition** (pin table).
|
||||
- p3 — order-code breakdown, applications (access control / vacation rentals / time attendance).
|
||||
|
||||
## Not in this datasheet (open)
|
||||
|
||||
- The **RS-232/RS-485 data protocol**: baud rate, frame format, and whether a QR scan is emitted as
|
||||
an **ASCII string** (expected) vs. some framed protocol. Decides the host-side adapter — see
|
||||
[[gee-qr-er80]] open questions. Resolve by vendor docs or by observing the port on a scan.
|
||||
@@ -5,13 +5,13 @@ sources: [qrcode-sdk]
|
||||
updated: 2026-06-16
|
||||
---
|
||||
|
||||
# Source: QRCode SDK v1.6.5 (GEE/Dingtian QR reader)
|
||||
# Source: QRCode SDK v1.6.5 (Dingtian DT-008 QR reader)
|
||||
|
||||
Vendor SDK for the QR access reader ([[gee-qr-er80]]; also branded Dingtian). Defines the
|
||||
Vendor SDK for the QR access reader (the Dingtian [[dingtian-dt008-reader]]). Defines the
|
||||
reader↔server **HTTP protocol** — the missing piece the datasheet omitted. Files at
|
||||
`QRCode_sdk - QRCode_v1_6_5/sdk/` (config tool `QRCode_v1_6_5.exe`, demos in C#/PHP/VC++, protocol
|
||||
docs `readme.txt`, `qrcode_HTTP_GET.txt`, `VC++/how to.txt`). **Not copied into `raw/`** — bulky +
|
||||
binaries; this summary is the faithful capture. Entity: [[gee-qr-er80]].
|
||||
binaries; this summary is the faithful capture. Entity: [[dingtian-dt008-reader]].
|
||||
|
||||
## The protocol — HTTP GET poll, server replies the verdict
|
||||
|
||||
@@ -52,7 +52,7 @@ GET /qa/mcardsea.php?cardid=445D2C&mjihao=1&cjihao=HW256097&status=11&time=15404
|
||||
|
||||
This is **host-in-the-loop, synchronous**: the GET *is* the access query; our JSON reply *is* the
|
||||
decision. So the backend endpoint must **decide (valid/invalid + direction) and reply** — richer
|
||||
than a fire-and-forget read. Direction comes from the `status` low digit. See [[gee-qr-er80]] +
|
||||
than a fire-and-forget read. Direction comes from the `status` low digit. See [[dingtian-dt008-reader]] +
|
||||
[[device-input-flow]].
|
||||
|
||||
## Defaults / misc
|
||||
|
||||
Reference in New Issue
Block a user