Files
adas-core/adas-core.Infrastructure/Services/RelayService.cs
T

459 lines
16 KiB
C#

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;
}
}