304 lines
15 KiB
C#
304 lines
15 KiB
C#
using System.Net;
|
|
using System.Xml;
|
|
using adas_core.Domain.Enums;
|
|
using adas_core.Domain.Models.MongoModels;
|
|
using adas_core.module.Relays.Models;
|
|
using adas_core.module.Relays.Utils;
|
|
using Serilog;
|
|
|
|
namespace adas_core.module.Relays.Devices;
|
|
|
|
/// <summary>
|
|
/// Class representing a KMTronic V2 relay device. This class inherits from the RelayDevice class and provides specific implementations for checking the device status, getting the status of individual relays, and controlling the power state of the relays for a KMTronic V2 relay device.
|
|
/// </summary>
|
|
/// <!-- aidoc:v1 sig=72b48d3 -->
|
|
public class KmTronicV2Relay : RelayDevice
|
|
{
|
|
/// <summary>
|
|
/// Constructor for the KmTronicV2Relay class. It initializes the base RelayDevice class with the provided Relay and RelaySettings objects, and then checks the device status to ensure it is functioning correctly.
|
|
/// </summary>
|
|
/// <param name="relay">The Relay object representing the relay device.</param>
|
|
/// <param name="relaySettings">The RelaySettings object containing the configuration settings for the relay device.</param>
|
|
/// <!-- aidoc:v1 sig=3bbe6ca body=0353383 -->
|
|
public KmTronicV2Relay(Relay relay, RelaySettings relaySettings) : base(relay, relaySettings)
|
|
{
|
|
CheckDevice();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the status of the KMTronic V2 relay device by sending a request to retrieve the status of the relays.
|
|
/// It updates the internal status of each relay based on the response received.
|
|
/// The method returns true if the device is responsive and provides a valid status, and false otherwise.
|
|
/// </summary>
|
|
/// <returns>True if the device is responsive and provides a valid status, false otherwise.</returns>
|
|
/// <!-- aidoc:v1 sig=b02a15f body=470606e -->
|
|
public sealed override bool CheckDevice()
|
|
{
|
|
CheckStatus();
|
|
var result = GetStatusRelay(1);
|
|
return result != RelayEnum.Status.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the status of the KMTronic V2 relay device by sending a request to retrieve the status of the relays.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The method sends an HTTP GET request to the "status.xml" endpoint of the KMTronic V2 relay device and parses the XML response to update the internal status of each relay.
|
|
/// </remarks>
|
|
/// <!-- aidoc:v1 sig=abdef98 body=7148801 -->
|
|
public override void CheckStatus()
|
|
{
|
|
try
|
|
{
|
|
var httpResponse = SendRequest("status.xml", HttpMethod.Get);
|
|
|
|
if (httpResponse.StatusCode.Equals(HttpStatusCode.OK))
|
|
{
|
|
var responseContent = httpResponse.Content;
|
|
var responseString = responseContent.ReadAsStringAsync().Result;
|
|
if (!string.IsNullOrEmpty(responseString))
|
|
{
|
|
var response = new XmlDocument();
|
|
response.LoadXml(responseString);
|
|
foreach (XmlNode node in response.SelectNodes("//response")!)
|
|
foreach (XmlNode childNode in node.ChildNodes)
|
|
{
|
|
if (!childNode.Name.StartsWith("relay") || string.IsNullOrEmpty(childNode.InnerText)) continue;
|
|
var id = int.Parse(childNode.Name[5..]);
|
|
if (id <= 0 || id > Relay.Total) continue;
|
|
var value = int.Parse(childNode.InnerText);
|
|
var relayStatus = value.Equals(0) ? RelayEnum.Status.Off :
|
|
value.Equals(1) ? RelayEnum.Status.On : RelayEnum.Status.Unknown;
|
|
Tuple.Create(Relay.Ip, Relay.Port, id);
|
|
SetStatusRelay(id, relayStatus);
|
|
}
|
|
}
|
|
|
|
Log.Debug(responseContent.ReadAsStringAsync().Result);
|
|
}
|
|
else
|
|
{
|
|
Log.Debug("Status code = " + httpResponse.StatusCode);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex.ToString());
|
|
Enumerable.Range(1, Relay.Total).ToList().ForEach(outletId =>
|
|
{
|
|
SetStatusRelay(outletId, RelayEnum.Status.Unknown);
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status of a specific relay outlet by sending a request to the KMTronic V2 relay device. The method returns the status of the specified relay outlet, which can be On, Off, or Unknown.
|
|
/// </summary>
|
|
/// <returns>True if the specified relay outlet is On, false otherwise.</returns>
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary refers to 'a specific relay outlet' and 'the specified relay outlet', but this overload takes no parameters and always checks relay 1." -->
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary claims the status can be 'On, Off, or Unknown', but the method returns a bool, not a status value." -->
|
|
public override bool GetStatusRelay()
|
|
{
|
|
return GetStatusRelay(1) == RelayEnum.Status.On;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status of a specific relay outlet by sending a request to the KMTronic V2 relay device. The method returns the status of the specified relay outlet, which can be On, Off, or Unknown.
|
|
/// </summary>
|
|
/// <returns>True if the specified relay outlet is On, false otherwise.</returns>
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary references 'the specified relay outlet' but the method takes no parameters to specify a relay." -->
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary states the method returns 'On, Off, or Unknown' but it returns a bool (true/false)." -->
|
|
public override Task<bool> GetStatusRelayAsync()
|
|
{
|
|
var result = GetStatusRelay();
|
|
return Task.FromResult(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status of a specific relay outlet by retrieving the status from the internal status dictionary. The method returns the status of the specified relay outlet, which can be On, Off, or Unknown.
|
|
/// </summary>
|
|
/// <param name="outletId">The ID of the relay outlet.</param>
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary describes getting/returning relay status, but the method PowerOnRelay turns a relay on by calling PowerRelay(outletId, RelayEnum.Status.On) and returns void." -->
|
|
public override void PowerOnRelay(int outletId)
|
|
{
|
|
PowerRelay(outletId, RelayEnum.Status.On);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the status of a specific relay outlet by retrieving the status from the internal status dictionary. The method returns the status of the specified relay outlet, which can be On, Off, or Unknown.
|
|
/// </summary>
|
|
/// <param name="outletId">The ID of the relay outlet.</param>
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "Summary describes getting/retrieving the status of a relay outlet, but the method actually powers off a relay outlet." -->
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
|
|
/// "Summary states the method returns a status (On, Off, or Unknown), but the method returns void." -->
|
|
public override void PowerOffRelay(int outletId)
|
|
{
|
|
PowerRelay(outletId, RelayEnum.Status.Off);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a URI for sending requests to the KMTronic V2 relay device based on the specified path and query parameters.
|
|
/// The method builds the URI using the IP address, port, and path of the relay device, and includes any query parameters if provided.
|
|
/// </summary>
|
|
/// <param name="path">The path of the relay device.</param>
|
|
/// <param name="query">The query parameters for the request.</param>
|
|
/// <returns>The constructed URI.</returns>
|
|
/// <!-- aidoc:v1 sig=995f6fd body=c5da058 -->
|
|
private Uri GetUri(string path, string? query = null)
|
|
{
|
|
var uriBuilder = new UriBuilder
|
|
{ Scheme = "http", Host = Relay.Ip, Port = Relay.Port, Path = path };
|
|
if (!string.IsNullOrEmpty(query)) uriBuilder.Query = query;
|
|
return uriBuilder.Uri;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs an HttpRequestMessage for sending requests to the KMTronic V2 relay device based on the specified path, HTTP method, and query parameters.
|
|
/// </summary>
|
|
/// <param name="path">The path of the relay device.</param>
|
|
/// <param name="method">The HTTP method for the request.</param>
|
|
/// <param name="query">The query parameters for the request.</param>
|
|
/// <returns>The constructed HttpRequestMessage.</returns>
|
|
/// <!-- aidoc:v1 sig=82d34b3 body=c3d1721 -->
|
|
private HttpRequestMessage GetRequest(string path, HttpMethod method, string? query = null)
|
|
{
|
|
var uri = GetUri(path, query);
|
|
var request = new HttpRequestMessage
|
|
{
|
|
RequestUri = uri,
|
|
Method = method
|
|
};
|
|
|
|
if (!string.IsNullOrEmpty(Relay.Username) && !string.IsNullOrEmpty(Relay.Password))
|
|
HttpUtils.AddBasicAuth(Relay.Username, Relay.Password, request.Headers);
|
|
return request;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends an HTTP request to the KMTronic V2 relay device using the specified path, HTTP method, and query parameters.
|
|
/// The method constructs the request using the GetRequest method and sends it using an HttpClient.
|
|
/// It handles any exceptions that may occur during the request and returns the HttpResponseMessage received from the relay device.
|
|
/// </summary>
|
|
/// <param name="path">The path of the relay device.</param>
|
|
/// <param name="method">The HTTP method for the request.</param>
|
|
/// <param name="query">The query parameters for the request.</param>
|
|
/// <returns>The HttpResponseMessage received from the relay device.</returns>
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
|
|
/// "The <returns> tag states the method returns 'The HttpResponseMessage received from the relay device', but when an exception is caught the method returns a locally constructed new HttpResponseMessage(HttpStatusCode.Gone), not one received from the relay." -->
|
|
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
|
/// "The summary's final sentence implies the returned message comes from the relay device after exception handling, but the catch branch synthesizes a HttpStatusCode.Gone response instead; this behavior (and the synthetic Gone return) is not documented." -->
|
|
private HttpResponseMessage SendRequest(string path, HttpMethod method, string? query = null)
|
|
{
|
|
using var client = new HttpClient();
|
|
client.Timeout = TimeSpan.FromSeconds(15);
|
|
try
|
|
{
|
|
return client.Send(GetRequest(path, method, query));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Warning("Error sending request to kmtronic ip {IP} port {Port}: {Message}", Relay.Ip,
|
|
Relay.Port, e.Message);
|
|
return new HttpResponseMessage(HttpStatusCode.Gone);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Controls the power state of a specific relay outlet by sending a request to the KMTronic V2 relay device.
|
|
/// The method checks the current status of the specified relay outlet and sends a request to change its state to the desired state (On or Off).
|
|
/// If the checkStatus parameter is set to true, it will also check the status of the device after sending the request to ensure that the state change was successful.
|
|
/// </summary>
|
|
/// <param name="outletId">The ID of the relay outlet to control.</param>
|
|
/// <param name="toState">The desired state for the relay outlet (On or Off).</param>
|
|
/// <param name="checkStatus">Whether to check the status of the device after sending the request.</param>
|
|
/// <!-- aidoc:v1 sig=36b130b body=ee2cc07 -->
|
|
private void PowerRelay(int outletId, RelayEnum.Status toState, bool checkStatus = true)
|
|
{
|
|
if (Relay.Mode == RelayEnum.Mode.OpenedOffClosedOn)
|
|
toState = toState == RelayEnum.Status.Off ? RelayEnum.Status.On : RelayEnum.Status.Off;
|
|
|
|
var status = GetStatusRelay(outletId);
|
|
|
|
if (status == toState) return;
|
|
|
|
|
|
//No entiendo esto así, siempre enciende/apaga el mismo uriBuilder.Query = $"relay={outlet.outlet}";
|
|
//me quedo con el nombre y solo con el número del rele que es la llamada que el hace realmente desde la web
|
|
//No uso una regex porque es mucho más lento
|
|
|
|
try
|
|
{
|
|
SendRequest("relays.cgi", HttpMethod.Post, $"relay={outletId}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Debug(ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
if (checkStatus) CheckStatus();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls the power state of all relay outlets by sending requests to the KMTronic V2 relay device.
|
|
/// The method iterates through all relay outlets and sends requests to change their state to the desired state (On or Off).
|
|
/// After sending the requests, it checks the status of the device to ensure that the state changes were successful.
|
|
/// </summary>
|
|
/// <!-- aidoc-review:v1 severity=low kind=stale_summary
|
|
/// "Summary describes the method as changing state to 'On or Off', but the method only sets state to On (RelayEnum.Status.On) since it is PowerOnAll." -->
|
|
public override void PowerOnAll()
|
|
{
|
|
PowerAll(RelayEnum.Status.On);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls the power state of all relay outlets by sending requests to the KMTronic V2 relay device.
|
|
/// </summary>
|
|
/// <!-- aidoc:v1 sig=bfcd3b2 body=2d798c0 -->
|
|
public override void PowerOffAll()
|
|
{
|
|
PowerAll(RelayEnum.Status.Off);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Controls the power state of all relay outlets by sending requests to the KMTronic V2 relay device.
|
|
/// </summary>
|
|
/// <param name="toState">The desired state for all relay outlets (On or Off).</param>
|
|
/// <!-- aidoc:v1 sig=b155bab body=11e41a0 -->
|
|
private void PowerAll(RelayEnum.Status toState)
|
|
{
|
|
Enumerable.Range(1, Relay.Total).ToList().ForEach(outletId => { PowerRelay(outletId, toState, false); });
|
|
CheckStatus();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reboots the KMTronic V2 relay device by first powering off all relay outlets, then waiting for a specified delay (if configured), and finally powering on all relay outlets again.
|
|
/// </summary>
|
|
/// <!-- aidoc:v1 sig=1be2480 body=f0389f8 -->
|
|
public override void Reboot()
|
|
{
|
|
PowerOffAll();
|
|
if (Relay.RebootDelay is > 0) Thread.Sleep(Relay.RebootDelay!.Value);
|
|
PowerOnAll();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reboots a specific relay outlet by first powering it off, then waiting for a specified delay (30 seconds in this case), and finally powering it on again.
|
|
/// </summary>
|
|
/// <param name="outletId">The ID of the relay outlet to reboot.</param>
|
|
/// <!-- aidoc:v1 sig=1edae69 body=1a46256 -->
|
|
public override void RebootOutlet(int outletId)
|
|
{
|
|
PowerOffRelay(outletId);
|
|
Thread.Sleep(30000);
|
|
PowerOnRelay(outletId);
|
|
}
|
|
} |