feat(booth): live lane busy/free barrier lights from camera vehicle detection
A Hikvision vehicle detection (eventType=VMD, targetType=vehicle) on a
camera bound to entry/exit now marks that lane "busy" and shows it as a
barrier light beside the scan input on the booth (green=free, red=busy).
Advisory only — it gates nothing (never blocks a ticket or opens a barrier).
- Parse eventState (active/inactive) from the Hik payload.
- LaneStatus tracker: a vehicle `active` event marks the camera's bound lane
busy + arms an auto-clear timer. This camera class sends no leave/`inactive`
signal, so "free" is timeout-driven (LANE_BUSY_TTL_MS, default 90s; the
camera re-fires `active` while a car sits there, refreshing the timer). A
"both"-direction camera marks both lanes.
- Push lane-status over the existing booth WS (+ in the hello snapshot);
live-store holds { entry, exit }; two BarrierLight icons render it.
- i18n booth.laneEntry/laneExit (sq + en).
Tests: lane-status.test.ts (7 — busy/free, TTL auto-clear, timer re-arm,
no re-emit while busy, both/exit direction, unknown device). server 120/120;
web + server build/lint green.
Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -76,6 +76,16 @@ export interface DeviceStatusEvent {
|
||||
readonly checkedAt: string; // ISO-8601
|
||||
}
|
||||
|
||||
/** Lane occupancy from a camera's vehicle detection — a per-direction "busy/free"
|
||||
* the booth shows as barrier lights. ADVISORY ONLY: a detection is a hint, never a
|
||||
* gate (it never blocks a ticket or opens a barrier). "busy" is set by a vehicle
|
||||
* `active` event; it auto-clears to "free" after a timeout (this camera class sends
|
||||
* no leave/`inactive` signal — see wiki/entities/lpr-camera.md). */
|
||||
export interface LaneStatusEvent {
|
||||
readonly entry: boolean; // true = busy (a vehicle is at the entry vicinity)
|
||||
readonly exit: boolean; // true = busy (a vehicle is at the exit vicinity)
|
||||
}
|
||||
|
||||
class DeviceEventBus extends EventEmitter {
|
||||
emitInput(event: DeviceInputEvent): void {
|
||||
this.emit("input", event);
|
||||
@@ -128,6 +138,16 @@ class DeviceEventBus extends EventEmitter {
|
||||
this.on("ledger", cb);
|
||||
return () => this.off("ledger", cb);
|
||||
}
|
||||
|
||||
/** Emitted whenever a lane's busy/free state CHANGES (from camera vehicle
|
||||
* detection). Drives the booth's barrier lights. Advisory only. */
|
||||
emitLaneStatus(event: LaneStatusEvent): void {
|
||||
this.emit("lane-status", event);
|
||||
}
|
||||
onLaneStatus(cb: (event: LaneStatusEvent) => void): () => void {
|
||||
this.on("lane-status", cb);
|
||||
return () => this.off("lane-status", cb);
|
||||
}
|
||||
}
|
||||
|
||||
/** Process-wide device event bus. */
|
||||
|
||||
Reference in New Issue
Block a user