Creado apartir del commit b888de6c92056de294456f581a56e25e734d4ab7 de develop
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using adas_core.Domain.Models;
|
||||
using adas_core.Domain.Utils;
|
||||
|
||||
namespace adas_core.module.LightBeacons.Devices;
|
||||
|
||||
public abstract class LightBeacon(EquatableDictionary<string, object> options)
|
||||
{
|
||||
protected readonly EquatableDictionary<string, object> Options = options;
|
||||
|
||||
|
||||
public abstract Task BlueCode();
|
||||
public abstract Task RedCode();
|
||||
public abstract Task YellowCode();
|
||||
public abstract Task PowerOffLed();
|
||||
public abstract Task GenerateColorAlert(PatientObservation obs);
|
||||
|
||||
|
||||
public abstract Task<Domain.Enums.LightBeaconColor> GetBeaconColor();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Serilog;
|
||||
|
||||
namespace adas_core.module.LightBeacons.Devices;
|
||||
|
||||
public abstract class LightBeaconAbstract(string host, string password)
|
||||
{
|
||||
public string Host = host;
|
||||
public string Password = password;
|
||||
|
||||
|
||||
public abstract void CodeBlue(object entry);
|
||||
|
||||
public abstract void CodeRed(object entry);
|
||||
|
||||
public abstract void CodeYellow(object entry);
|
||||
|
||||
public abstract void PowerOffLed(object entry);
|
||||
|
||||
public static LightBeaconAbstract? GetBeaconDevice(string driver, string host, string password)
|
||||
{
|
||||
var modelType = Type.GetType($"adas_core.Drivers.LightBeacon.{driver}");
|
||||
if (modelType == null)
|
||||
{
|
||||
Log.Debug($"LightBeacon modelType not found: driver {driver}, host {host}, password {password}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var ctor = modelType.GetConstructor([typeof(string), typeof(string)]);
|
||||
if (ctor == null)
|
||||
return null;
|
||||
|
||||
return (LightBeaconAbstract)ctor.Invoke([host, password]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using adas_core.Domain.Enums;
|
||||
using adas_core.Domain.Models;
|
||||
using adas_core.Domain.Utils;
|
||||
using HtmlAgilityPack;
|
||||
using Serilog;
|
||||
|
||||
namespace adas_core.module.LightBeacons.Devices;
|
||||
|
||||
public class Turktbens2LightBeacon : LightBeacon
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly int _code = 75;
|
||||
private readonly bool _emulate;
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _password = "password";
|
||||
private readonly int _port = 1;
|
||||
|
||||
private readonly Dictionary<int, int[]> _portMappings = new()
|
||||
{
|
||||
{ 1, [0, 1] },
|
||||
{ 2, [2, 3] },
|
||||
{ 3, [4, 5] },
|
||||
{ 4, [6, 7] }
|
||||
};
|
||||
|
||||
private readonly Dictionary<int, string[]> _ports = new()
|
||||
{
|
||||
{ 1, ["|1|1|1|2", "|12|0|1|0", "|1|0|1|2"] },
|
||||
{ 2, ["|1|3|1|2", "|12|1|1|0", "|1|2|1|2"] },
|
||||
{ 3, ["|1|5|1|2", "|12|2|1|0", "|1|4|1|2"] },
|
||||
{ 4, ["|1|7|1|2", "|12|3|1|0", "|1|6|1|2"] }
|
||||
};
|
||||
|
||||
private readonly int _timeout = 30;
|
||||
|
||||
private readonly string _url;
|
||||
|
||||
public Turktbens2LightBeacon(EquatableDictionary<string, object> options) : base(options)
|
||||
{
|
||||
_logger = Log.ForContext<Turktbens2LightBeacon>();
|
||||
if (options == null) throw new ArgumentException("TURKTBENS2Beacon has no options");
|
||||
if (!options.TryGetValue("url", out var url))
|
||||
throw new ArgumentException("TURKTBENS2Beacon has no valid url option");
|
||||
if (url == null) throw new ArgumentException("TURKTBENS2Beacon has no valid url option");
|
||||
_url = url.ToString()!;
|
||||
if (options.TryGetValue("password", out var password)) _password = password.ToString() ?? "password";
|
||||
if (options.TryGetValue("port", out var port)) _port = Convert.ToInt32(port);
|
||||
if (options.TryGetValue("timeout", out var timeout)) _timeout = Convert.ToInt32(timeout);
|
||||
if (options.TryGetValue("code", out var code)) _code = Convert.ToInt32(code);
|
||||
if (options.TryGetValue("emulate", out var emulate)) _emulate = Convert.ToBoolean(emulate);
|
||||
_client = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(_timeout)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// GetByCodeSysAndCode beacon color downloading the html of the web. and using AgilityPackHtml to access.
|
||||
/// The web is bugged and sometimes you are logged or not. The information is in two urls, the color is formed by 3
|
||||
/// bits, Vaux html changes if logged/not logged
|
||||
/// returns OFF when some error appears or try to parse the bits to BalizaColors enum
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override async Task<LightBeaconColor> GetBeaconColor()
|
||||
{
|
||||
if (_emulate)
|
||||
return LightBeaconColor.Off;
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
|
||||
try
|
||||
{
|
||||
//we dont need to login to check status
|
||||
var vauxValue = 0;
|
||||
var output1Value = 0;
|
||||
var output2Value = 0;
|
||||
var outputUri = new Uri($"{_url}/IO01_03.html");
|
||||
var vauxUri = new Uri($"{_url}/IO12_03.html");
|
||||
|
||||
var outputs = await httpClient.GetAsync(outputUri);
|
||||
var vauxs = await httpClient.GetAsync(vauxUri);
|
||||
if (!outputs.IsSuccessStatusCode || !vauxs.IsSuccessStatusCode)
|
||||
return LightBeaconColor.Off;
|
||||
|
||||
var outputsHtml = new HtmlDocument();
|
||||
outputsHtml.LoadHtml(await outputs.Content.ReadAsStringAsync());
|
||||
|
||||
var vauxHtml = new HtmlDocument();
|
||||
vauxHtml.LoadHtml(await vauxs.Content.ReadAsStringAsync());
|
||||
|
||||
//GetByCodeSysAndCode Vaux info from vauxUri can be on off and we translate it to 0,1
|
||||
var trElements = vauxHtml.DocumentNode.SelectNodes("//tr");
|
||||
if (trElements == null || trElements.Count < _port)
|
||||
return LightBeaconColor.Off;
|
||||
|
||||
var portNode = trElements[_port - 1];
|
||||
var tdNodes = portNode.SelectNodes(".//td");
|
||||
var vauxResult = tdNodes[1];
|
||||
switch (vauxResult.InnerText)
|
||||
{
|
||||
case "on":
|
||||
vauxValue = 1;
|
||||
break;
|
||||
//sometimes it display a select option when logged and other times its a label. this case is for option, take selected option
|
||||
case "offon":
|
||||
{
|
||||
var selected = vauxResult.SelectSingleNode("select").SelectSingleNode("option[@selected]");
|
||||
if (selected.InnerText.Equals("on")) vauxValue = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//GetByCodeSysAndCode outputs info
|
||||
var checkboxElements = outputsHtml.DocumentNode.SelectNodes("//input[@type='checkbox']");
|
||||
if (checkboxElements == null)
|
||||
return LightBeaconColor.Off;
|
||||
|
||||
var portsParsed = _portMappings.TryGetValue(_port, out var mappedValues);
|
||||
if (!portsParsed) return LightBeaconColor.Off;
|
||||
var output1 = checkboxElements[mappedValues![0]];
|
||||
var output2 = checkboxElements[mappedValues[1]];
|
||||
|
||||
if (output1.Attributes["checked"] != null) output1Value = 1;
|
||||
if (output2.Attributes["checked"] != null) output2Value = 1;
|
||||
|
||||
var finalValue = $"{output2Value}{vauxValue}{output1Value}";
|
||||
var parsed = Enum.TryParse(finalValue, out LightBeaconColor parsedColor);
|
||||
return parsed ? parsedColor : LightBeaconColor.Off;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(
|
||||
"Exception trying to get beacon color for beacon: {url} port: {port} ex: {eMessage}, trace: {eStackTrace}",
|
||||
_url, _port, e.Message, e.StackTrace);
|
||||
return LightBeaconColor.Off;
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task BlueCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Send(ColorsCodes.Blue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("An error occurred while sending the Blue code: {Message}", e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task RedCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Send(ColorsCodes.Red);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("An error occurred while sending the Red code: {Message}", e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task YellowCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Send(ColorsCodes.Yellow);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("An error occurred while sending the Yellow code: {Message}", e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task PowerOffLed()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Send(ColorsCodes.Off);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("An error occurred while sending the power off code: {Message}", e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override Task GenerateColorAlert(PatientObservation obs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
private async Task Send(ColorsCodes colorCodes)
|
||||
{
|
||||
var clr = ((int)colorCodes).ToString().PadLeft(3, '0');
|
||||
var passwordEncoded = Uri.EscapeDataString(_password);
|
||||
var body = new Dictionary<string, string>
|
||||
{
|
||||
{ "access_password", passwordEncoded },
|
||||
{ _code + _ports[_port][0], clr[0].ToString() },
|
||||
{ _code + _ports[_port][1], clr[1].ToString() },
|
||||
{ _code + _ports[_port][2], clr[2].ToString() }
|
||||
};
|
||||
|
||||
if (!_emulate)
|
||||
try
|
||||
{
|
||||
await SendMessage(body);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error("Failed to send color codes: {errorMessage}", e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected async Task SendMessage(Dictionary<string, string> body)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_url)) throw new InvalidOperationException("URL is not set.");
|
||||
|
||||
var retryCount = 0;
|
||||
const int maxRetries = 3; // Número máximo de intentos
|
||||
var delayMilliseconds = 500; // Tiempo de espera inicial entre reintentos
|
||||
|
||||
while (retryCount < maxRetries)
|
||||
try
|
||||
{
|
||||
var uri = new Uri(_url);
|
||||
var content = new FormUrlEncodedContent(body);
|
||||
var b = await content.ReadAsStringAsync();
|
||||
_logger.Debug("Sending to {url} body {b}", _url, b);
|
||||
var response = await _client.PostAsync(uri, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception(
|
||||
$"HTTP error: {response.StatusCode} while sending message to {_url} with body {b}");
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
retryCount++;
|
||||
_logger.Warning(
|
||||
"Error sending to url: {url}, message: {eMessage}, stack trace: {eStackTrace}, attempt: {retryCount}",
|
||||
_url, e.Message, e.StackTrace, retryCount);
|
||||
|
||||
if (retryCount >= maxRetries)
|
||||
{
|
||||
_logger.Error("Max retry attempts reached. Failing operation.");
|
||||
throw;
|
||||
}
|
||||
|
||||
await Task.Delay(delayMilliseconds);
|
||||
delayMilliseconds *= 2; // Incrementa el tiempo de espera para cada reintento
|
||||
}
|
||||
}
|
||||
|
||||
private enum ColorsCodes
|
||||
{
|
||||
Off = 000,
|
||||
Red = 001,
|
||||
Yellow = 011,
|
||||
Green = 010,
|
||||
Cyan = 110,
|
||||
Blue = 100,
|
||||
Magenta = 101,
|
||||
White = 111
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>adas_core.module.LightBeacons</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\adas-core.Application\adas-core.Application.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user