459 lines
16 KiB
C#
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;
|
|
}
|
|
} |