diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj index 9d16e3c..4dca5d7 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Elektrifikatsiya.csproj @@ -18,12 +18,21 @@ + + + + + + + + + @@ -35,10 +44,7 @@ - - - - + diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs new file mode 100644 index 0000000..9732592 --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Models/PrometheusQueryResult.cs @@ -0,0 +1,73 @@ +using System.Text.Json.Serialization; + +namespace Elektrifikatsiya.Models; + +public enum Status +{ + Success, + Error +} + +public enum ResultType +{ + Matrix, + Vector, + Scalar, + String +} + +public class PrometheusQueryResult +{ + public Status Status { get; set; } + + public string? ErrorType { get; set; } + public string? Error { get; set; } + public List? Warnings { get; set; } + + public PrometheusQueryResult(Status status, string? errorType, string? error, List? warnings) + { + Status = status; + ErrorType = errorType; + Error = error; + Warnings = warnings; + } +} + +internal class PrometheusDataWrapper +{ + public ResultType ResultType { get; set; } + public List Result { get; set; } + + public PrometheusDataWrapper(ResultType resultType, List result) + { + ResultType = resultType; + Result = result; + } +} + +internal class PrometheusDataMetric +{ + [JsonPropertyName("__name__")] public string Name { get; set; } + + public string Job { get; set; } + public string Instance { get; set; } + + public PrometheusDataMetric(string name, string job, string instance) + { + Name = name; + Job = job; + Instance = instance; + } +} + +internal class PrometheusData +{ + public PrometheusDataMetric Metric { get; set; } + public object Value { get; set; } + + public PrometheusData(PrometheusDataMetric metric, object value) + { + Metric = metric; + Value = value; + } +} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Models/ShellyResponse.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Models/ShellyResponse.cs new file mode 100644 index 0000000..bf6cd03 --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Models/ShellyResponse.cs @@ -0,0 +1,13 @@ +namespace Elektrifikatsiya.Models; + +public class ShellyResponse +{ + public string Type { get; set; } + public string Mac { get; set; } + + public ShellyResponse(string type, string mac) + { + Type = type; + Mac = mac; + } +} \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs index 1da0c91..bda0e67 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceManagmentService.cs @@ -10,7 +10,7 @@ public interface IDeviceManagmentService { public Task> Register(IPAddress ip, User user, string? name = null, string room = "default"); - public Task UnRegister(string macAdress); + public Task Unregister(string macAdress); public Result GetDevice(string macAdress); diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceStatusService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceStatusService.cs index ef7fab3..61e1cc1 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceStatusService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/IDeviceStatusService.cs @@ -13,4 +13,6 @@ public interface IDeviceStatusService public Result UpdateDeviceStatus(Device device); public Result TrackDevice(Device device); + + public Result UntrackDevice(string macAddress); } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs index 75e9a65..b659cf1 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceManagmentService.cs @@ -4,6 +4,7 @@ using Elektrifikatsiya.Models; using FluentResults; using System.Net; +using System.Net.NetworkInformation; namespace Elektrifikatsiya.Services.Implementations; @@ -11,11 +12,13 @@ public class DeviceManagmentService : IDeviceManagmentService { private readonly DeviceManagmentDatabaseContext deviceManagmentDatabaseContext; private readonly IDeviceStatusService deviceStatusService; + private readonly HttpClient httpClient; - public DeviceManagmentService(DeviceManagmentDatabaseContext deviceManagmentDatabaseContext, IDeviceStatusService deviceStatusService) + public DeviceManagmentService(DeviceManagmentDatabaseContext deviceManagmentDatabaseContext, IDeviceStatusService deviceStatusService, HttpClient httpClient) { this.deviceManagmentDatabaseContext = deviceManagmentDatabaseContext; this.deviceStatusService = deviceStatusService; + this.httpClient = httpClient; } public Result GetDevice(string macAdress) @@ -73,13 +76,50 @@ public class DeviceManagmentService : IDeviceManagmentService return getDevicesResult.Value.Where(d => d.User.Id == userId).ToList(); } - public Task> Register(IPAddress ip, User user, string? name = null, string room = "default") + public async Task> Register(IPAddress ip, User user, string? name = null, string room = "default") { - throw new NotImplementedException(); + ShellyResponse? shellyResponse = await httpClient.GetFromJsonAsync($"{ip}/shelly"); + + if(shellyResponse is null || shellyResponse.Type != "SHPLG-S") + { + return Result.Fail("Device is not a \"SHPLG-S\" or not reachable!"); + } + + string mac = shellyResponse.Mac; + + if(PhysicalAddress.TryParse(mac, out _)) + { + return Result.Fail("Invalid mac address!"); + } + + Device device = new Device(mac, name ?? mac, ip, user, room); + + deviceManagmentDatabaseContext.Add(device); + Result saveDatabaseChangesResult = await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync()); + + Result trackDeviceResult = deviceStatusService.TrackDevice(device); + + return Result.Merge(saveDatabaseChangesResult, trackDeviceResult).ToResult(device); } - public Task UnRegister(string macAdress) + public async Task Unregister(string macAdress) { - throw new NotImplementedException(); + Result getDeviceResult = GetDevice(macAdress); + + if (getDeviceResult.IsFailed) + { + return getDeviceResult.ToResult(); + } + + Result untrackDeviceResult = deviceStatusService.UntrackDevice(macAdress); + + if (untrackDeviceResult.IsFailed) + { + return untrackDeviceResult; + } + + deviceManagmentDatabaseContext.Remove(getDeviceResult.Value); + + return await Result.Try(async Task () => await deviceManagmentDatabaseContext.SaveChangesAsync()); } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs index 8b4263e..3574cec 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/DeviceStatusService.cs @@ -45,4 +45,9 @@ public class DeviceStatusService : IDeviceStatusService return Result.Ok(); } + + public Result UntrackDevice(string macAddress) + { + return devices.Remove(macAddress) ? Result.Ok() : Result.Fail("Device is not tracked!"); + } } \ No newline at end of file diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs index 6271a23..980826d 100644 --- a/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Services/Implementations/UpdateService.cs @@ -1,7 +1,8 @@ using Elektrifikatsiya.Database; using Elektrifikatsiya.Models; - +using FluentResults; using Microsoft.EntityFrameworkCore; +using System.Threading.Tasks; namespace Elektrifikatsiya.Services.Implementations; @@ -28,14 +29,24 @@ public class UpdateService : IHostedService, IDisposable _ = deviceStatusService.TrackDevice(device); } - timer = new Timer(Update, null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); + timer = new Timer(async (_) => await Update(), null, TimeSpan.Zero, TimeSpan.FromSeconds(15)); logger.LogInformation("Update service started."); } - private void Update(object? state) + private async Task Update() { - //TODO: Update all devices. + Result> getDeviceStatusResult = deviceStatusService.GetDevices(); + + if (getDeviceStatusResult.IsFailed) + { + logger.LogError("Updating devices failed!"); + } + + foreach(Device device in getDeviceStatusResult.Value) + { + //TODO: Some update magic + } } public Task StopAsync(CancellationToken cancellationToken) diff --git a/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs new file mode 100644 index 0000000..a635857 --- /dev/null +++ b/src/Elektrifikatsiya/Elektrifikatsiya/Utilities/PrometheusQuery.cs @@ -0,0 +1,25 @@ +using System.Net; +using System.Net.Sockets; +using System.Text.Encodings.Web; +using System.Text.Json; +using Elektrifikatsiya.Models; +using FluentResults; + +namespace Elektrifikatsiya.Utilities; + +public class PrometheusQuery +{ + private string connectionString; + private readonly HttpClient client = new(); + + public PrometheusQuery(string connectionString) + { + this.connectionString = connectionString; + client.BaseAddress = new Uri(connectionString); + } + + public Task Query(string query) + { + return client.GetFromJsonAsync($"/v1/query?{UrlEncoder.Create().Encode(query)}"); + } +} \ No newline at end of file