Real Hikvision/Dahua camera driver; gate Backend-push-IP on capability
Replace the camera stub with HttpCamera: Hikvision ISAPI and Dahua CGI snapshots over client-side HTTP Digest (new drivers/http-digest.ts). healthCheck() now pulls a real frame instead of returning ready/stub. Snapshot carries bytes (driver fetches); storage/imageRef is the caller's job, keeping the adapter free of storage deps. Fix the cosmetic Backend-push-IP field: add pushesToBackend to DeviceDriver (only Dingtian sets it), expose as pushCapable in the catalog, and gate the wizard's backend-IP fetch + field on it so pull-only devices hide it. Verified on hardware (Hikvision 10.0.10.121): healthCheck ready, captureSnapshot returns a valid JPEG.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
type: entity
|
||||
tags: [parking, hardware, readers, offline-first]
|
||||
sources: [parking-system-architecture]
|
||||
updated: 2026-06-14
|
||||
updated: 2026-06-15
|
||||
---
|
||||
|
||||
# LPR Camera
|
||||
@@ -20,3 +20,36 @@ License-plate-recognition camera (recommended: **Milesight edge-AI LPR**). For
|
||||
host's signed [[append-only-event-chain]] entry + the controller's remote-open event) that
|
||||
should reconcile one-to-one; any mismatch is an anomaly.
|
||||
- Mounting: within ~15° of vehicle travel at a controlled chokepoint for best reads.
|
||||
|
||||
## Snapshot driver (entry/exit fraud-control record)
|
||||
|
||||
Separate from edge-AI LPR: the camera driver (`packages/devices/src/drivers/camera.ts`) does
|
||||
**snapshot-on-event** — the host pulls a still over HTTP when an entry/exit fires and stores it,
|
||||
referenced from the signed [[append-only-event-chain]] entry as an independent record. The camera
|
||||
**pulls, it does not push** — so it is NOT `pushesToBackend` and the setup wizard correctly hides
|
||||
the "Backend push IP" field for it (gated on the driver's `pushesToBackend` flag; only
|
||||
[[dingtian-relay]] sets it).
|
||||
|
||||
- **Hikvision** uses **ISAPI**: `GET /ISAPI/Streaming/channels/<id>/picture` (`101` = ch1 main
|
||||
stream) with **HTTP Digest** auth. The "Enable Hikvision-CGI" toggle (Network → Advanced →
|
||||
Integration Protocol) is a *different* legacy CGI surface — **not** needed for ISAPI.
|
||||
- **Dahua** uses CGI: `GET /cgi-bin/snapshot.cgi?channel=<n>` (0-based channel; the wizard's
|
||||
1-based channel is decremented).
|
||||
|
||||
**Driver / storage boundary:** the driver FETCHES the image bytes (client-side HTTP Digest in
|
||||
`drivers/http-digest.ts`) and returns them on `Snapshot.bytes`; **storage is the caller's job**
|
||||
(the future entry/exit flow stores the bytes + mints a durable `imageRef`). This keeps the device
|
||||
adapter free of any filesystem/blob-store dependency. `healthCheck()` is honest — it actually pulls
|
||||
a frame (exercising reachability + auth + path/channel in one shot), not a fake `ready/stub`.
|
||||
|
||||
### Verified on hardware (2026-06-15)
|
||||
|
||||
A **Hikvision** unit ("Camera 20", MAC `94:e1:ac:…`, Hikvision OUI) at `10.0.10.121`, creds
|
||||
`admin` / `admin123` (Digest), TCP 80:
|
||||
|
||||
- Initial `curl` test confirmed the ISAPI path returns a 2688×1520 JPEG (~306 KB).
|
||||
- The **real driver** (no longer a stub) was then run end to end against it:
|
||||
`healthCheck()` → `ready` (pulled a frame), `captureSnapshot()` → valid `image/jpeg`, ~322 KB,
|
||||
correct JPEG magic. Digest handshake works through `HttpCamera`.
|
||||
- Reaching it from the WSL dev box required forcing the source address (`config.localAddress`,
|
||||
threaded into the driver) — see [[wsl-dev-networking]] (multi-subnet source-selection trap).
|
||||
|
||||
+41
@@ -297,3 +297,44 @@ guarantee. Recorded in [[dingtian-relay]] (new Hardening section).
|
||||
- Documented that `source` stays null for raw inputs by design (it's an IdentitySource, not a
|
||||
device field); device provenance is in `identity`.
|
||||
- Updated [[append-only-event-chain]].
|
||||
|
||||
## [2026-06-15] test+lesson | Hikvision camera verified; multi-subnet source-address trap
|
||||
- Pulled a real snapshot from a Hikvision camera on the bench: `GET
|
||||
http://10.0.10.121/ISAPI/Streaming/channels/101/picture`, Digest auth, admin/admin123 → HTTP 200,
|
||||
2688×1520 JPEG. Path + auth + creds confirmed. ISAPI is the right surface; the device's
|
||||
"Enable Hikvision-CGI" toggle is a *different* legacy CGI API and is NOT needed.
|
||||
- Caveat recorded: the camera driver is still a STUB — the wizard's "● ready — stub / ●
|
||||
preconditions OK" contacts nothing; cameras have no preconditions (only [[dingtian-relay]]
|
||||
implements checkPreconditions). Noted the cosmetic "Backend push IP" bug (camera pulls, doesn't
|
||||
push; field should gate on a `pushesToBackend` capability).
|
||||
- LESSON (cost an hour of "why can't we ping the subnet"): with two device subnets stacked on one
|
||||
NIC (`192.168.1.123` + `10.0.10.203` on eth1), Linux picked the WRONG source address for
|
||||
`10.0.10.x` → ARP shows REACHABLE but all ping/TCP times out. Fix: pin `src` on the connected
|
||||
route (`ip route change <subnet>/24 dev <nic> proto kernel scope link src <host-ip>`), or force
|
||||
source per-call (`ping -I` / `curl --interface`). Devices arrive on assorted static `/24`s; the
|
||||
host carries one IP per subnet — this trap is the recurring cost of that.
|
||||
- Decision context: production is a dedicated hardened **Linux appliance** (this WSL2 box is a dev
|
||||
stand-in). Multi-subnet config + `src` pinning is an appliance deployment concern (made
|
||||
persistent via networkd/netplan), riding on [[network-isolation]]; long-term answer is to re-IP
|
||||
devices onto one planned parking subnet at install.
|
||||
- Updated [[lpr-camera]] (snapshot driver + verified-on-hardware section), [[wsl-dev-networking]]
|
||||
(multi-subnet source-address trap + appliance pattern).
|
||||
|
||||
## [2026-06-15] driver+fix | Real Hikvision/Dahua camera driver; push-IP field gated
|
||||
- Replaced the camera STUB with a real `HttpCamera` (`packages/devices/src/drivers/camera.ts`):
|
||||
Hikvision ISAPI (`/ISAPI/Streaming/channels/<ch>01/picture`) + Dahua CGI (0-based channel), both
|
||||
over client-side HTTP Digest (new `drivers/http-digest.ts`, two-shot 401→challenge→response,
|
||||
qop=auth MD5 — the client counterpart to the server's digest-auth.ts). `healthCheck()` now
|
||||
actually pulls a frame instead of returning `ready/stub`. Added `localAddress` + `timeoutMs` +
|
||||
`channel` config; threads the device-facing NIC for the multi-subnet trap.
|
||||
- Snapshot interface: `Snapshot` now carries `bytes: Buffer` (driver fetches); `imageRef` is
|
||||
optional and set by the CALLER once stored — keeps the adapter free of storage deps. Nothing
|
||||
consumed captureSnapshot yet, so no migration needed.
|
||||
- Cosmetic bug fixed: "Backend push IP" showed for any reachable host. Added a `pushesToBackend`
|
||||
flag to `DeviceDriver` (only [[dingtian-relay]] sets it), exposed as `pushCapable` in the catalog
|
||||
(mirrors `discoverable`), and gated both the wizard's backend-IP fetch and the field on it.
|
||||
Cameras/printers/readers no longer show it.
|
||||
- VERIFIED on hardware: built clean (5/5 packages); ran the real driver against the Hikvision at
|
||||
10.0.10.121 → healthCheck ready, captureSnapshot returned a valid 322 KB JPEG (correct magic).
|
||||
- Updated [[lpr-camera]].
|
||||
|
||||
|
||||
Reference in New Issue
Block a user