Device-agnostic driver registry + first-run setup
Make the device-adapter pattern selectable so the admin chooses hardware at install — per lane, from a catalog of supported drivers. Adding a device = registering one more driver; no business-logic change. packages/devices: - interfaces.ts: AccessControlDevice / ReaderDevice / CameraDevice / PrinterDevice (adds CameraDevice for entry/exit snapshot-on-event; access relay stays intent-only per "a barrier is not a door"). - registry.ts: driver catalog with per-driver config fields + factory, config validation, and a catalog payload for the setup UI. - drivers/: stub adapters — access (zkteco, esp32-relay), reader (wiegand, tcp-ip), camera (hikvision, dahua). Real vendor protocols TBD. packages/db: - lane_devices + setup_state tables (migration 0001); re-export query helpers. apps/server: - routes/setup.ts: GET /api/setup/catalog (public schema), and admin-only /assign, /state, /complete with registry validation before persisting. - extract auth.ts (requireJwtSecret, requireRole, JWT type aug). apps/web: - SetupWizard scaffold + api client: pick a driver per category for a lane, render its config fields. wiki: device-registry + first-run-setup concept pages; cross-link from device-adapter-pattern; index + log updated. Verified: full turbo build (5/5); catalog lists all drivers; admin assign persists; missing-config and no-token requests are rejected.
This commit is contained in:
@@ -1,33 +1,9 @@
|
||||
// Device-agnostic adapter interfaces.
|
||||
// @parking/devices — device-agnostic adapters + the driver registry that lets
|
||||
// an admin select between supported devices at first-run setup.
|
||||
//
|
||||
// Business logic talks ONLY to these interfaces, never to a device SDK. Swapping
|
||||
// hardware means writing a new adapter that implements one of these — nothing
|
||||
// else changes. See wiki/concepts/device-adapter-pattern.md.
|
||||
//
|
||||
// SAFETY: a barrier is NOT a door. The relay interface expresses INTENT only
|
||||
// (`pulseOpen`); it never times or forces a close against a vehicle. Physical
|
||||
// safety (induction loops, anti-crush, auto-reverse) lives in the barrier
|
||||
// operator's own firmware. See wiki/concepts/barrier-not-a-door.md.
|
||||
// See wiki/concepts/device-adapter-pattern.md and device-registry.md.
|
||||
|
||||
export interface CardReaderDevice {
|
||||
connect(): Promise<void>;
|
||||
onCardRead(cb: (cardNumber: string, door: number) => void): void;
|
||||
disconnect(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface TicketData {
|
||||
readonly ticketId: string;
|
||||
readonly lane: number;
|
||||
readonly issuedAt: string; // ISO-8601
|
||||
}
|
||||
|
||||
export interface PrinterDevice {
|
||||
printTicket(data: TicketData): Promise<void>;
|
||||
checkStatus(): Promise<"ready" | "offline" | "paper_out">;
|
||||
}
|
||||
|
||||
export interface RelayDevice {
|
||||
/** Express intent to open. NEVER timed/forced closed against a vehicle. */
|
||||
pulseOpen(doorId: number): Promise<void>;
|
||||
getDoorStatus(doorId: number): Promise<"open" | "closed">;
|
||||
}
|
||||
export * from "./interfaces.js";
|
||||
export * from "./registry.js";
|
||||
export { registerBuiltinDrivers } from "./drivers/index.js";
|
||||
export { setDeviceLogSink, type DeviceLogSink } from "./drivers/common.js";
|
||||
|
||||
Reference in New Issue
Block a user