rama creada apartir de master en j

This commit is contained in:
jrojas
2026-06-26 10:29:23 +02:00
parent 319fd3dfb0
commit c1517fda87
2810 changed files with 1927392 additions and 25392 deletions
@@ -3,17 +3,53 @@ using adas_core.Domain.Utils;
namespace adas_core.module.LightBeacons.Devices;
/// <summary>
/// Abstract class representing a light beacon device. This class defines the basic structure and behavior of a light beacon, including methods for controlling the beacon's colors and generating alerts based on patient observations.
/// The specific implementation of these methods will depend on the type of light beacon being used and the requirements of the application.
/// </summary>
/// <param name="options">A dictionary containing configuration options for the light beacon. This can include settings such as the beacon's IP address, port number, color configurations, and other relevant parameters.</param>
public abstract class LightBeacon(EquatableDictionary<string, object> options)
{
/// <summary>
/// A dictionary containing configuration options for the light beacon. This can include settings such as the beacon's IP address, port number, color configurations, and other relevant parameters.
/// </summary>
protected readonly EquatableDictionary<string, object> Options = options;
/// <summary>
/// Turns the light beacon blue. This method should be implemented to control the beacon's hardware or software interface to change its color to blue.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public abstract Task BlueCode();
/// <summary>
/// Turns the light beacon red. This method should be implemented to control the beacon's hardware or software interface to change its color to red.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public abstract Task RedCode();
/// <summary>
/// Turns the light beacon yellow. This method should be implemented to control the beacon's hardware or software interface to change its color to yellow.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public abstract Task YellowCode();
/// <summary>
/// Turns off the light beacon. This method should be implemented to control the beacon's hardware or software interface to turn off the light, regardless of its current color.
/// </summary>
/// <returns></returns>
public abstract Task PowerOffLed();
/// <summary>
/// Generates a color alert based on the provided patient observation. This method should analyze the patient observation and determine the appropriate color alert to display on the light beacon.
/// The specific logic for determining the color alert will depend on the criteria defined in the patient observation, such as thresholds for vital signs or other relevant parameters.
/// </summary>
/// <param name="obs">The patient observation used to determine the appropriate color alert.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public abstract Task GenerateColorAlert(PatientObservation obs);
/// <summary>
/// Retrieves the current color of the light beacon. This method should be implemented to query the beacon's hardware or software interface to determine its current color state and return it as a value from the LightBeaconColor enumeration.
/// </summary>
/// <returns>A task representing the asynchronous operation, with a result of the current color of the light beacon.</returns>
public abstract Task<Domain.Enums.LightBeaconColor> GetBeaconColor();
}
@@ -2,20 +2,55 @@
namespace adas_core.module.LightBeacons.Devices;
/// <summary>
/// Abstract class for LightBeacon devices. Each specific LightBeacon model should inherit from this class and implement the abstract methods for controlling the LED states (CodeBlue, CodeRed, CodeYellow, PowerOffLed).
/// </summary>
/// <param name="host">The host address of the LightBeacon device. This is typically the IP address or hostname used to connect to the device for controlling its LED states.</param>
/// <param name="password">The password used for authenticating with the LightBeacon device. This is required to establish a connection and control the device's LED states securely.</param>
public abstract class LightBeaconAbstract(string host, string password)
{
/// <summary>
/// The host address of the LightBeacon device. This is typically the IP address or hostname used to connect to the device for controlling its LED states.
/// </summary>
public string Host = host;
/// <summary>
/// The password used for authenticating with the LightBeacon device. This is required to establish a connection and control the device's LED states securely.
/// </summary>
public string Password = password;
/// <summary>
/// Abstract method to set the LightBeacon device to Code Blue state. This method should be implemented by each specific LightBeacon model to control the LED state accordingly.
/// </summary>
/// <param name="entry">An object parameter that can be used to pass additional information or context needed for setting the LED state.</param>
public abstract void CodeBlue(object entry);
/// <summary>
/// Abstract method to set the LightBeacon device to Code Red state. This method should be implemented by each specific LightBeacon model to control the LED state accordingly.
/// </summary>
/// <param name="entry">An object parameter that can be used to pass additional information or context needed for setting the LED state.</param>
public abstract void CodeRed(object entry);
/// <summary>
/// Abstract method to set the LightBeacon device to Code Yellow state. This method should be implemented by each specific LightBeacon model to control the LED state accordingly.
/// </summary>
/// <param name="entry">An object parameter that can be used to pass additional information or context needed for setting the LED state.</param>
public abstract void CodeYellow(object entry);
/// <summary>
/// Abstract method to power off the LED of the LightBeacon device. This method should be implemented by each specific LightBeacon model to control the LED state accordingly.
/// </summary>
/// <param name="entry">An object parameter that can be used to pass additional information or context needed for setting the LED state.</param>
public abstract void PowerOffLed(object entry);
/// <summary>
/// Static factory method to create an instance of a specific LightBeacon device based on the provided driver name, host, and password.
/// This method uses reflection to dynamically instantiate the appropriate LightBeacon model class that inherits from LightBeaconAbstract.
/// </summary>
/// <param name="driver">The name of the driver class for the specific LightBeacon model.</param>
/// <param name="host">The host address of the LightBeacon device.</param>
/// <param name="password">The password for authenticating with the LightBeacon device.</param>
/// <returns>An instance of the specific LightBeacon device, or null if the driver class is not found or cannot be instantiated.</returns>
public static LightBeaconAbstract? GetBeaconDevice(string driver, string host, string password)
{
var modelType = Type.GetType($"adas_core.Drivers.LightBeacon.{driver}");
@@ -6,15 +6,47 @@ using Serilog;
namespace adas_core.module.LightBeacons.Devices;
/// <summary>
/// This class represents a light beacon device that can be controlled via HTTP requests. It retrieves the current color of the beacon by parsing HTML pages and sends commands to change the beacon's color.
/// The class includes error handling and retry logic for network operations, and it can be configured to emulate the beacon for testing purposes.
/// </summary>
public class Turktbens2LightBeacon : LightBeacon
{
/// <summary>
/// The _client field is an instance of HttpClient that is used to send HTTP requests to the beacon's web interface. It is initialized with a timeout value specified in the options, and it is disposed of after use to free up resources.
/// </summary>
private readonly HttpClient _client;
/// <summary>
/// The _code field is a constant integer that represents a base code used in the construction of the HTTP request body when sending commands to the beacon.
/// It is combined with specific port and color information to form the complete command sent to the beacon's web interface.
/// </summary>
private readonly int _code = 75;
/// <summary>
/// The _emulate field is a boolean that indicates whether the beacon should operate in emulation mode.
/// When set to true, the GetBeaconColor method will return Off without attempting to access the web interface, allowing for testing and development without requiring a physical beacon device.
/// </summary>
private readonly bool _emulate;
/// <summary>
/// The _logger field is an instance of ILogger from the Serilog library, used for logging information, warnings, and errors throughout the class.
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// The _password field is a string that holds the password used for authentication when sending commands to the beacon's web interface.
/// </summary>
private readonly string _password = "password";
/// <summary>
/// The _port field is an integer that specifies which port of the beacon is being controlled. The beacon has multiple ports, and this field determines which one is targeted for color changes and status retrieval.
/// </summary>
private readonly int _port = 1;
/// <summary>
/// The _portMappings dictionary maps each port number to the corresponding indices of the checkbox inputs in the HTML page that represent the output states for that port.
/// </summary>
private readonly Dictionary<int, int[]> _portMappings = new()
{
{ 1, [0, 1] },
@@ -23,6 +55,9 @@ public class Turktbens2LightBeacon : LightBeacon
{ 4, [6, 7] }
};
/// <summary>
/// The _ports dictionary maps each port number to an array of strings that represent the specific parameters used in the HTTP request body for controlling the beacon's color.
/// </summary>
private readonly Dictionary<int, string[]> _ports = new()
{
{ 1, ["|1|1|1|2", "|12|0|1|0", "|1|0|1|2"] },
@@ -31,10 +66,21 @@ public class Turktbens2LightBeacon : LightBeacon
{ 4, ["|1|7|1|2", "|12|3|1|0", "|1|6|1|2"] }
};
/// <summary>
/// The _timeout field is an integer that specifies the timeout duration in seconds for HTTP requests made to the beacon's web interface. If a request takes longer than this duration, it will be aborted and an error will be logged.
/// </summary>
private readonly int _timeout = 30;
/// <summary>
/// The _url field is a string that holds the base URL of the beacon's web interface. This URL is used to construct the full endpoints for retrieving the beacon's status and sending commands to change its color.
/// </summary>
private readonly string _url;
/// <summary>
/// The constructor of the Turktbens2LightBeacon class initializes a new instance of the class with the specified options.
/// </summary>
/// <param name="options">A dictionary containing the configuration options for the beacon.</param>
/// <exception cref="ArgumentException">Thrown when required options are missing or invalid.</exception>
public Turktbens2LightBeacon(EquatableDictionary<string, object> options) : base(options)
{
_logger = Log.ForContext<Turktbens2LightBeacon>();
@@ -61,7 +107,7 @@ public class Turktbens2LightBeacon : LightBeacon
/// bits, Vaux html changes if logged/not logged
/// returns OFF when some error appears or try to parse the bits to BalizaColors enum
/// </summary>
/// <returns></returns>
/// <returns>The current color of the beacon as a LightBeaconColor enum value.</returns>
public override async Task<LightBeaconColor> GetBeaconColor()
{
if (_emulate)
@@ -142,6 +188,12 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The BlueCode method sends a command to the beacon's web interface to change the beacon's color to blue.
/// It constructs the appropriate HTTP request body using the predefined code and port mappings, and it includes error handling to log any exceptions that occur during the process.
/// If an error occurs, it logs the error message and rethrows the exception to be handled by the caller.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public override async Task BlueCode()
{
try
@@ -155,6 +207,10 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The RedCode method sends a command to the beacon's web interface to change the beacon's color to red.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public override async Task RedCode()
{
try
@@ -168,6 +224,10 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The YellowCode method sends a command to the beacon's web interface to change the beacon's color to yellow.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public override async Task YellowCode()
{
try
@@ -181,6 +241,10 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The GreenCode method sends a command to the beacon's web interface to change the beacon's color to green.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public override async Task PowerOffLed()
{
try
@@ -194,12 +258,22 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The GenerateColorAlert method is not implemented in this class. It is intended to generate a color alert based on the provided PatientObservation, but the specific implementation details are not defined in this class and will need to be implemented in a subclass or by the caller.
/// </summary>
/// <param name="obs">The patient observation based on which the color alert should be generated.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <exception cref="NotImplementedException"></exception>
public override Task GenerateColorAlert(PatientObservation obs)
{
throw new NotImplementedException();
}
/// <summary>
/// The Send method is a private helper method that constructs and sends an HTTP request to the beacon's web interface to change the beacon's color based on the provided ColorsCodes enum value.
/// </summary>
/// <param name="colorCodes">The color code to be sent to the beacon.</param>
/// <returns>A task representing the asynchronous operation.</returns>
private async Task Send(ColorsCodes colorCodes)
{
var clr = ((int)colorCodes).ToString().PadLeft(3, '0');
@@ -224,7 +298,14 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The SendMessage method is a private helper method that sends an HTTP POST request to the beacon's web interface with the specified body parameters.
/// It includes retry logic to handle transient network errors, attempting to resend the request up to a maximum number of retries with exponential backoff between attempts.
/// If the request fails after all retry attempts, it logs an error message and rethrows the exception.
/// </summary>
/// <param name="body">The body parameters to be sent in the HTTP POST request.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException"></exception>
protected async Task SendMessage(Dictionary<string, string> body)
{
if (string.IsNullOrEmpty(_url)) throw new InvalidOperationException("URL is not set.");
@@ -265,6 +346,11 @@ public class Turktbens2LightBeacon : LightBeacon
}
}
/// <summary>
/// The ColorsCodes enum defines the binary codes corresponding to each color that the beacon can display.
/// Each color is represented by a three-bit binary code, where each bit corresponds to a specific output state of the beacon.
/// The enum values are used in the Send method to construct the appropriate command for changing the beacon's color.
/// </summary>
private enum ColorsCodes
{
Off = 000,
+223
View File
@@ -0,0 +1,223 @@
# 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>
@@ -19,6 +19,11 @@ using LightBeacon = adas_core.Domain.Models.MongoModels.LightBeacon;
namespace adas_core.module.LightBeacons.Services;
/// <summary>
/// Service responsible for managing light beacons, including sending color alerts, powering off LEDs, and broadcasting beacon status to subscribers.
/// It interacts with the point of care service to determine which beacons are associated with specific locations and maintains an in-memory cache of the current color state of each location's beacon.
/// The service also handles CRUD operations for light beacon configurations stored in a MongoDB repository.
/// </summary>
public class LightBeaconService : ILightBeaconService
{
private readonly IClientMessageService _clientMessageService;
@@ -30,6 +35,15 @@ public class LightBeaconService : ILightBeaconService
private readonly ILightBeaconRepository _lightBeaconRepository;
private readonly ISubscribersService _subscribersService;
/// <summary>
/// Initializes a new instance of the LightBeaconService class with the specified dependencies and configuration settings.
/// </summary>
/// <param name="apiSettings">The API settings containing configuration values for the light beacon service.</param>
/// <param name="logger">The logger instance for logging information and errors.</param>
/// <param name="clientMessageService">The client message service for sending messages to clients.</param>
/// <param name="subscribersService">The subscribers service for managing subscribers.</param>
/// <param name="pointOfCareService">The point of care service for retrieving point of care information.</param>
/// <param name="lightBeaconRepository">The light beacon repository for managing light beacon data.</param>
public LightBeaconService(
IOptions<ApiSettings> apiSettings,
ILogger<LightBeaconService> logger,
@@ -56,8 +70,18 @@ public class LightBeaconService : ILightBeaconService
//_ = SetLocationsWithColor();
}
/// <summary>
/// Gets the URL for the light beacon API from the configuration settings. This URL is used to send commands to the light beacon devices.
/// </summary>
private string? Url { get; }
/// <summary>
/// Powers off the LED of the light beacon associated with the specified point of care ID. It retrieves the point of care information,
/// checks if there are any associated beacons, and sends a command to turn off the LED for each associated beacon.
/// The method also updates the in-memory cache to reflect that the beacon is now off for the specified location.
/// </summary>
/// <param name="pocId">The ID of the point of care for which to power off the LED.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task PowerOffLed(ObjectId pocId)
{
var poc = await _pointOfCareService.FindById(pocId);
@@ -70,6 +94,12 @@ public class LightBeaconService : ILightBeaconService
_ = PowerOffLed(poc);
}
/// <summary>
/// Inserts a new light beacon configuration into the repository. Before inserting, it checks if a beacon with the same name already exists to prevent duplicates.
/// </summary>
/// <param name="beacon">The light beacon configuration to insert.</param>
/// <returns>The inserted light beacon configuration, or null if the insertion failed.</returns>
/// <exception cref="Exception">Thrown if a light beacon with the same name already exists.</exception>
public async Task<LightBeacon?> InsertOne(LightBeacon beacon)
{
var beaconFound = await _lightBeaconRepository.GetByName(beacon.Name);
@@ -77,6 +107,12 @@ public class LightBeaconService : ILightBeaconService
return await _lightBeaconRepository.InsertOneAsyncAndReturn(beacon);
}
/// <summary>
/// Retrieves a paginated list of light beacons from the repository based on the specified pagination filter.
/// The method also checks which beacons are currently in use by querying the point of care service and updates the "InUse" property of each beacon accordingly before returning the paginated response.
/// </summary>
/// <param name="filter">The pagination filter to apply when retrieving the light beacons.</param>
/// <returns>A paginated response containing the light beacons that match the specified filter.</returns>
public async Task<PaginationResponse<LightBeacon>> GetPaginatedBeacons(PaginationFilter filter)
{
var usedRelayIds = await _pointOfCareService.FindAllIdBeaconsInUse();
@@ -117,17 +153,34 @@ public class LightBeaconService : ILightBeaconService
return new PaginationResponse<LightBeacon>(data, filter.PageNumber, filter.PageSize, count);
}
/// <summary>
/// Searches for light beacons in the repository that match the specified text in their name. The method returns a list of light beacons whose names contain the provided search text, allowing for partial matches.
/// </summary>
/// <param name="textToSearch">The text to search for in the names of the light beacons.</param>
/// <returns>A list of light beacons whose names contain the specified search text.</returns>
public async Task<List<LightBeacon>> GetSearchByName(string textToSearch)
{
return await _lightBeaconRepository.GetSearchByName(textToSearch);
}
/// <summary>
/// Updates an existing light beacon configuration in the repository.
/// The method takes a light beacon object with updated properties, updates the corresponding record in the repository based on the beacon's ID, and then retrieves and returns the updated light beacon configuration to confirm the changes.
/// </summary>
/// <param name="beacon">The light beacon object with updated properties.</param>
/// <returns>The updated light beacon configuration, or null if the update failed.</returns>
public async Task<LightBeacon?> UpdateOne(LightBeacon beacon)
{
await _lightBeaconRepository.UpdateOneAsync(beacon.Id, beacon);
return await _lightBeaconRepository.GetById(beacon.Id);
}
/// <summary>
/// Powers off the LED of the light beacon associated with the specified point of care.
/// The method checks if the beacon is already off to avoid unnecessary commands, retrieves the beacon configuration for the point of care, and sends a command to turn off the LED for each associated beacon.
/// </summary>
/// <param name="poc">The point of care for which to power off the LED.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task PowerOffLed(PointOfCare poc)
{
if (_locationsWithColor.TryGetValue(poc.Id, out var lightBeaconColor) &&
@@ -152,6 +205,10 @@ public class LightBeaconService : ILightBeaconService
_logger.LogDebug("Locations with color cached: {dct}", DictionaryToString(_locationsWithColor));
}
/// <summary>
/// Generates a color alert for a patient observation by determining the appropriate color based on the observation's status and configured colors for warnings and alerts.
/// </summary>
/// <param name="obs">The patient observation for which to generate the color alert.</param>
public void GenerateColorAlert(PatientObservation obs)
{
try
@@ -230,6 +287,13 @@ public class LightBeaconService : ILightBeaconService
}
}
/// <summary>
/// Sends a color command to the light beacon associated with the specified point of care ID.
/// The method retrieves the point of care information, checks if there are any associated beacons, and sends a command to set the specified color for each associated beacon.
/// </summary>
/// <param name="pocId">The ID of the point of care for which to send the color command.</param>
/// <param name="color">The color to set on the light beacon.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task SendColor(ObjectId pocId, LightBeaconColor color)
{
var poc = await _pointOfCareService.FindById(pocId);
@@ -237,6 +301,13 @@ public class LightBeaconService : ILightBeaconService
_ = SendColor(poc, color);
}
/// <summary>
/// Sends a color command to the light beacon associated with the specified point of care.
/// The method checks if the beacon is already set to the specified color to avoid unnecessary commands, retrieves the beacon configuration for the point of care, and sends a command to set the specified color for each associated beacon.
/// </summary>
/// <param name="poc">The point of care for which to send the color command.</param>
/// <param name="color">The color to set on the light beacon.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task SendColor(PointOfCare poc, LightBeaconColor color)
{
try
@@ -285,6 +356,11 @@ public class LightBeaconService : ILightBeaconService
}
}
/// <summary>
/// Retrieves the current color state of the light beacon associated with the specified point of care ID.
/// </summary>
/// <param name="pocId">The ID of the point of care for which to retrieve the color state.</param>
/// <returns>A task representing the asynchronous operation, with the current color state of the light beacon.</returns>
public async Task<LightBeaconColor> GetColor(ObjectId pocId)
{
var poc = await _pointOfCareService.FindById(pocId);
@@ -292,6 +368,11 @@ public class LightBeaconService : ILightBeaconService
return await GetColor(poc);
}
/// <summary>
/// Retrieves the current color state of the light beacon associated with the specified point of care.
/// </summary>
/// <param name="poc">The point of care for which to retrieve the color state.</param>
/// <returns>A task representing the asynchronous operation, with the current color state of the light beacon.</returns>
public async Task<LightBeaconColor> GetColor(PointOfCare poc)
{
try
@@ -316,6 +397,12 @@ public class LightBeaconService : ILightBeaconService
}
}
/// <summary>
/// Sends a beacon broadcast message to all subscribers associated with the specified point of care, indicating the current color state of the beacon.
/// </summary>
/// <param name="poc">The point of care for which to send the color command.</param>
/// <param name="color">The color to set on the light beacon.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task SendBeaconBroadcast(PointOfCare poc, LightBeaconColor color)
{
var subscribers = _subscribersService.GetSubscribers()
@@ -327,6 +414,12 @@ public class LightBeaconService : ILightBeaconService
await _clientMessageService.SendAsync(subscriber.Id, OperationType.Beacon, bc);
}
/// <summary>
/// Sends a beacon broadcast message to all subscribers associated with the specified point of care ID, indicating the current color state of the beacon.
/// </summary>
/// <param name="pocId">The ID of the point of care for which to send the color command.</param>
/// <param name="color">The color to set on the light beacon.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task SendBeaconBroadcast(ObjectId pocId, LightBeaconColor color)
{
var poc = await _pointOfCareService.FindById(pocId);
@@ -334,6 +427,12 @@ public class LightBeaconService : ILightBeaconService
_ = SendBeaconBroadcast(poc, color);
}
/// <summary>
/// Sends a raw message to the light beacon API using an HTTP POST request.
/// The method constructs the request body with the specified parameters, sends the request to the configured URL, and logs the result of the operation, including any errors that may occur during the process.
/// </summary>
/// <param name="body">The body of the message to send.</param>
/// <returns>A task representing the asynchronous operation.</returns>
private async Task SendMessage(string body)
{
if (string.IsNullOrEmpty(Url))
@@ -360,6 +459,12 @@ public class LightBeaconService : ILightBeaconService
}
}
/// <summary>
/// Retrieves a light beacon device instance based on the specified light beacon ID.
/// The method queries the repository for the light beacon configuration, checks if the necessary options are present, and creates an instance of the appropriate light beacon device class based on the type specified in the configuration.
/// </summary>
/// <param name="lightBeaconId">The ID of the light beacon to retrieve.</param>
/// <returns>A task representing the asynchronous operation, with a result of the light beacon device instance if found; otherwise, null.</returns>
private async Task<LightBeaconDevice?> GetBeacon(ObjectId lightBeaconId)
{
var cfg = await _lightBeaconRepository.GetById(lightBeaconId);
@@ -381,6 +486,11 @@ public class LightBeaconService : ILightBeaconService
return null;
}
/// <summary>
/// Converts the contents of a concurrent dictionary mapping point of care IDs to their associated light beacon colors into a string representation for logging purposes.
/// </summary>
/// <param name="dictionary"></param>
/// <returns></returns>
public static string DictionaryToString(ConcurrentDictionary<ObjectId, LightBeaconColor> dictionary)
{
var builder = new StringBuilder();