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
@@ -9,6 +9,9 @@ using Newtonsoft.Json;
namespace adas_core.Infrastructure.Services;
/// <summary>
/// Represents a service that implements the <see cref="IPublisherService"/> contract, providing the concrete implementation of the publishing operations defined by the interface.
/// </summary>
public class PublisherService : IPublisherService
{
private readonly ILogger<PublisherService> _logger;
@@ -64,63 +67,81 @@ public class PublisherService : IPublisherService
return Task.FromResult(true);
}
/// <summary>
/// Sends a message to the specified queue using the configured bus. If the bus is not initialized or an error occurs, the operation is logged and <c>false</c> is returned.
/// </summary>
/// <param name="msg">The message payload to send to the queue.</param>
/// <param name="queueName">The name of the target queue to which the message will be sent.</param>
/// <returns><c>true</c> if the message was sent successfully; otherwise, <c>false</c>.</returns>
public async Task<bool> SendMessage(string msg, string queueName)
{
try
{
if (_bus == null)
try
{
_logger.LogError("Bus is null");
if (_bus == null)
{
_logger.LogError("Bus is null");
return false;
}
await CreateQueue(queueName);
_logger.LogDebug("Sending message to {queueName}", queueName);
await _bus.SendReceive.SendAsync(queueName, msg);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending message to {queueName}", queueName);
return false;
}
await CreateQueue(queueName);
_logger.LogDebug("Sending message to {queueName}", queueName);
await _bus.SendReceive.SendAsync(queueName, msg);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending message to {queueName}", queueName);
return false;
}
}
/// <summary>
/// Serializes the specified object to JSON and asynchronously sends it to the named queue.
/// </summary>
/// <param name="obj">The object to serialize and send to the queue.</param>
/// <param name="queueName">The name of the destination queue.</param>
/// <returns>A task that resolves to <c>true</c> if the message was sent successfully; otherwise, <c>false</c>.</returns>
public async Task<bool> SendMessage(object obj, string queueName)
{
var json = JsonConvert.SerializeObject(obj);
return await SendMessage(json, queueName);
}
public async Task<bool> SendMessageError(object obj, string queueName)
{
try
{
if (_bus == null)
var json = JsonConvert.SerializeObject(obj);
return await SendMessage(json, queueName);
}
/// <summary>
/// Sends an error message to the specified queue. Validates that the bus instance is available and that the supplied object is a <c>Message&lt;Error&gt;</c>; returns <c>false</c> when either validation fails or when the send operation throws an exception.
/// </summary>
/// <param name="obj">The message object expected to be a <c>Message&lt;Error&gt;</c>. If it is not, the method returns <c>false</c> without sending anything.</param>
/// <param name="queueName">The name of the queue to which the error message body will be sent. The queue is created if it does not already exist.</param>
/// <returns>A <see cref="Task{Boolean}"/> that resolves to <c>true</c> when the error message is successfully sent, and <c>false</c> when the bus is null, the object is not a <c>Message&lt;Error&gt;</c>, or an exception is raised while sending.</returns>
public async Task<bool> SendMessageError(object obj, string queueName)
{
try
{
_logger.LogError("Bus is null - cannot send error message");
if (_bus == null)
{
_logger.LogError("Bus is null - cannot send error message");
return false;
}
if (obj is not Message<Error> errorMessage)
return false;
await CreateQueue(queueName);
_logger.LogDebug("Sending error message to {queueName}", queueName);
// enviamos solo el Error (no Message<Error>)
await _bus.SendReceive.SendAsync(queueName, errorMessage.Body);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending error message");
return false;
}
if (obj is not Message<Error> errorMessage)
return false;
await CreateQueue(queueName);
_logger.LogDebug("Sending error message to {queueName}", queueName);
// enviamos solo el Error (no Message<Error>)
await _bus.SendReceive.SendAsync(queueName, errorMessage.Body);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error sending error message");
return false;
}
}
}
@@ -10,6 +10,9 @@ using Newtonsoft.Json;
namespace adas_core.Infrastructure.Services;
/// <summary>
/// Provides functionality for receiving and processing incoming data, messages, or requests.
/// </summary>
public class ReceiverService
{
private readonly ILogger<ReceiverService> _logger;
@@ -63,65 +66,78 @@ public class ReceiverService
}
}
/// <summary>
/// Registers all application queues defined in settings, including queues for observations, treatments, patients, pumps, appointments, recordings, recording alerts, and alarm observations, by adding each one through the <c>AddQueue</c> call.
/// </summary>
private void SetQueues()
{
AddQueue(_settings.ObservationsQueue);
AddQueue(_settings.TreatmentsQueue);
AddQueue(_settings.PatientsQueue);
AddQueue(_settings.PumpsQueue);
AddQueue(_settings.AppointmentsQueue);
AddQueue(_settings.RecordingQueue);
AddQueue(_settings.RecordingAlertQueue);
AddQueue(_settings.AlarmObservationQueue);
}
{
AddQueue(_settings.ObservationsQueue);
AddQueue(_settings.TreatmentsQueue);
AddQueue(_settings.PatientsQueue);
AddQueue(_settings.PumpsQueue);
AddQueue(_settings.AppointmentsQueue);
AddQueue(_settings.RecordingQueue);
AddQueue(_settings.RecordingAlertQueue);
AddQueue(_settings.AlarmObservationQueue);
}
/// <summary>
/// Adds a queue to the internal collection only when the supplied value is not null, empty, or whitespace; otherwise the call is ignored.
/// </summary>
/// <param name="queue">The queue identifier to add to the collection. Null, empty, or whitespace values are silently skipped.</param>
private void AddQueue(string? queue)
{
if (!string.IsNullOrWhiteSpace(queue))
_queues.Add(queue!);
}
{
if (!string.IsNullOrWhiteSpace(queue))
_queues.Add(queue!);
}
/// <summary>
/// Attempts to establish a connection to RabbitMQ by disposing any existing bus, configuring EasyNetQ with the configured connection string, and registering consumers for all configured queues. If the connection fails, the failure is logged and a timer is scheduled to retry the connection after a five-second delay, recursively invoking the method when the timer elapses.
/// </summary>
private void TryToConnect()
{
try
{
DisposeBus();
var services = new ServiceCollection();
services.AddEasyNetQ(_settings.ConnectionString);
var provider = services.BuildServiceProvider();
_bus = provider.GetRequiredService<IBus>();
foreach (var queue in _queues)
try
{
RegisterConsumer(queue);
DisposeBus();
var services = new ServiceCollection();
services.AddEasyNetQ(_settings.ConnectionString);
var provider = services.BuildServiceProvider();
_bus = provider.GetRequiredService<IBus>();
foreach (var queue in _queues)
{
RegisterConsumer(queue);
}
_logger.LogInformation("RabbitMQ connected. Registered {Count} queues", _queues.Count);
}
_logger.LogInformation("RabbitMQ connected. Registered {Count} queues", _queues.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "RabbitMQ connection failed. Retrying in 5 seconds...");
var timer = new System.Timers.Timer(5000);
timer.Elapsed += (_, _) =>
catch (Exception ex)
{
timer.Stop();
TryToConnect();
};
timer.Start();
_logger.LogError(ex, "RabbitMQ connection failed. Retrying in 5 seconds...");
var timer = new System.Timers.Timer(5000);
timer.Elapsed += (_, _) =>
{
timer.Stop();
TryToConnect();
};
timer.Start();
}
}
}
/// <summary>
/// Disposes the underlying bus instance if it implements <see cref="IDisposable"/>.
/// </summary>
private void DisposeBus()
{
if (_bus is IDisposable disposable)
{
disposable.Dispose();
if (_bus is IDisposable disposable)
{
disposable.Dispose();
}
}
}
private void RegisterConsumer(string queue)
{
@@ -154,58 +170,67 @@ public class ReceiverService
});
}
/// <summary>
/// Resolves and returns the <see cref="IApiRequestService"/> instance associated with the specified queue name by matching it against the configured queue settings for observations, treatments, patients, pumps, recordings, recording alerts, alarm observations, and appointments.
/// </summary>
/// <param name="queue">The queue name to resolve to its corresponding API request service.</param>
/// <returns>The matching <see cref="IApiRequestService"/> instance if the queue is recognized; otherwise, <c>null</c>.</returns>
private IApiRequestService? ResolveService(string queue)
{
return queue switch
{
var q when q == _settings.ObservationsQueue => _observationService,
var q when q == _settings.TreatmentsQueue => _treatmentService,
var q when q == _settings.PatientsQueue => _patientsService,
var q when q == _settings.PumpsQueue => _pumpService,
var q when q == _settings.RecordingQueue => _recordingService,
var q when q == _settings.RecordingAlertQueue => _recordingAlertService,
var q when q == _settings.AlarmObservationQueue => _alarmService,
var q when q == _settings.AppointmentsQueue => _appointmentService,
_ => null
};
}
public async Task ProcessErrorQueue(string errorQueueName)
{
if (_bus == null)
{
_logger.LogError("Cannot process error queue - bus is null");
return;
return queue switch
{
var q when q == _settings.ObservationsQueue => _observationService,
var q when q == _settings.TreatmentsQueue => _treatmentService,
var q when q == _settings.PatientsQueue => _patientsService,
var q when q == _settings.PumpsQueue => _pumpService,
var q when q == _settings.RecordingQueue => _recordingService,
var q when q == _settings.RecordingAlertQueue => _recordingAlertService,
var q when q == _settings.AlarmObservationQueue => _alarmService,
var q when q == _settings.AppointmentsQueue => _appointmentService,
_ => null
};
}
await _bus.SendReceive.ReceiveAsync<Error>(errorQueueName, async err =>
/// <summary>
/// Asynchronously reprocesses messages from the specified error queue by deriving the target queue name from the routing key (with "Key" removed), resolving the associated service, and resubmitting the message. Operations are skipped when the bus is uninitialized, the derived queue name is empty, or no service is resolved.
/// </summary>
/// <param name="errorQueueName">The name of the error queue from which to reprocess messages.</param>
public async Task ProcessErrorQueue(string errorQueueName)
{
try
if (_bus == null)
{
var queue = err.RoutingKey?.Replace("Key", "");
if (string.IsNullOrEmpty(queue))
return;
var service = ResolveService(queue);
if (service == null)
return;
var message = new Message<string>(
err.Message,
new MessageProperties()
);
await Task.Run(() => TryToParseAndSend(message, service));
_logger.LogError("Cannot process error queue - bus is null");
return;
}
catch (Exception ex)
await _bus.SendReceive.ReceiveAsync<Error>(errorQueueName, async err =>
{
_logger.LogError(ex, "Error reprocessing message from {queue}", errorQueueName);
throw;
}
});
}
try
{
var queue = err.RoutingKey?.Replace("Key", "");
if (string.IsNullOrEmpty(queue))
return;
var service = ResolveService(queue);
if (service == null)
return;
var message = new Message<string>(
err.Message,
new MessageProperties()
);
await Task.Run(() => TryToParseAndSend(message, service));
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reprocessing message from {queue}", errorQueueName);
throw;
}
});
}
/// <summary>
/// Sends EasyNetQ messages to a service implementing IApiRequestService
+346 -258
View File
@@ -20,6 +20,14 @@ using Quartz.Util;
namespace adas_core.Infrastructure.Services;
/// <summary>
/// Provides a concrete implementation of the <see cref="IRelayService"/> contract,
/// delivering relay-related functionality to consumers of the service.
/// </summary>
/// <remarks>
/// This class is the default implementation of <see cref="IRelayService"/>,
/// and can be substituted via dependency injection where the interface is required.
/// </remarks>
public class RelayService : IRelayService
{
private readonly List<RelayDevice> _devices = [];
@@ -50,22 +58,84 @@ public class RelayService : IRelayService
Task.Run(async () => await InitRelayWithStatus());
}
/// <summary>
/// Asynchronously determines the current status of a relay, preferring a cached value when available and falling back to a direct device read or an external endpoint when the device is unreachable.
/// Returns the cached status if caching is enabled, the cached value is present and not <see cref="RelayEnum.Status.NotInitialized"/>; otherwise queries the relay device, or invokes the configured URL endpoint if the device cannot be obtained.
/// On failure, logs the error and resolves the status to <see cref="RelayEnum.Status.Unknown"/>.
/// </summary>
/// <param name="relay">The relay to check, identified by its IP, port, and relay number used both as the cache key and as the target of the status request.</param>
/// <returns>A task that yields the resolved <see cref="RelayEnum.Status"/> of the relay, or <see cref="RelayEnum.Status.Unknown"/> when the status cannot be determined.</returns>
public async Task<RelayEnum.Status> CheckRelayStatus(Relay relay)
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
try
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
try
{
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
if (_relayWithStatus.TryGetValue(cacheKey, out var status) &&
status != RelayEnum.Status.NotInitialized)
return status;
_logger.LogDebug("Relay status not found in cache: {Dct}",
DictionaryToString(_relayWithStatus));
}
RelayDevice? relayDevice = null;
try
{
relayDevice = GetRelayDevice(relay);
}
catch (Exception e)
{
_logger.LogWarning("Error getting relay device: {EMessage} {EStackTrace}", e.Message, e.StackTrace);
}
if (relayDevice == null && !string.IsNullOrEmpty(_url)) return await GetRelayStatusByOr(relay);
if (relayDevice != null) return relayDevice.GetStatusRelay(relay.RelayNumber);
}
catch (Exception e)
{
_logger.LogError("Error CheckRelayStatus relay {Relay}: {EMessage} {EStackTrace}", relay, e.Message,
e.StackTrace);
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = RelayEnum.Status.Unknown;
}
}
return RelayEnum.Status.Unknown;
}
/// <summary>
/// Checks the status of a relay identified by its unique ID. Returns <see cref="RelayEnum.Status.Unknown"/> when the relay cannot be found, otherwise delegates to the relay-based overload to resolve the current status.
/// </summary>
/// <param name="relayId">The unique identifier of the relay whose status should be checked.</param>
/// <returns>The current <see cref="RelayEnum.Status"/> of the relay, or <see cref="RelayEnum.Status.Unknown"/> if no relay with the given ID exists.</returns>
public async Task<RelayEnum.Status> CheckRelayStatus(ObjectId relayId)
{
var relay = await _relayRepository.GetById(relayId);
if(relay == null) return RelayEnum.Status.Unknown;
return await CheckRelayStatus(relay);
}
/// <summary>
/// Asynchronously powers off the specified relay. Uses a cache to avoid redundant operations when the relay is already off, falls back to an HTTP request when no local relay device is available, and updates the cache after a successful power off.
/// </summary>
/// <param name="relay">The relay to power off, including its network address and relay number.</param>
public async Task PowerOff(Relay relay)
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
if (_relayWithStatus.TryGetValue(cacheKey, out var status) &&
status != RelayEnum.Status.NotInitialized)
return status;
_logger.LogDebug("Relay status not found in cache: {Dct}",
DictionaryToString(_relayWithStatus));
if (_relayWithStatus.Any() &&
_relayWithStatus[cacheKey] == RelayEnum.Status.Off)
return;
}
RelayDevice? relayDevice = null;
try
{
@@ -75,156 +145,137 @@ public class RelayService : IRelayService
{
_logger.LogWarning("Error getting relay device: {EMessage} {EStackTrace}", e.Message, e.StackTrace);
}
if (relayDevice == null && !string.IsNullOrEmpty(_url)) return await GetRelayStatusByOr(relay);
if (relayDevice != null) return relayDevice.GetStatusRelay(relay.RelayNumber);
}
catch (Exception e)
{
_logger.LogError("Error CheckRelayStatus relay {Relay}: {EMessage} {EStackTrace}", relay, e.Message,
e.StackTrace);
lock (_relayWithStatus)
if (relayDevice == null && !string.IsNullOrEmpty(_url))
{
_relayWithStatus[cacheKey] = RelayEnum.Status.Unknown;
_builder = new UriBuilder(_url)
{
Path = $"Relay/{relay.RelayNumber}/powerOff"
};
_logger.LogDebug("Send PowerOff relay {Relay}", relay);
await PowerRelay(relay, _builder);
}
}
return RelayEnum.Status.Unknown;
}
public async Task<RelayEnum.Status> CheckRelayStatus(ObjectId relayId)
{
var relay = await _relayRepository.GetById(relayId);
if(relay == null) return RelayEnum.Status.Unknown;
return await CheckRelayStatus(relay);
}
public async Task PowerOff(Relay relay)
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
if (_relayWithStatus.Any() &&
_relayWithStatus[cacheKey] == RelayEnum.Status.Off)
return;
}
RelayDevice? relayDevice = null;
try
{
relayDevice = GetRelayDevice(relay);
}
catch (Exception e)
{
_logger.LogWarning("Error getting relay device: {EMessage} {EStackTrace}", e.Message, e.StackTrace);
}
if (relayDevice == null && !string.IsNullOrEmpty(_url))
{
_builder = new UriBuilder(_url)
{
Path = $"Relay/{relay.RelayNumber}/powerOff"
};
_logger.LogDebug("Send PowerOff relay {Relay}", relay);
await PowerRelay(relay, _builder);
}
relayDevice?.PowerOffRelay(relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = RelayEnum.Status.Off;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
}
public async Task PowerOn(Relay relay)
{
if (string.IsNullOrEmpty(relay.Driver) || string.IsNullOrEmpty(relay.Ip)) return;
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
relayDevice?.PowerOffRelay(relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
if (_relayWithStatus.Any() &&
_relayWithStatus[cacheKey] == RelayEnum.Status.On)
return;
_relayWithStatus[cacheKey] = RelayEnum.Status.Off;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
RelayDevice? relayDevice = null;
try
{
relayDevice = GetRelayDevice(relay);
}
catch (Exception e)
{
_logger.LogWarning("Error getting relay device: {EMessage} {EStackTrace}", e.Message, e.StackTrace);
}
if (relayDevice == null && !string.IsNullOrEmpty(_url))
/// <summary>
/// Powers on the specified relay. If the relay is configured with a URL, sends a remote power-on request; otherwise, drives the relay device directly. Honors a caching layer to avoid redundant power-on commands when the relay is already reported as on.
/// </summary>
/// <param name="relay">The relay to power on, including driver, IP, port, relay number, and cache settings.</param>
public async Task PowerOn(Relay relay)
{
_builder = new UriBuilder(_url)
if (string.IsNullOrEmpty(relay.Driver) || string.IsNullOrEmpty(relay.Ip)) return;
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
if (_relayWithStatus.Any() &&
_relayWithStatus[cacheKey] == RelayEnum.Status.On)
return;
}
RelayDevice? relayDevice = null;
try
{
Path = $"Relay/{relay.RelayNumber}/powerOn"
};
_logger.LogDebug("Send PowerOn relay {Relay}", relay);
await PowerRelay(relay, _builder);
}
relayDevice?.PowerOnRelay(relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = RelayEnum.Status.On;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
relayDevice = GetRelayDevice(relay);
}
}
catch (Exception e)
{
_logger.LogWarning("Error getting relay device: {EMessage} {EStackTrace}", e.Message, e.StackTrace);
}
if (relayDevice == null && !string.IsNullOrEmpty(_url))
{
_builder = new UriBuilder(_url)
{
Path = $"Relay/{relay.RelayNumber}/powerOn"
};
_logger.LogDebug("Send PowerOn relay {Relay}", relay);
await PowerRelay(relay, _builder);
}
relayDevice?.PowerOnRelay(relay.RelayNumber);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = RelayEnum.Status.On;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
}
/// <summary>
/// Sets the manual relay status for a point of care device. If the point of care exists and has a configured relay list, the relay matching the specified type is updated with the new status; otherwise, the operation is silently skipped.
/// </summary>
/// <param name="status">The manual relay status to apply.</param>
/// <param name="pocId">The identifier of the point of care device whose relay configuration should be updated.</param>
/// <param name="type">The relay type used to look up the target relay within the point of care's relay configuration.</param>
public async Task SetManualRelay(RelayEnum.Status status, ObjectId pocId, RelayEnum.Type type)
{
try
{
var poc = await _pointOfCareService.FindById(pocId);
if (poc is { Configuration.RelayIdList: not null })
try
{
var relay = _relayRepository.GetRelayByTypeInList(poc.Configuration.RelayIdList, type).FirstOrDefault();
if (relay != null) relay.ManualRelayStatus = status;
await _pointOfCareService.UpdateRelayConfig(poc);
_logger.LogDebug("SetManualRelay: {Status}, pocId: {Location}, bed: {Bed}", status, poc.Id, poc.Bed);
var poc = await _pointOfCareService.FindById(pocId);
if (poc is { Configuration.RelayIdList: not null })
{
var relay = _relayRepository.GetRelayByTypeInList(poc.Configuration.RelayIdList, type).FirstOrDefault();
if (relay != null) relay.ManualRelayStatus = status;
await _pointOfCareService.UpdateRelayConfig(poc);
_logger.LogDebug("SetManualRelay: {Status}, pocId: {Location}, bed: {Bed}", status, poc.Id, poc.Bed);
}
}
catch (Exception ex)
{
_logger.LogError("Error settings manual relay. Exception: {ex}", ex);
throw;
}
}
catch (Exception ex)
{
_logger.LogError("Error settings manual relay. Exception: {ex}", ex);
throw;
}
}
/// <summary>
/// Retrieves a relay entity by its unique identifier from the repository.
/// Returns <c>null</c> when no matching relay is found.
/// </summary>
/// <param name="relay">The unique identifier of the relay to look up.</param>
/// <returns>A task that resolves to the <see cref="Relay"/> if found, or <c>null</c> if no relay matches the supplied identifier.</returns>
public Task<Relay?> GetById(ObjectId relay)
{
return _relayRepository.GetById(relay);
}
{
return _relayRepository.GetById(relay);
}
/// <summary>
/// Retrieves a list of relays matching the specified list of relay identifiers. Returns an empty list when the provided identifier list is null.
/// </summary>
/// <param name="relayList">The list of relay identifiers to look up. Can be null.</param>
/// <returns>A list of <see cref="Relay"/> objects corresponding to the provided identifiers, or an empty list if the input is null.</returns>
public List<Relay> GetRelayInList(List<ObjectId>? relayList)
{
if(relayList == null) return new List<Relay>();
return _relayRepository.GetRelayInList(relayList);
}
{
if(relayList == null) return new List<Relay>();
return _relayRepository.GetRelayInList(relayList);
}
/// <summary>
/// Retrieves the relays of a specified type from the provided list of configuration relay identifiers. Returns an empty list when the input list is <c>null</c>, otherwise delegates the lookup to the relay repository.
/// </summary>
/// <param name="configurationRelayList">The optional list of <see cref="ObjectId"/> values identifying the configuration relays to filter; when <c>null</c>, the method short-circuits and returns an empty list.</param>
/// <param name="type">The relay type used to filter the matching relays within the supplied list.</param>
/// <returns>A <see cref="List{Relay}"/> containing the relays matching the specified <paramref name="type"/>, or an empty list if <paramref name="configurationRelayList"/> is <c>null</c>.</returns>
public List<Relay> GetRelayByTypeInList(List<ObjectId>? configurationRelayList, RelayEnum.Type type)
{
if(configurationRelayList == null) return new List<Relay>();
return _relayRepository.GetRelayByTypeInList(configurationRelayList, type);
}
{
if(configurationRelayList == null) return new List<Relay>();
return _relayRepository.GetRelayByTypeInList(configurationRelayList, type);
}
public async Task<PaginationResponse<Relay>> GetPaginatedRelays(PaginationFilter filter)
{
@@ -266,17 +317,29 @@ public class RelayService : IRelayService
return new PaginationResponse<Relay>(data, filter.PageNumber, filter.PageSize, count);
}
/// <summary>
/// Inserts a new relay into the repository after verifying that no relay with the same name already exists.
/// </summary>
/// <param name="request">The relay entity to insert, whose name is checked for duplicates prior to persistence.</param>
/// <returns>The inserted <see cref="Relay"/> entity returned by the repository, or <c>null</c> if the repository yields no result.</returns>
/// <exception cref="Exception">Thrown when a relay with the same name as <paramref name="request"/> already exists in the repository.</exception>
public async Task<Relay?> InsertRelay(Relay request)
{
var relayExist = await _relayRepository.GetByName(request.RelayName);
if(relayExist != null) throw new Exception($"Relay with name {request.RelayName} already exists");
return await _relayRepository.InsertOneRelayAsync(request);
}
{
var relayExist = await _relayRepository.GetByName(request.RelayName);
if(relayExist != null) throw new Exception($"Relay with name {request.RelayName} already exists");
return await _relayRepository.InsertOneRelayAsync(request);
}
/// <summary>
/// Updates an existing relay identified by its unique ObjectId by delegating to the relay repository.
/// </summary>
/// <param name="objectId">The unique identifier of the relay to update.</param>
/// <param name="relay">The relay object containing the updated information.</param>
/// <returns>A task representing the asynchronous operation, containing the updated <see cref="Relay"/>, or <c>null</c> if no relay with the specified identifier was found.</returns>
public Task<Relay?> UpdateRelayById(ObjectId objectId, Relay relay)
{
return _relayRepository.UpdateRelayAsync(objectId, relay);
}
{
return _relayRepository.UpdateRelayAsync(objectId, relay);
}
public event EventHandler<Tuple<RelayDevice, int>>? RelayStatusChanged;
@@ -325,135 +388,160 @@ public class RelayService : IRelayService
}
}
/// <summary>
/// Retrieves a <see cref="RelayDevice"/> for the given relay, returning a cached instance when available
/// or dynamically creating and registering a new one based on the relay's driver type. Returns <c>null</c>
/// when the relay has no IP address or its port is zero.
/// </summary>
/// <param name="relay">The relay configuration used to locate an existing device or instantiate a new one.</param>
/// <returns>The existing or newly created <see cref="RelayDevice"/>, or <c>null</c> if the relay is missing a valid IP or port.</returns>
/// <exception cref="AdasException">Thrown when no constructor matching <see cref="Relay"/> and <see cref="RelaySettings"/> is found on the resolved driver type.</exception>
/// <exception cref="AdasException">Thrown when the resolved driver type cannot be instantiated into a <see cref="RelayDevice"/>.</exception>
private RelayDevice? GetRelayDevice(Relay relay)
{
if(relay.Ip.IsNullOrWhiteSpace() || relay.Port == 0) return null;
var device = _devices.FirstOrDefault(d => d.Relay.Ip == relay.Ip && d.Relay.Port == relay.Port);
if (device != null) return device;
var type = TypesUtils.GetDriver("Relay", relay.Driver ?? string.Empty);
var constructor = type.GetConstructor([typeof(Relay), typeof(RelaySettings)]);
if (constructor == null) throw new AdasException("Constructor not found for relay device");
var relayDevice = (RelayDevice)constructor.Invoke([relay, _relaySettings]);
if (relayDevice == null) throw new AdasException("Relay device not found");
relayDevice.RelayStatusChanged += (_, outletId) =>
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, outletId);
var value = relayDevice.GetStatusRelay(outletId);
lock (_relayWithStatus)
if(relay.Ip.IsNullOrWhiteSpace() || relay.Port == 0) return null;
var device = _devices.FirstOrDefault(d => d.Relay.Ip == relay.Ip && d.Relay.Port == relay.Port);
if (device != null) return device;
var type = TypesUtils.GetDriver("Relay", relay.Driver ?? string.Empty);
var constructor = type.GetConstructor([typeof(Relay), typeof(RelaySettings)]);
if (constructor == null) throw new AdasException("Constructor not found for relay device");
var relayDevice = (RelayDevice)constructor.Invoke([relay, _relaySettings]);
if (relayDevice == null) throw new AdasException("Relay device not found");
relayDevice.RelayStatusChanged += (_, outletId) =>
{
_relayWithStatus[cacheKey] = value;
}
_logger.LogDebug("Relay status changed for {Key} with value {Value}", cacheKey, value);
RelayStatusChanged?.Invoke(this, Tuple.Create(relayDevice, outletId));
};
_devices.Add(relayDevice);
return relayDevice;
}
var cacheKey = Tuple.Create(relay.Ip, relay.Port, outletId);
var value = relayDevice.GetStatusRelay(outletId);
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = value;
}
_logger.LogDebug("Relay status changed for {Key} with value {Value}", cacheKey, value);
RelayStatusChanged?.Invoke(this, Tuple.Create(relayDevice, outletId));
};
_devices.Add(relayDevice);
return relayDevice;
}
/// <summary>
/// Sends a power relay request by appending relay configuration details (driver, host, port, name, total count, and credentials) as query parameters to the provided URI and issuing an HTTP POST. Logs a debug message when the response status is not OK, and logs and swallows any exception encountered during the request.
/// </summary>
/// <param name="relay">The relay whose driver, IP, port, name, total count, username, and password are included in the request query string.</param>
/// <param name="builder">The URI builder whose query is updated with the relay parameters and whose resulting URI is used as the request target.</param>
private async Task PowerRelay(Relay relay, UriBuilder builder)
{
var query = HttpUtility.ParseQueryString(builder.Query);
query["driver"] = relay.Driver;
query["host"] = relay.Ip;
query["port"] = relay.Port.ToString();
query["relayName"] = relay.RelayName;
query["relays"] = relay.Total.ToString();
query["username"] = relay.Username;
query["password"] = relay.Password;
builder.Query = query.ToString();
HttpRequestMessage request = new()
{
RequestUri = builder.Uri,
Method = HttpMethod.Post
};
try
{
using var client = _httpClientFactory.CreateClient();
var httpResponse = await client.SendAsync(request);
if (!httpResponse.StatusCode.Equals(HttpStatusCode.OK))
_logger.LogDebug("Send PowerRelay relay {Relay}", relay);
var query = HttpUtility.ParseQueryString(builder.Query);
query["driver"] = relay.Driver;
query["host"] = relay.Ip;
query["port"] = relay.Port.ToString();
query["relayName"] = relay.RelayName;
query["relays"] = relay.Total.ToString();
query["username"] = relay.Username;
query["password"] = relay.Password;
builder.Query = query.ToString();
HttpRequestMessage request = new()
{
RequestUri = builder.Uri,
Method = HttpMethod.Post
};
try
{
using var client = _httpClientFactory.CreateClient();
var httpResponse = await client.SendAsync(request);
if (!httpResponse.StatusCode.Equals(HttpStatusCode.OK))
_logger.LogDebug("Send PowerRelay relay {Relay}", relay);
}
catch (Exception e)
{
_logger.LogError("Error PowerRelay relay {Relay}: {EMessage} {EStackTrace}", relay, e.Message,
e.StackTrace);
}
}
catch (Exception e)
{
_logger.LogError("Error PowerRelay relay {Relay}: {EMessage} {EStackTrace}", relay, e.Message,
e.StackTrace);
}
}
/// <summary>
/// Converts a concurrent dictionary of relay statuses into a human-readable string representation, formatting each entry as a line containing the device ID, port, and current status.
/// </summary>
/// <param name="dictionary">The concurrent dictionary containing relay status entries keyed by a tuple of device ID, port, and an additional integer value.</param>
/// <returns>A string containing one formatted line per dictionary entry describing the device ID, port, and status.</returns>
private static string DictionaryToString(ConcurrentDictionary<Tuple<string, int, int>, RelayEnum.Status> dictionary)
{
var builder = new StringBuilder();
foreach (var pair in dictionary)
builder.AppendLine($"Device ID: {pair.Key.Item1}, Port: {pair.Key.Item2}, Status: {pair.Value}");
return builder.ToString();
}
{
var builder = new StringBuilder();
foreach (var pair in dictionary)
builder.AppendLine($"Device ID: {pair.Key.Item1}, Port: {pair.Key.Item2}, Status: {pair.Value}");
return builder.ToString();
}
/// <summary>
/// Retrieves the current status of the specified relay by querying its external service endpoint, and caches the result (success or failure) when caching is enabled for both the application and the relay.
/// Falls back to <see cref="RelayEnum.Status.Unknown"/> when the HTTP response is not successful.
/// </summary>
/// <param name="relay">The relay instance whose status should be queried; its IP, port, relay number, driver, credentials, and mode are used to build the request.</param>
/// <returns>The parsed <see cref="RelayEnum.Status"/> returned by the remote relay service, or <see cref="RelayEnum.Status.Unknown"/> if the request fails.</returns>
private async Task<RelayEnum.Status> GetRelayStatusByOr(Relay relay)
{
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
_builder = new UriBuilder(_url)
{
Path = $"Relay/{relay.RelayNumber}/status"
};
var query = HttpUtility.ParseQueryString(_builder.Query);
query["driver"] = relay.Driver;
query["host"] = relay.Ip;
query["port"] = relay.Port.ToString();
query["relayName"] = relay.RelayName;
query["relays"] = relay.Total.ToString();
query["username"] = relay.Username;
query["password"] = relay.Password;
query["mode"] = relay.Mode.ToString();
query["refreshTime"] = "0";
_builder.Query = query.ToString();
HttpRequestMessage request = new()
{
RequestUri = _builder.Uri,
Method = HttpMethod.Get
};
using var client = _httpClientFactory.CreateClient();
client.Timeout = new TimeSpan(0, 0, 3);
var httpResponse = await client.SendAsync(request);
if (httpResponse.StatusCode.Equals(HttpStatusCode.OK))
{
var responseContent = httpResponse.Content;
var response = await responseContent.ReadAsStringAsync();
response = response.Replace("\"", "");
var relayStatusParsed = (RelayEnum.Status)Enum.Parse(typeof(RelayEnum.Status), response);
var cacheKey = Tuple.Create(relay.Ip, relay.Port, relay.RelayNumber);
_builder = new UriBuilder(_url)
{
Path = $"Relay/{relay.RelayNumber}/status"
};
var query = HttpUtility.ParseQueryString(_builder.Query);
query["driver"] = relay.Driver;
query["host"] = relay.Ip;
query["port"] = relay.Port.ToString();
query["relayName"] = relay.RelayName;
query["relays"] = relay.Total.ToString();
query["username"] = relay.Username;
query["password"] = relay.Password;
query["mode"] = relay.Mode.ToString();
query["refreshTime"] = "0";
_builder.Query = query.ToString();
HttpRequestMessage request = new()
{
RequestUri = _builder.Uri,
Method = HttpMethod.Get
};
using var client = _httpClientFactory.CreateClient();
client.Timeout = new TimeSpan(0, 0, 3);
var httpResponse = await client.SendAsync(request);
if (httpResponse.StatusCode.Equals(HttpStatusCode.OK))
{
var responseContent = httpResponse.Content;
var response = await responseContent.ReadAsStringAsync();
response = response.Replace("\"", "");
var relayStatusParsed = (RelayEnum.Status)Enum.Parse(typeof(RelayEnum.Status), response);
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = relayStatusParsed;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
return relayStatusParsed;
}
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = relayStatusParsed;
_relayWithStatus[cacheKey] = RelayEnum.Status.Unknown;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
return relayStatusParsed;
return RelayEnum.Status.Unknown;
}
if (_relaySettings.Cache && relay.Cache)
lock (_relayWithStatus)
{
_relayWithStatus[cacheKey] = RelayEnum.Status.Unknown;
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
}
return RelayEnum.Status.Unknown;
}
}
@@ -34,249 +34,298 @@ public class SendAlertService(
// PlatformID.WinCE
//};
/// <summary>
/// Asynchronously retrieves the list of available queues from the Rabbit messaging system.
/// If the Rabbit connection string is not defined in the web config, an error is logged and an empty list is returned.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Queue"/> objects, or an empty list if the Rabbit connection string is not configured.</returns>
public async Task<List<Queue>> GetQueues()
{
if (_rabbitConnectionString != null) return await GetQueuesAsync();
logger.LogError("Rabbit connection string not defined in web config");
return [];
}
{
if (_rabbitConnectionString != null) return await GetQueuesAsync();
logger.LogError("Rabbit connection string not defined in web config");
return [];
}
/// <summary>
/// Retrieves performance data from hospitals, including CPU, RAM, and storage consumption metrics.
/// Logs and suppresses any errors encountered while collecting the data, returning the successfully gathered metrics.
/// </summary>
/// <returns>A list of <see cref="Performance"/> entries containing the collected performance metrics; returns an empty list if all collection attempts fail.</returns>
public List<Performance> GetPerformance()
{
logger.LogDebug("Starting check performance data from hospitals");
var list = new List<Performance>();
try
{
list.Add(GetConsumedCpu());
list.Add(GetConsumedRam());
list.AddRange(GetConsumedStorages());
}
catch (Exception e)
{
logger.LogError("Error check error performance data from hospitals: {eMessage} {eStackTrace}",
e.Message, e.StackTrace);
logger.LogDebug("Starting check performance data from hospitals");
var list = new List<Performance>();
try
{
list.Add(GetConsumedCpu());
list.Add(GetConsumedRam());
list.AddRange(GetConsumedStorages());
}
catch (Exception e)
{
logger.LogError("Error check error performance data from hospitals: {eMessage} {eStackTrace}",
e.Message, e.StackTrace);
}
return list;
}
return list;
}
/// <summary>
/// Asynchronously retrieves a list of API clients by delegating to the underlying data retrieval method.
/// </summary>
/// <returns>A task representing the asynchronous operation, containing the list of <see cref="ApiClients"/> instances retrieved.</returns>
public async Task<List<ApiClients>> GetApiClients()
{
return await GetApiClientsAsync();
}
{
return await GetApiClientsAsync();
}
/// <summary>
/// Asynchronously retrieves statistics for the configured RabbitMQ error queues (recording, patients, treatments, observations, and pumps). Only queues with non-empty names are queried, duplicates are ignored, and any exception raised while connecting or fetching stats is logged and results in an empty list being returned.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of <see cref="Queue"/> objects with the name, message count, and consumer count of each queried queue, or an empty list if an error occurs.</returns>
private async Task<List<Queue>> GetQueuesAsync()
{
logger.LogDebug("Starting check rabbitMQ data from hospitals");
var errorQueues = new List<Queue>();
var errorQueuesNames = new List<string>();
try
{
var services = new ServiceCollection();
services.AddEasyNetQ(_rabbitConnectionString);
var provider = services.BuildServiceProvider();
var bus = provider.GetRequiredService<IBus>();
var advanced = bus.Advanced;
if (!string.IsNullOrEmpty(_errorRecordingQueue))
errorQueuesNames.Add(_errorRecordingQueue);
if (!string.IsNullOrEmpty(_errorPatientsQueue))
errorQueuesNames.Add(_errorPatientsQueue);
if (!string.IsNullOrEmpty(_errorTreatmetsQueue))
errorQueuesNames.Add(_errorTreatmetsQueue);
if (!string.IsNullOrEmpty(_errorObservationsQueue))
errorQueuesNames.Add(_errorObservationsQueue);
if (!string.IsNullOrEmpty(_errorPumpsQueue))
errorQueuesNames.Add(_errorPumpsQueue);
foreach (var errorQueueName in errorQueuesNames.Distinct())
logger.LogDebug("Starting check rabbitMQ data from hospitals");
var errorQueues = new List<Queue>();
var errorQueuesNames = new List<string>();
try
{
var stats = await advanced.GetQueueStatsAsync(errorQueueName);
errorQueues.Add(new Queue
var services = new ServiceCollection();
services.AddEasyNetQ(_rabbitConnectionString);
var provider = services.BuildServiceProvider();
var bus = provider.GetRequiredService<IBus>();
var advanced = bus.Advanced;
if (!string.IsNullOrEmpty(_errorRecordingQueue))
errorQueuesNames.Add(_errorRecordingQueue);
if (!string.IsNullOrEmpty(_errorPatientsQueue))
errorQueuesNames.Add(_errorPatientsQueue);
if (!string.IsNullOrEmpty(_errorTreatmetsQueue))
errorQueuesNames.Add(_errorTreatmetsQueue);
if (!string.IsNullOrEmpty(_errorObservationsQueue))
errorQueuesNames.Add(_errorObservationsQueue);
if (!string.IsNullOrEmpty(_errorPumpsQueue))
errorQueuesNames.Add(_errorPumpsQueue);
foreach (var errorQueueName in errorQueuesNames.Distinct())
{
Name = errorQueueName,
Messages = stats.MessagesCount,
Consumers = stats.ConsumersCount
});
var stats = await advanced.GetQueueStatsAsync(errorQueueName);
errorQueues.Add(new Queue
{
Name = errorQueueName,
Messages = stats.MessagesCount,
Consumers = stats.ConsumersCount
});
}
}
}
catch (Exception e)
{
logger.LogError("Error checking RabbitMQ queues: {msg} {stack}",
e.Message, e.StackTrace);
catch (Exception e)
{
logger.LogError("Error checking RabbitMQ queues: {msg} {stack}",
e.Message, e.StackTrace);
}
return errorQueues;
}
return errorQueues;
}
/// <summary>
/// Retrieves the CPU consumption of the current process as a Performance measurement.
/// Calculates usage from total processor time divided by processor count, and returns a default Performance instance if the underlying process query fails.
/// </summary>
/// <returns>A Performance object describing the CPU usage percentage, total capacity, and unit. If an error occurs, a default Performance instance is returned and the exception is logged.</returns>
public Performance GetConsumedCpu()
{
Performance performance = new();
try
{
var currentProcess = Process.GetCurrentProcess();
var percentage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.ProcessorCount / 10;
performance = new Performance
Performance performance = new();
try
{
Name = "CPU",
PercentageConsumed = percentage,
ValueTotal = 100,
Unit = "%"
};
}
catch (Exception e)
{
logger.LogError("Error check CPU performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
var currentProcess = Process.GetCurrentProcess();
var percentage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.ProcessorCount / 10;
performance = new Performance
{
Name = "CPU",
PercentageConsumed = percentage,
ValueTotal = 100,
Unit = "%"
};
}
catch (Exception e)
{
logger.LogError("Error check CPU performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
}
return performance;
}
return performance;
}
/// <summary>
/// Retrieves the current RAM consumption in gigabytes, returning it as a Performance metric where the total and consumed values are reported as equal with a consumption percentage of 100. If an error occurs while retrieving the memory information, the exception is logged and an empty Performance object is returned.
/// </summary>
/// <returns>A Performance object representing the RAM usage in GB; returns an empty Performance object if the memory retrieval fails.</returns>
public Performance GetConsumedRam()
{
Performance performance = new();
try
{
var totalMemoryBytes = GC.GetTotalMemory(false);
var totalMemoryGb = Math.Round((double)totalMemoryBytes / 1024 / 1024 / 1024, 2);
performance = new Performance
Performance performance = new();
try
{
Name = "RAM",
ValueTotal = totalMemoryGb,
ValueConsumed = totalMemoryGb,
PercentageConsumed = 100,
Unit = "GB"
};
}
catch (Exception e)
{
logger.LogError("Error check RAM performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
var totalMemoryBytes = GC.GetTotalMemory(false);
var totalMemoryGb = Math.Round((double)totalMemoryBytes / 1024 / 1024 / 1024, 2);
performance = new Performance
{
Name = "RAM",
ValueTotal = totalMemoryGb,
ValueConsumed = totalMemoryGb,
PercentageConsumed = 100,
Unit = "GB"
};
}
catch (Exception e)
{
logger.LogError("Error check RAM performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
}
return performance;
}
return performance;
}
/// <summary>
/// Calculates the consumed storage for the specified drive, returning total and consumed values in gigabytes along with the consumption percentage. If the drive's total size is zero, the percentage is not computed and the total remains zero. Any errors encountered during calculation are logged and an empty <see cref="Performance"/> object is returned.
/// </summary>
/// <param name="drive">The drive whose storage consumption is to be measured.</param>
/// <returns>A <see cref="Performance"/> instance containing the consumed storage, total storage, percentage consumed, and the unit (GB) for the drive.</returns>
public Performance GetConsumedStorage(DriveInfo drive)
{
Performance performance = new();
try
{
double percentage = 0;
//Total storage
double valueTotal = drive.TotalSize;
//Consumed storage
var value = valueTotal - drive.AvailableFreeSpace;
if (valueTotal != 0)
Performance performance = new();
try
{
percentage = Math.Round(value * 100 / valueTotal, 2); //%
valueTotal = Math.Round(valueTotal / 1024 / 1024 / 1024, 2); //Bytes -> GB
double percentage = 0;
//Total storage
double valueTotal = drive.TotalSize;
//Consumed storage
var value = valueTotal - drive.AvailableFreeSpace;
if (valueTotal != 0)
{
percentage = Math.Round(value * 100 / valueTotal, 2); //%
valueTotal = Math.Round(valueTotal / 1024 / 1024 / 1024, 2); //Bytes -> GB
}
value = Math.Round(value / 1024 / 1024 / 1024, 2); //Bytes -> GB
performance = new Performance
{
Name = "STORAGE " + drive.Name,
ValueConsumed = value,
ValueTotal = valueTotal,
PercentageConsumed = percentage,
Unit = "GB"
};
}
value = Math.Round(value / 1024 / 1024 / 1024, 2); //Bytes -> GB
performance = new Performance
catch (Exception e)
{
Name = "STORAGE " + drive.Name,
ValueConsumed = value,
ValueTotal = valueTotal,
PercentageConsumed = percentage,
Unit = "GB"
};
}
catch (Exception e)
{
logger.LogError("Error check Storage performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
logger.LogError("Error check Storage performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
}
return performance;
}
return performance;
}
/// <summary>
/// Retrieves the consumed storage performance information for all ready drives on the system,
/// skipping any drive that is not ready and logging errors encountered while processing
/// individual drives or the overall enumeration without halting the operation.
/// </summary>
/// <returns>A list of <see cref="Performance"/> entries describing the consumed storage for each
/// successfully processed drive; returns an empty list if no drives yield results or if an
/// error occurs during enumeration.</returns>
private List<Performance> GetConsumedStorages()
{
var list = new List<Performance>();
try
{
foreach (var drive in DriveInfo.GetDrives())
var list = new List<Performance>();
try
{
if (!drive.IsReady) continue;
try
foreach (var drive in DriveInfo.GetDrives())
{
var performance = GetConsumedStorage(drive);
list.Add(performance);
}
catch (Exception e)
{
logger.LogError("Error check Storage drive {drive} in performance: {eMessage}", drive.Name,
e.Message);
if (!drive.IsReady) continue;
try
{
var performance = GetConsumedStorage(drive);
list.Add(performance);
}
catch (Exception e)
{
logger.LogError("Error check Storage drive {drive} in performance: {eMessage}", drive.Name,
e.Message);
}
}
}
}
catch (Exception e)
{
logger.LogError("Error check Storages performance: {eMessage}", e.Message);
catch (Exception e)
{
logger.LogError("Error check Storages performance: {eMessage}", e.Message);
}
return list;
}
return list;
}
/// <summary>
/// Asynchronously retrieves the list of connected API clients (WebSocket clients) from hospitals.
/// Returns an empty list if the underlying call returns null or fails; any exception is caught and logged without being rethrown.
/// </summary>
/// <returns>A task that resolves to the list of connected API clients, which may be empty if no clients were retrieved.</returns>
private async Task<List<ApiClients>> GetApiClientsAsync()
{
logger.LogDebug("Starting check conected clients from hospitals");
var clientsList = new List<ApiClients>();
try
{
var clients = await GetWebSocketClients();
if (clients != null)
clientsList.Add(clients);
}
catch (Exception e)
{
logger.LogError("Error check error conected clients from hospitals: {eMessage} {eStackTrace}",
e.Message, e.StackTrace);
logger.LogDebug("Starting check conected clients from hospitals");
var clientsList = new List<ApiClients>();
try
{
var clients = await GetWebSocketClients();
if (clients != null)
clientsList.Add(clients);
}
catch (Exception e)
{
logger.LogError("Error check error conected clients from hospitals: {eMessage} {eStackTrace}",
e.Message, e.StackTrace);
}
return clientsList;
}
return clientsList;
}
/// <summary>
/// Asynchronously retrieves the list of connected WebSocket API clients, handling any errors by logging them and returning an empty <see cref="ApiClients"/> instance as a fallback.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> that resolves to an <see cref="ApiClients"/> instance representing the connected WebSocket subscribers.</returns>
private Task<ApiClients?> GetWebSocketClients()
{
ApiClients apiClients = new();
try
{
//TODO new ws apiClients = defaultWebSocketHandler.GetSubscribersConected();
//apiClients = webSocketHandler.GetSubscribersConected();
ApiClients apiClients = new();
try
{
//TODO new ws apiClients = defaultWebSocketHandler.GetSubscribersConected();
//apiClients = webSocketHandler.GetSubscribersConected();
}
catch (Exception e)
{
logger.LogError("Error check WebSocket Clients: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
}
return Task.FromResult<ApiClients?>(apiClients);
}
catch (Exception e)
{
logger.LogError("Error check WebSocket Clients: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
}
return Task.FromResult<ApiClients?>(apiClients);
}
}