Creado apartir del commit b888de6c92056de294456f581a56e25e734d4ab7 de develop
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Domain.Models.AppSettings;
|
||||
using EasyNetQ;
|
||||
using EasyNetQ.SystemMessages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace adas_core.Infrastructure.Services;
|
||||
|
||||
public class PublisherService : IPublisherService
|
||||
{
|
||||
private readonly ILogger<PublisherService> _logger;
|
||||
private readonly HashSet<string> _queues = new();
|
||||
private readonly IBus? _bus;
|
||||
|
||||
public PublisherService(
|
||||
IOptions<RabbitMqSettings> rabbitMqSettings,
|
||||
ILogger<PublisherService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
try
|
||||
{
|
||||
var connection = rabbitMqSettings.Value.ConnectionString;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(connection))
|
||||
{
|
||||
_logger.LogError("Missing RabbitMQ connection string");
|
||||
return;
|
||||
}
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddEasyNetQ(connection);
|
||||
|
||||
var provider = services.BuildServiceProvider();
|
||||
_bus = provider.GetRequiredService<IBus>();
|
||||
|
||||
_logger.LogInformation("PublisherService initialized");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error initializing PublisherService");
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> CreateQueue(string queueName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queueName))
|
||||
return Task.FromResult(false);
|
||||
|
||||
if (_queues.Contains(queueName))
|
||||
{
|
||||
_logger.LogDebug("Queue {queueName} already registered", queueName);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
_queues.Add(queueName);
|
||||
|
||||
_logger.LogInformation("Queue registered: {queueName}", queueName);
|
||||
|
||||
// EasyNetQ crea colas automáticamente
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public async Task<bool> SendMessage(string msg, string queueName)
|
||||
{
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Domain.Models;
|
||||
using adas_core.Domain.Models.AppSettings;
|
||||
using EasyNetQ;
|
||||
using EasyNetQ.SystemMessages;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace adas_core.Infrastructure.Services;
|
||||
|
||||
public class ReceiverService
|
||||
{
|
||||
private readonly ILogger<ReceiverService> _logger;
|
||||
private readonly RabbitMqSettings _settings;
|
||||
|
||||
private readonly IObservationService _observationService;
|
||||
private readonly ITreatmentService _treatmentService;
|
||||
private readonly IPatientService _patientsService;
|
||||
private readonly IPumpService _pumpService;
|
||||
private readonly IRecordingAlertService _recordingAlertService;
|
||||
private readonly IRecordingService _recordingService;
|
||||
private readonly IAppointmentService _appointmentService;
|
||||
private readonly IAlarmService _alarmService;
|
||||
|
||||
private readonly List<string> _queues = [];
|
||||
|
||||
private IBus? _bus;
|
||||
|
||||
public ReceiverService(
|
||||
IObservationService observationService,
|
||||
ITreatmentService treatmentService,
|
||||
IPatientService patientsService,
|
||||
IPumpService pumpService,
|
||||
IRecordingAlertService recordingAlertService,
|
||||
IRecordingService recordingService,
|
||||
IAppointmentService appointmentService,
|
||||
IAlarmService alarmService,
|
||||
IOptions<RabbitMqSettings> settings,
|
||||
ILogger<ReceiverService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings.Value;
|
||||
|
||||
_observationService = observationService;
|
||||
_treatmentService = treatmentService;
|
||||
_patientsService = patientsService;
|
||||
_pumpService = pumpService;
|
||||
_recordingAlertService = recordingAlertService;
|
||||
_recordingService = recordingService;
|
||||
_appointmentService = appointmentService;
|
||||
_alarmService = alarmService;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_settings.ConnectionString))
|
||||
{
|
||||
SetQueues();
|
||||
TryToConnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("RabbitMQ connection string NOT configured");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private void AddQueue(string? queue)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(queue))
|
||||
_queues.Add(queue!);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
RegisterConsumer(queue);
|
||||
}
|
||||
|
||||
_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 += (_, _) =>
|
||||
{
|
||||
timer.Stop();
|
||||
TryToConnect();
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeBus()
|
||||
{
|
||||
if (_bus is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterConsumer(string queue)
|
||||
{
|
||||
_bus!.SendReceive.ReceiveAsync<string>(queue, async payload =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Message received from {queue}", queue);
|
||||
|
||||
var service = ResolveService(queue);
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
_logger.LogWarning("No service mapped for queue {queue}", queue);
|
||||
return;
|
||||
}
|
||||
|
||||
var message = new Message<string>(
|
||||
payload,
|
||||
new MessageProperties()
|
||||
);
|
||||
|
||||
await Task.Run(() => TryToParseAndSend(message, service));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing message from {queue}", queue);
|
||||
throw; // 🔴 importante: activa retry
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
await _bus.SendReceive.ReceiveAsync<Error>(errorQueueName, async err =>
|
||||
{
|
||||
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
|
||||
/// Throws exception to trigger retry on failure
|
||||
/// </summary>
|
||||
private void TryToParseAndSend(IMessage<string> msg, IApiRequestService service)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Processing message: {msg}", msg.Body);
|
||||
|
||||
var apiRequest = JsonConvert.DeserializeObject<ApiRequest>(msg.Body);
|
||||
|
||||
if (apiRequest == null)
|
||||
{
|
||||
_logger.LogError("Invalid ApiRequest JSON: {msg}", msg.Body);
|
||||
return;
|
||||
}
|
||||
|
||||
if (apiRequest.PatientNumber == "\"\"")
|
||||
apiRequest.PatientNumber = string.Empty;
|
||||
|
||||
service.SaveRequest(apiRequest);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing request");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using adas_core.Application.Repositories.Interfaces;
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Domain.Enums;
|
||||
using adas_core.Domain.Exceptions;
|
||||
using adas_core.Domain.Models.Filter;
|
||||
using adas_core.Domain.Models.MongoModels;
|
||||
using adas_core.Domain.Models.Responses;
|
||||
using adas_core.Infrastructure.Utils;
|
||||
using adas_core.module.Relays.Devices;
|
||||
using adas_core.module.Relays.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using Quartz.Util;
|
||||
|
||||
namespace adas_core.Infrastructure.Services;
|
||||
|
||||
public class RelayService : IRelayService
|
||||
{
|
||||
private readonly List<RelayDevice> _devices = [];
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<RelayService> _logger;
|
||||
private readonly IPointOfCareService _pointOfCareService;
|
||||
private readonly RelaySettings _relaySettings;
|
||||
private readonly IRelayRepository _relayRepository;
|
||||
|
||||
private readonly ConcurrentDictionary<Tuple<string, int, int>, RelayEnum.Status> _relayWithStatus = new();
|
||||
private readonly string _url;
|
||||
private UriBuilder? _builder;
|
||||
|
||||
public RelayService(
|
||||
ILogger<RelayService> logger,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IOptions<RelaySettings> relaySettings,
|
||||
IPointOfCareService pointOfCareService,
|
||||
IRelayRepository relayRepository)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_pointOfCareService = pointOfCareService;
|
||||
_relayRepository = relayRepository;
|
||||
_logger = logger;
|
||||
_relaySettings = relaySettings.Value;
|
||||
_url = _relaySettings.RecordingOrApiUrl ?? string.Empty;
|
||||
|
||||
Task.Run(async () => await InitRelayWithStatus());
|
||||
}
|
||||
|
||||
public async Task<RelayEnum.Status> CheckRelayStatus(Relay relay)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
if (_relaySettings.Cache && relay.Cache)
|
||||
lock (_relayWithStatus)
|
||||
{
|
||||
if (_relayWithStatus.Any() &&
|
||||
_relayWithStatus[cacheKey] == RelayEnum.Status.On)
|
||||
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}/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));
|
||||
}
|
||||
}
|
||||
|
||||
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 })
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Relay?> GetById(ObjectId relay)
|
||||
{
|
||||
return _relayRepository.GetById(relay);
|
||||
}
|
||||
public List<Relay> GetRelayInList(List<ObjectId>? relayList)
|
||||
{
|
||||
if(relayList == null) return new List<Relay>();
|
||||
return _relayRepository.GetRelayInList(relayList);
|
||||
}
|
||||
|
||||
public List<Relay> GetRelayByTypeInList(List<ObjectId>? configurationRelayList, RelayEnum.Type type)
|
||||
{
|
||||
if(configurationRelayList == null) return new List<Relay>();
|
||||
return _relayRepository.GetRelayByTypeInList(configurationRelayList, type);
|
||||
}
|
||||
|
||||
public async Task<PaginationResponse<Relay>> GetPaginatedRelays(PaginationFilter filter)
|
||||
{
|
||||
var usedRelayIds = await _pointOfCareService.FindAllIdRelaysInUse();
|
||||
|
||||
var fluentQuery = _relayRepository.GetPaginatedRelays(filter);
|
||||
|
||||
if (filter.FilteredRequest?.InUse != null)
|
||||
{
|
||||
bool filterInUse = filter.FilteredRequest.InUse.Value;
|
||||
var filterBuilder = Builders<Relay>.Filter;
|
||||
|
||||
var idFilter = filterInUse
|
||||
? filterBuilder.In(c => c.Id, usedRelayIds)
|
||||
: filterBuilder.Not(filterBuilder.In(c => c.Id, usedRelayIds));
|
||||
|
||||
fluentQuery.Filter = filterBuilder.And(fluentQuery.Filter, idFilter);
|
||||
}
|
||||
|
||||
var count = await fluentQuery.CountDocumentsAsync();
|
||||
var data = await fluentQuery
|
||||
.Skip((filter.PageNumber - 1) * filter.PageSize)
|
||||
.Limit(filter.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
if (data == null) return new PaginationResponse<Relay>([], filter.PageNumber, filter.PageSize, count);
|
||||
|
||||
foreach (var camera in data)
|
||||
{
|
||||
if (camera == null) continue;
|
||||
|
||||
bool isInUse = usedRelayIds.Contains(camera.Id);
|
||||
|
||||
// Asignación mediante reflexión para el private set
|
||||
camera.GetType().GetProperty(nameof(Relay.InUse))
|
||||
?.SetValue(camera, isInUse);
|
||||
}
|
||||
|
||||
return new PaginationResponse<Relay>(data, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public Task<Relay?> UpdateRelayById(ObjectId objectId, Relay relay)
|
||||
{
|
||||
return _relayRepository.UpdateRelayAsync(objectId, relay);
|
||||
}
|
||||
|
||||
public event EventHandler<Tuple<RelayDevice, int>>? RelayStatusChanged;
|
||||
|
||||
private async Task InitRelayWithStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var pocConfigList = await _pointOfCareService.GetAllConfigs();
|
||||
var relayTasks = new List<Task>();
|
||||
|
||||
pocConfigList.ForEach(poc =>
|
||||
{
|
||||
if (poc.Configuration?.RelayList == null) return;
|
||||
relayTasks.AddRange(from relayConf in poc.Configuration.RelayList
|
||||
let statusTask = CheckRelayStatus(relayConf)
|
||||
select statusTask.ContinueWith(task =>
|
||||
{
|
||||
if (task.IsCompletedSuccessfully)
|
||||
{
|
||||
var cackeKey = Tuple.Create(relayConf.Ip, relayConf.Port, relayConf.RelayNumber);
|
||||
if (!relayConf.Cache || !relayConf.Cache) return;
|
||||
lock (_relayWithStatus)
|
||||
{
|
||||
_relayWithStatus[cackeKey] = task.Result;
|
||||
}
|
||||
}
|
||||
else if (task.IsFaulted)
|
||||
{
|
||||
// Manejar la excepción si la llamada asincrónica falla
|
||||
_logger.LogError("Error al obtener el estado del relé {RelayParsedIp}: {TaskException}",
|
||||
relayConf.Ip, task.Exception.Message);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
await Task.WhenAll(relayTasks);
|
||||
|
||||
lock (_relayWithStatus)
|
||||
{
|
||||
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Exception setting relay with status: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("Error PowerRelay relay {Relay}: {EMessage} {EStackTrace}", relay, e.Message,
|
||||
e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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] = RelayEnum.Status.Unknown;
|
||||
_logger.LogDebug("Relay status cached: {Dct}", DictionaryToString(_relayWithStatus));
|
||||
}
|
||||
|
||||
return RelayEnum.Status.Unknown;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Domain.Models.AppSettings;
|
||||
using adas_core.Domain.Models.SystemAlerts;
|
||||
using EasyNetQ;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace adas_core.Infrastructure.Services;
|
||||
|
||||
public class SendAlertService(
|
||||
IOptions<RabbitMqSettings> rabbitMqSettings,
|
||||
ILogger<SendAlertService> logger)
|
||||
: ISendAlertService
|
||||
{
|
||||
private readonly string _errorObservationsQueue = $"Error{rabbitMqSettings.Value.ObservationsQueue}";
|
||||
private readonly string _errorPatientsQueue = $"Error{rabbitMqSettings.Value.PatientsQueue}";
|
||||
private readonly string _errorPumpsQueue = $"Error{rabbitMqSettings.Value.PumpsQueue}";
|
||||
private readonly string _errorRecordingQueue = $"Error{rabbitMqSettings.Value.RecordingQueue}";
|
||||
private readonly string _errorTreatmetsQueue = $"Error{rabbitMqSettings.Value.TreatmentsQueue}";
|
||||
private readonly string? _rabbitConnectionString = rabbitMqSettings.Value.ConnectionString;
|
||||
|
||||
|
||||
//readonly PlatformID[] _windowsPlatforms;
|
||||
|
||||
////TODO new ws private static DefaultWebSocketHandler defaultWebSocketHandler = new ();
|
||||
|
||||
//_windowsPlatforms = new[]
|
||||
//{
|
||||
// PlatformID.Win32NT,
|
||||
// PlatformID.Win32S,
|
||||
// PlatformID.Win32Windows,
|
||||
// PlatformID.WinCE
|
||||
//};
|
||||
|
||||
public async Task<List<Queue>> GetQueues()
|
||||
{
|
||||
if (_rabbitConnectionString != null) return await GetQueuesAsync();
|
||||
|
||||
logger.LogError("Rabbit connection string not defined in web config");
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public async Task<List<ApiClients>> GetApiClients()
|
||||
{
|
||||
return await GetApiClientsAsync();
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return errorQueues;
|
||||
}
|
||||
|
||||
public Performance GetConsumedCpu()
|
||||
{
|
||||
Performance performance = new();
|
||||
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
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"
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError("Error check Storage performance: {eMessage} {eStackTrace}", e.Message, e.StackTrace);
|
||||
}
|
||||
|
||||
return performance;
|
||||
}
|
||||
|
||||
private List<Performance> GetConsumedStorages()
|
||||
{
|
||||
var list = new List<Performance>();
|
||||
try
|
||||
{
|
||||
foreach (var drive in DriveInfo.GetDrives())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return clientsList;
|
||||
}
|
||||
|
||||
private Task<ApiClients?> GetWebSocketClients()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user