Skip to content

Infrastructure device integration foundation — facility fans first #723

Description

@rongxin-liu

Summary

Foundational work for integrating external infrastructure devices into Proto Fleet: facility-side equipment (fans, and later PDUs, dampers, pumps, ...) that the server can inventory and eventually command, with all protocol knowledge isolated behind a driver adapter registry so the core stays protocol-agnostic. Facility fans (single fan or fan group behind a PLC/drive) are the first device kind, and Modbus TCP is the first driver adapter — motivated by curtailment sequencing (fans off after a delay once miners are curtailed; fans on first, then miners restored after a delay), but the foundation is deliberately kind- and protocol-agnostic.

No device behavior activates from this work — the write path lands with the later Modbus I/O and reconciler-sequencing phases. This is inert surface area until then.

Delivered by #724. Full design: docs/plans/2026-07-09-facility-fan-curtailment-integration-plan.md (see "Delivery: PR grouping").

Scope

  • Migration pair: infrastructure_device table — site_id FK, building_name, name, device_kind (single_fan | fan_group), fan_count, enabled, driver_type (text), driver_config (opaque jsonb owned by the adapter), timestamps
  • proto/infrastructure/v1/infrastructure.proto: InfrastructureDevice (opaque driver_config, no per-protocol messages; device_kind/driver_type as strings so new kinds and protocols are never wire breaks) + InfrastructureService List/Get/Create/Update/Delete; just gen, generated code committed together
  • Driver seam: server/internal/domain/infrastructure/driver/Controller interface (SetState(ctx, device, DesiredState), ValidateConfig, Capabilities(); doc comment reserving ReadStatus for v2 read-back) and driver_type → adapter factory registry
  • driver/modbustcp/ config parsing + ValidateConfig: unit ID 1–247, port 1–65535, register address 0–65535, endpoint restricted to private/loopback/link-local (mirrors the MQTT subscriber's validateBrokerTransport guard). Protocol I/O is a later phase, not here
  • sqlc queries (server/sqlc/queries/infrastructure_device.sql), org-scoped
  • Domain CRUD service: protocol-blind field validation in core, driver_config validation delegated to the registry; transactional writes with a site row-lock (TOCTOU guard vs concurrent DeleteSite)
  • DeleteSite cascade soft-deletes the site's infrastructure devices
  • Connect handlers gated on site:read / site:manage
  • Tests: Postgres integration (CRUD, validation matrix, org isolation, site cascade), driver/adapter unit tests incl. adversarial endpoint vectors, handler auth-gate and translation tests

Out of scope (later phases of the fan integration)

  • Client add/edit/persist UI
  • Response-profile fan settings (facility_fan_device_ids, delays) + the device deletion reference-guard that depends on that column
  • Modbus TCP write path + dev simulator
  • Reconciler fan sequencing (delays, per-cycle re-assertion, terminal-path handling)

Extensibility notes (for future device kinds like PDUs)

  • New protocol (SNMP, HTTP, BACnet, ...): one adapter package + one registry line + a client form module — no schema/proto/handler changes.
  • New device kind (e.g. PDU): a migration extending the kind CHECK constraints plus additive edits to the proto validation list and models.ValidKind; sub-component control (PDU outlets) fits by modeling each outlet as its own device row.
  • New capability (e.g. fan speed): additive DesiredState fields, gated per-driver via Capabilities().

Repo hygiene

just gen after proto/sqlc/migration changes; feature branch (never main); just lint before PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions