279 lines
9.5 KiB
C#
279 lines
9.5 KiB
C#
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
|
|
}
|
|
} |