396 lines
14 KiB
C#
396 lines
14 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Text;
|
|
using adas_core.Application.Repositories.Interfaces;
|
|
using adas_core.Application.Services.Interfaces;
|
|
using adas_core.Domain.Enums;
|
|
using adas_core.Domain.Models;
|
|
using adas_core.Domain.Models.AppSettings;
|
|
using adas_core.Domain.Models.Filter;
|
|
using adas_core.Domain.Models.MongoModels;
|
|
using adas_core.Domain.Models.Responses;
|
|
using adas_core.Domain.Utils;
|
|
using adas_core.module.LightBeacons.Devices;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using MongoDB.Bson;
|
|
using MongoDB.Driver;
|
|
using LightBeaconDevice = adas_core.module.LightBeacons.Devices.LightBeacon;
|
|
using LightBeacon = adas_core.Domain.Models.MongoModels.LightBeacon;
|
|
|
|
namespace adas_core.module.LightBeacons.Services;
|
|
|
|
public class LightBeaconService : ILightBeaconService
|
|
{
|
|
private readonly IClientMessageService _clientMessageService;
|
|
|
|
private readonly ConcurrentDictionary<ObjectId, LightBeaconColor> _locationsWithColor = new();
|
|
|
|
private readonly ILogger<LightBeaconService> _logger;
|
|
private readonly IPointOfCareService _pointOfCareService;
|
|
private readonly ILightBeaconRepository _lightBeaconRepository;
|
|
private readonly ISubscribersService _subscribersService;
|
|
|
|
public LightBeaconService(
|
|
IOptions<ApiSettings> apiSettings,
|
|
ILogger<LightBeaconService> logger,
|
|
IClientMessageService clientMessageService,
|
|
ISubscribersService subscribersService,
|
|
IPointOfCareService pointOfCareService,
|
|
ILightBeaconRepository lightBeaconRepository)
|
|
{
|
|
_logger = logger;
|
|
_clientMessageService = clientMessageService;
|
|
_subscribersService = subscribersService;
|
|
_pointOfCareService = pointOfCareService;
|
|
_lightBeaconRepository = lightBeaconRepository;
|
|
|
|
try
|
|
{
|
|
Url = apiSettings.Value.BalizaUrl;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Error BalizaService apiSettings values: {exMessage} ", ex);
|
|
}
|
|
|
|
//_ = SetLocationsWithColor();
|
|
}
|
|
|
|
private string? Url { get; }
|
|
|
|
public async Task PowerOffLed(ObjectId pocId)
|
|
{
|
|
var poc = await _pointOfCareService.FindById(pocId);
|
|
if (poc == null)
|
|
{
|
|
_logger.LogError("Power off led did not find poc by id: {PocId}", pocId);
|
|
return;
|
|
}
|
|
|
|
_ = PowerOffLed(poc);
|
|
}
|
|
|
|
public async Task<LightBeacon?> InsertOne(LightBeacon beacon)
|
|
{
|
|
var beaconFound = await _lightBeaconRepository.GetByName(beacon.Name);
|
|
if(beaconFound != null) throw new Exception($"Light beacon with name {beacon.Name} already exists");
|
|
return await _lightBeaconRepository.InsertOneAsyncAndReturn(beacon);
|
|
}
|
|
|
|
public async Task<PaginationResponse<LightBeacon>> GetPaginatedBeacons(PaginationFilter filter)
|
|
{
|
|
var usedRelayIds = await _pointOfCareService.FindAllIdBeaconsInUse();
|
|
|
|
var fluentQuery = _lightBeaconRepository.GetPaginatedRelays(filter);
|
|
|
|
if (filter.FilteredRequest?.InUse != null)
|
|
{
|
|
bool filterInUse = filter.FilteredRequest.InUse.Value;
|
|
var filterBuilder = Builders<LightBeacon>.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<LightBeacon>([], 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<LightBeacon>(data, filter.PageNumber, filter.PageSize, count);
|
|
}
|
|
|
|
public async Task<List<LightBeacon>> GetSearchByName(string textToSearch)
|
|
{
|
|
return await _lightBeaconRepository.GetSearchByName(textToSearch);
|
|
}
|
|
|
|
public async Task<LightBeacon?> UpdateOne(LightBeacon beacon)
|
|
{
|
|
await _lightBeaconRepository.UpdateOneAsync(beacon.Id, beacon);
|
|
return await _lightBeaconRepository.GetById(beacon.Id);
|
|
}
|
|
|
|
public async Task PowerOffLed(PointOfCare poc)
|
|
{
|
|
if (_locationsWithColor.TryGetValue(poc.Id, out var lightBeaconColor) &&
|
|
lightBeaconColor == LightBeaconColor.Off)
|
|
return;
|
|
|
|
var beaconConfig = poc.Configuration?.BeaconIdList;
|
|
if (beaconConfig == null || !beaconConfig.Any()) return;
|
|
foreach (var beacon in beaconConfig.Select(GetBeacon))
|
|
if (beacon != null)
|
|
await SendColor(poc.Id, LightBeaconColor.Off);
|
|
|
|
lock (_locationsWithColor)
|
|
{
|
|
if (_locationsWithColor.ContainsKey(poc.Id))
|
|
_locationsWithColor[poc.Id] = LightBeaconColor.Off;
|
|
|
|
else
|
|
_locationsWithColor.TryAdd(poc.Id, LightBeaconColor.Off);
|
|
}
|
|
|
|
_logger.LogDebug("Locations with color cached: {dct}", DictionaryToString(_locationsWithColor));
|
|
}
|
|
|
|
public void GenerateColorAlert(PatientObservation obs)
|
|
{
|
|
try
|
|
{
|
|
LightBeaconColor colorEnum;
|
|
var colorNumbers = new char[3];
|
|
|
|
switch (obs.Status)
|
|
{
|
|
case StatusEnum.Type.Warning:
|
|
if (obs.WarnColor == null) return;
|
|
colorEnum = (LightBeaconColor)Enum.Parse(typeof(LightBeaconColor),
|
|
obs.WarnColor);
|
|
var colorValue = (int)colorEnum;
|
|
var valuesChart = colorValue.ToString().ToCharArray();
|
|
if (valuesChart.Length is < 3 and < 2)
|
|
{
|
|
var c = valuesChart[0];
|
|
colorNumbers[0] = '0';
|
|
colorNumbers[1] = '0';
|
|
colorNumbers[2] = c;
|
|
}
|
|
else if (valuesChart.Length < 3)
|
|
{
|
|
var c = valuesChart[0];
|
|
var c2 = valuesChart[1];
|
|
colorNumbers[0] = '0';
|
|
colorNumbers[1] = c;
|
|
colorNumbers[2] = c2;
|
|
}
|
|
|
|
break;
|
|
|
|
case StatusEnum.Type.Alert:
|
|
if (obs.AlertColor == null) return;
|
|
colorEnum = (LightBeaconColor)Enum.Parse(typeof(LightBeaconColor),
|
|
obs.AlertColor);
|
|
var value = (int)colorEnum;
|
|
var vchart = value.ToString().ToCharArray();
|
|
if (vchart.Length is < 3 and < 2)
|
|
{
|
|
var c = vchart[0];
|
|
colorNumbers[0] = '0';
|
|
colorNumbers[1] = '0';
|
|
colorNumbers[2] = c;
|
|
}
|
|
else if (vchart.Length < 3)
|
|
{
|
|
var c = vchart[0];
|
|
var c2 = vchart[1];
|
|
colorNumbers[0] = '0';
|
|
colorNumbers[1] = c;
|
|
colorNumbers[2] = c2;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
colorNumbers[0] = '0';
|
|
colorNumbers[1] = '0';
|
|
colorNumbers[2] = '0';
|
|
|
|
break;
|
|
}
|
|
|
|
var passwordEncoded = Uri.EscapeDataString("password");
|
|
var body =
|
|
$"access_password={passwordEncoded}&58|12|1|1|0={Uri.EscapeDataString(colorNumbers[0].ToString())}&58|1|3|1|2={Uri.EscapeDataString(colorNumbers[1].ToString())}&58|1|2|1|2={Uri.EscapeDataString(colorNumbers[2].ToString())}";
|
|
_ = SendMessage(body);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Error GenerateColorAlert {obs}: {exMessage}", obs, ex);
|
|
}
|
|
}
|
|
|
|
public async Task SendColor(ObjectId pocId, LightBeaconColor color)
|
|
{
|
|
var poc = await _pointOfCareService.FindById(pocId);
|
|
if (poc == null) return;
|
|
_ = SendColor(poc, color);
|
|
}
|
|
|
|
public async Task SendColor(PointOfCare poc, LightBeaconColor color)
|
|
{
|
|
try
|
|
{
|
|
if (_locationsWithColor.TryGetValue(poc.Id, out var lightBeaconColor) && lightBeaconColor == color)
|
|
return;
|
|
|
|
var beaconConfig = poc.Configuration?.BeaconIdList;
|
|
if (beaconConfig == null || !beaconConfig.Any()) return;
|
|
foreach (var beaconId in beaconConfig)
|
|
{
|
|
var beacon = await GetBeacon(beaconId);
|
|
switch (color)
|
|
{
|
|
case LightBeaconColor.Red:
|
|
beacon?.RedCode();
|
|
break;
|
|
case LightBeaconColor.Blue:
|
|
beacon?.BlueCode();
|
|
break;
|
|
case LightBeaconColor.Yellow:
|
|
beacon?.YellowCode();
|
|
break;
|
|
case LightBeaconColor.Off:
|
|
beacon?.PowerOffLed();
|
|
break;
|
|
}
|
|
|
|
await SendBeaconBroadcast(poc, color);
|
|
|
|
lock (_locationsWithColor)
|
|
{
|
|
if (_locationsWithColor.ContainsKey(poc.Id))
|
|
_locationsWithColor[poc.Id] = color;
|
|
else
|
|
_locationsWithColor.TryAdd(poc.Id, color);
|
|
}
|
|
}
|
|
|
|
_logger.LogDebug("Locations with color cached: {dct}", DictionaryToString(_locationsWithColor));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError("An error occurred on sending color {color} while sending the Beacon code: {Message}",
|
|
color.ToString(), e.Message);
|
|
}
|
|
}
|
|
|
|
public async Task<LightBeaconColor> GetColor(ObjectId pocId)
|
|
{
|
|
var poc = await _pointOfCareService.FindById(pocId);
|
|
if (poc == null) return LightBeaconColor.Off;
|
|
return await GetColor(poc);
|
|
}
|
|
|
|
public async Task<LightBeaconColor> GetColor(PointOfCare poc)
|
|
{
|
|
try
|
|
{
|
|
if (_locationsWithColor.TryGetValue(poc.Id, out var lightBeaconColor)) return lightBeaconColor;
|
|
|
|
var lightBeaconConfig = poc.Configuration?.BeaconIdList;
|
|
if (lightBeaconConfig == null || !lightBeaconConfig.Any()) return LightBeaconColor.Off;
|
|
foreach (var beaconId in lightBeaconConfig)
|
|
{
|
|
var beacon = await GetBeacon(beaconId);
|
|
if (beacon != null)
|
|
return await beacon.GetBeaconColor();
|
|
}
|
|
|
|
return LightBeaconColor.Off;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Exception getting color from beacon patient poc Id: {poc} Exception: {ex}", poc.Id, ex);
|
|
return LightBeaconColor.Off;
|
|
}
|
|
}
|
|
|
|
public async Task SendBeaconBroadcast(PointOfCare poc, LightBeaconColor color)
|
|
{
|
|
var subscribers = _subscribersService.GetSubscribers()
|
|
.Where(s => s.LocationIds.Any(lid => lid == poc.Id))
|
|
.ToList();
|
|
BeaconResponse bc = new(color.ToString(), poc.Id, poc.UnitId);
|
|
|
|
foreach (var subscriber in subscribers)
|
|
await _clientMessageService.SendAsync(subscriber.Id, OperationType.Beacon, bc);
|
|
}
|
|
|
|
public async Task SendBeaconBroadcast(ObjectId pocId, LightBeaconColor color)
|
|
{
|
|
var poc = await _pointOfCareService.FindById(pocId);
|
|
if (poc == null) return;
|
|
_ = SendBeaconBroadcast(poc, color);
|
|
}
|
|
|
|
private async Task SendMessage(string body)
|
|
{
|
|
if (string.IsNullOrEmpty(Url))
|
|
{
|
|
_logger.LogError("Url string is null or empty. Message:{body}", body);
|
|
return;
|
|
}
|
|
|
|
HttpClient client = new();
|
|
var content = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
|
|
|
|
try
|
|
{
|
|
var response = await client.PostAsync(Url, content);
|
|
|
|
if (response.IsSuccessStatusCode)
|
|
_logger.LogDebug("Message sent successfully: {body}", body);
|
|
else
|
|
_logger.LogError("Error SendMessage {body}: {responseStatusCode}", body, response.StatusCode);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Error SendMessage {body}: {exMessage}", body, ex);
|
|
}
|
|
}
|
|
|
|
private async Task<LightBeaconDevice?> GetBeacon(ObjectId lightBeaconId)
|
|
{
|
|
var cfg = await _lightBeaconRepository.GetById(lightBeaconId);
|
|
if (cfg == null) return null;
|
|
if (string.IsNullOrEmpty(cfg.Options.Url) || cfg.Options.Port == null)
|
|
return null;
|
|
switch (cfg.Type)
|
|
{
|
|
case "TURKTBENS2":
|
|
return new Turktbens2LightBeacon(new EquatableDictionary<string, object>
|
|
{
|
|
{ "url", cfg.Options.Url },
|
|
{ "port", cfg.Options.Port },
|
|
{ "password", cfg.Options.Password ?? "password" },
|
|
{ "emulate", cfg.Options.Emulate }
|
|
});
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string DictionaryToString(ConcurrentDictionary<ObjectId, LightBeaconColor> dictionary)
|
|
{
|
|
var builder = new StringBuilder();
|
|
|
|
foreach (var pair in dictionary) builder.AppendLine($"Location: {pair.Key}, Color: {pair.Value}");
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
|
|
//TODO:Almacenar en memoria los estados de los colores de la baliza
|
|
//Array con el puerto y color
|
|
} |