Skip to content

arcnode-io/ems-device-api

Repository files navigation

EMS Device API 🌳

📖 About

The API is designed to manage a structure of devices. The devices could be hierarchical if necessary. The structure is organized as follows:

  • Devices: There can be many devices. They can be nested. Currently, this api is only two levels deep, for example, device-0 and device-1, but the structure could go deeper in the future.

The AsyncAPI v3 spec generated by this service is the single contract consumed by ems-industrial-gateway, ems-line-controller, and ems-hmi. Topic shape, payload schemas, unit vocabulary, naming, and versioning are governed by ems/topic_structure_adr.md.

Diagrams

rectangle "Device Level 0" as device0 {
  rectangle "Device 0A" as d0a
  rectangle "Device 0B" as d0b
  rectangle "Device 0C" as d0c
}

rectangle "Device Level 1" as device1 {
  rectangle "Device 1A" as d1a
  rectangle "Device 1B" as d1b
  rectangle "Device 1C" as d1c
  rectangle "Device 1D" as d1d
}

d0a -d-> d1a
d0a -d-> d1b
d0b -d-> d1c
d0c -d-> d1d

note right of device0
  Parent devices
  Accessed via /devices-0/:device0Id
end note

note right of device1
  Child devices
  Accessed via /devices-0/:device0Id/devices-1/:device1Id
end note

Topology Creation Flow

participant client
participant device_api
database typeorm_db

client -> device_api: POST /topology
device_api -> typeorm_db: save device sitemap
device_api -> device_api: generate AsyncAPI v3 spec\n(topics + schemas + x-* protocol bindings)
device_api -> typeorm_db: save spec
device_api -> client: topology created

Spec and Topology Consumers

participant device_api
participant industrial_gateway
participant ems_hmi

device_api -> industrial_gateway: GET /asyncapi\n(topics + x-modbus, x-snmp, x-connection)
device_api -> ems_hmi: GET /asyncapi\n(topics + message schemas)
device_api -> ems_hmi: GET /topology\n(devices + buses[])

GET /asyncapi and GET /topology serve different purposes for the HMI:

  • GET /asyncapi — messaging contract: measurement/command vocabulary per device, payload schemas, x-severity on enum values, enum:[...] constraint on each enum channel's value field (sourced from the class values: keys), poll rates. Used for TypeScript codegen at build time and topic subscription at runtime.
  • GET /topology — device catalog: the full DTM (devices + buses[]). Used to populate the module browser, derive module type for routing (bess_module.*/modules/bess/:id), and render the SLD from buses[].

The gateway only needs /asyncapi. The HMI needs both.

Build-time vs runtime

The spec serves two purposes at different times:

  • Build-time: Message schemas (e.g. BooleanReading, FloatReading, CommandPayload) are stable across all deployments. The HMI runs @asyncapi/modelina against the spec to generate TypeScript interfaces. These are compiled into the app. The gateway does the same for Rust structs.
  • Runtime: Topic paths (e.g. arcnode/{deployment_uuid}/{module_id}/{device_uuid}) are deployment-specific — they depend on which modules and devices are in the DTM. These cannot be compiled in. Both gateway and HMI fetch GET /asyncapi at startup and resolve topic paths dynamically.

Message types are compiled. Topic paths are fetched.

When the topology changes at runtime (device added, sensor goes offline, DTM re-provisioned), device-api publishes to system/topology-changed. Both the gateway and HMI subscribe to this topic — on receipt they re-fetch GET /asyncapi and diff against their current subscriptions.

API Endpoints

The API provides several endpoints to manage this structure:

  • POST /topology: Accepts the full DTM (devices + buses[]), persists it, and regenerates the AsyncAPI v3 spec. Called by platform-api at delivery time and on release rollout — not by the HMI.
  • GET /topology: Returns the full DTM — devices (all device-0 and device-1 instances with class and display name) and buses[] (electrical bus topology). The HMI's primary source for the module browser and SLD rendering. Module type is derived from the class field: bess_module.* → BESS, compute_module.* → Compute, grid_module.* → Grid.
  • GET /asyncapi: Returns the generated AsyncAPI v3 spec. Consumed by ems-industrial-gateway, ems-line-controller, and ems-hmi.
  • Devices-0: Devices at level 0 can be accessed at the /devices-0/:device0Id endpoint.
  • Devices-1: Devices at level 1 can be accessed at the /devices-0/:device0Id/devices-1/device1Id endpoint. In this scenario device0Id is the parent device and device1Id is the child device.

Releases

No releases published

Packages

 
 
 

Contributors