Files
adas-core/adas-core.module.LightBeacons/README.md
T
2026-06-26 10:29:23 +02:00

223 lines
11 KiB
Markdown

# adas-core.module.LightBeacons — Light Beacon Module
> A **functional module** of the ADAS Core platform.
> Encapsulates all logic for managing, controlling, and monitoring **light beacon devices** deployed at Points of Care. This module operates as an independent satellite that the Host registers at startup, maintaining full separation from unrelated domain concerns.
---
## Table of Contents
1. [Overview](#overview)
2. [Responsibilities](#responsibilities)
3. [Project Structure](#project-structure)
4. [Dependencies](#dependencies)
5. [Device Abstraction](#device-abstraction)
6. [Beacon Control Flow](#beacon-control-flow)
7. [LightBeaconService Orchestration](#lightbeaconservice-orchestration)
8. [Design Rules](#design-rules)
---
## Overview
`adas-core.module.LightBeacons` is a modular satellite project that encapsulates every concern related to light beacon hardware within the ADAS Core ecosystem. It is responsible for discovering beacon configurations, translating clinical observation statuses into color commands, dispatching those commands to physical devices, and broadcasting state changes to connected subscribers.
Key characteristics:
- **Modular Monolith Pattern** — Operates as a self-contained module with its own device drivers, service logic, and repository interactions. No other module depends on it.
- **Polymorphic Devices** — Supports multiple beacon hardware models via an abstract `LightBeacon` base class. New models are added by inheriting from the base and registering the type string in `LightBeaconService`.
- **State Caching** — Maintains an in-memory `ConcurrentDictionary` of current beacon colors per Point-of-Care to avoid redundant commands.
- **Subscriber Broadcasting** — Pushes beacon color changes to WebSocket / SignalR subscribers via `IClientMessageService`.
- **HTTP-Based Drivers** — Current concrete implementation (`Turktbens2LightBeacon`) communicates over HTTP, parses HTML status pages, and sends form-encoded commands.
---
## Responsibilities
| Concern | What this project does |
|---------|----------------------|
| **Device Abstraction** | Defines `LightBeacon` and `LightBeaconAbstract` base classes that every beacon driver implements. |
| **Hardware Control** | Sends color commands (red, blue, yellow, off) to physical beacon devices over the network. |
| **Status Retrieval** | Reads the current color state from a beacon by scraping its web interface or calling its API. |
| **Color Alert Generation** | Translates `PatientObservation` status (Warning / Alert) into the configured beacon color. |
| **Configuration CRUD** | Manages beacon device configurations (name, URL, port, password, type) through `ILightBeaconRepository`. |
| **Association Management** | Links beacons to Points of Care via `Configuration.BeaconIdList`. |
| **State Caching** | Tracks per-PoC color in memory to suppress duplicate commands. |
| **Subscriber Broadcast** | Notifies all subscribers of a PoC when the beacon color changes via `BeaconResponse`. |
| **Retry Logic** | Implements exponential-backoff retry for transient network failures when sending commands. |
| **Audit Logging** | Emits audit events for beacon state changes and configuration mutations. |
---
## Project Structure
```
adas-core.module.LightBeacons/
├── Devices/
│ ├── LightBeacon.cs # Abstract base class for all beacon drivers
│ ├── LightBeaconAbstract.cs # Legacy abstract base with static factory
│ └── Turktbens2LightBeacon.cs # Concrete HTTP-based driver for TURKTBENS2
└── Services/
└── LightBeaconService.cs # Orchestrates beacon control, caching, and broadcasting
```
| File | Role |
|------|------|
| `LightBeacon.cs` | Abstract base defining `BlueCode()`, `RedCode()`, `YellowCode()`, `PowerOffLed()`, `GetBeaconColor()`, and `GenerateColorAlert()`. |
| `LightBeaconAbstract.cs` | Older abstraction with `Host`/`Password` fields and reflection-based static factory `GetBeaconDevice()`. |
| `Turktbens2LightBeacon.cs` | HTTP driver: parses HTML status pages, sends form-encoded commands, retry with exponential backoff, emulation mode. |
| `LightBeaconService.cs` | Service implementing `ILightBeaconService`. Mediates between Application-layer commands and physical devices. |
---
## Dependencies
### Downstream References
| Project | Role |
|---------|------|
| `adas-core.Application` | Consumes `ILightBeaconRepository`, `IPointOfCareService`, `ISubscribersService`, `IClientMessageService`, and `ILightBeaconService` interface. |
| `adas-core.Domain` | Uses `LightBeaconColor`, `PatientObservation`, `StatusEnum`, `PointOfCare`, `BeaconResponse`, `OperationType`, `EquatableDictionary`, and domain exceptions. |
### Upstream References (projects that depend on this)
| Project | Reason |
|---------|--------|
| `adas-core` (Host) | Registers the module at startup. Host controllers invoke `ILightBeaconService` endpoints. |
| `adas-core.Infrastructure` | `LightBeaconRepository` (in Infrastructure) implements `ILightBeaconRepository` defined in Application. |
### NuGet Packages
| Package | Version | Purpose |
|---------|---------|---------|
| `HtmlAgilityPack` | 1.12.4 | HTML DOM parsing for scraping beacon status pages. |
| `AuditLogs` | 1.0.59 | Audit tagging on beacon state transitions and configuration changes. |
---
## Device Abstraction
### LightBeacon (Primary Async Abstraction)
The main device contract for all modern beacon drivers:
```csharp
public abstract class LightBeacon(EquatableDictionary<string, object> options)
{
public abstract Task BlueCode();
public abstract Task RedCode();
public abstract Task YellowCode();
public abstract Task PowerOffLed();
public abstract Task GenerateColorAlert(PatientObservation obs);
public abstract Task<LightBeaconColor> GetBeaconColor();
}
```
### Turktbens2LightBeacon (Concrete Driver)
- **Communication** — HTTP POST to the beacon's embedded web server.
- **Status Reading** — Scrapes `/IO01_03.html` and `/IO12_03.html`, parses checkbox states with `HtmlAgilityPack`.
- **Command Mapping** — Converts `LightBeaconColor` to a 3-bit binary code sent as form fields.
- **Retry Policy** — Exponential backoff (500 ms -> 1 s -> 2 s) with max 3 retries on network failures.
- **Emulation Mode** — When `emulate: true`, skips HTTP calls and returns `LightBeaconColor.Off` for reads.
- **Port Selection** — Supports multi-port beacons (ports 1-4) via `_portMappings`.
---
## Beacon Control Flow
```
[PatientObservation Status Change] --> [LightBeaconService.GenerateColorAlert]
|
v
[Map StatusEnum -> LightBeaconColor]
[Warning -> WarnColor, Alert -> AlertColor]
|
v
[GetBeacon(beaconId)]
[Resolve device from config.Type]
|
v
[SendColor(poc, color)]
[Cache check: skip if unchanged]
|
v
[Dispatch to physical device]
[Turktbens2: HTTP POST with retry]
|
v
[Update in-memory cache]
[SendBeaconBroadcast -> subscribers]
|
v
[Return / Log]
```
---
## LightBeaconService Orchestration
### Color Dispatch
| Method | Behavior |
|--------|----------|
| `SendColor(pocId, color)` | Resolves PoC, retrieves associated beacons, dispatches color command, updates cache, broadcasts. |
| `PowerOffLed(pocId)` | Sets all associated beacons to `LightBeaconColor.Off`, updates cache. |
| `GetColor(pocId)` | Returns cached color or queries the physical beacon for current state. |
### Alert Generation
`GenerateColorAlert(PatientObservation)` converts:
- `StatusEnum.Type.Warning` -> `obs.WarnColor` (e.g., `"Yellow"`, `"001"`)
- `StatusEnum.Type.Alert` -> `obs.AlertColor` (e.g., `"Red"`, `"100"`)
- Other -> `LightBeaconColor.Off`
The method builds an HTTP form body and sends it via `SendMessage`.
### Broadcasting
`SendBeaconBroadcast(poc, color)`:
1. Queries `ISubscribersService.GetSubscribers()` filtered by `LocationIds`.
2. Builds a `BeaconResponse` (color string, PoC ID, Unit ID).
3. Sends to each subscriber via `IClientMessageService.SendAsync(..., OperationType.Beacon, ...)`.
This drives real-time UI updates in client applications.
### Configuration CRUD
| Method | Behavior |
|--------|----------|
| `InsertOne(beacon)` | Validates uniqueness by name, inserts via repository. |
| `UpdateOne(beacon)` | Updates record, returns refreshed entity. |
| `GetPaginatedBeacons(filter)` | Paginated list with `InUse` flag computed from `PointOfCare` associations. |
| `GetSearchByName(text)` | Partial-match search on beacon names. |
### State Caching
`_locationsWithColor` is a `ConcurrentDictionary<ObjectId, LightBeaconColor>` keyed by PoC ID:
- Prevents redundant commands when the requested color matches the cached color.
- Updated under `lock()` during writes to avoid race conditions.
- Falls back to device query when the PoC is not in the cache.
---
## Design Rules
1. **Module Independence** — No project outside Infrastructure depends on this module. The Host registers it via assembly scan.
2. **Device Abstraction** — All hardware-specific code is isolated in `Devices/`. `LightBeaconService` only sees the `LightBeacon` base type.
3. **No Blocking Calls** — Modern drivers use async/await exclusively. Legacy `LightBeaconAbstract` remains synchronous but is deprecated.
4. **Cache Consistency** — In-memory cache is always updated after a successful physical-device command, never before.
5. **Graceful Degradation** — Network failures return `LightBeaconColor.Off` rather than crashing the observation pipeline.
6. **Retry Discipline** — Exponential backoff capped at 3 attempts. Logs each retry attempt with `Warning`, final failure with `Error`.
7. **Audit Emission** — Every `InsertOne`, `UpdateOne`, `SendColor`, and `PowerOffLed` emits an audit event.
8. **Configuration-Driven** — Beacon URL, port, password, timeout, and emulation flag all come from `LightBeacon.Options`. No hardcoded device addresses.
9. **Factory Extensibility** — Adding a new beacon model requires only: (a) inherit `LightBeacon`, (b) add a `case` in `LightBeaconService.GetBeacon`, (c) register in DI. No other code changes.
10. **Broadcast Decoupling**`IClientMessageService` is an injected abstraction. Replacing SignalR with WebSockets or SSE requires no changes in this module.
---
<p align="center">
Back to <a href="../README.md">adas-core Root README</a>
</p>