From 203908152c734dcb0f8d5d11957f2762839a311e Mon Sep 17 00:00:00 2001 From: Noe Date: Mon, 1 Sep 2025 13:07:26 +0200 Subject: [PATCH] Add Data Collector App --- csharp/DataCollectorWebApp/Controller.cs | 271 ++++++++++++++++++ .../DataCollectorWebApp.csproj | 14 + csharp/DataCollectorWebApp/Program.cs | 19 ++ csharp/InnovEnergy.sln | 7 + 4 files changed, 311 insertions(+) create mode 100644 csharp/DataCollectorWebApp/Controller.cs create mode 100644 csharp/DataCollectorWebApp/DataCollectorWebApp.csproj create mode 100644 csharp/DataCollectorWebApp/Program.cs diff --git a/csharp/DataCollectorWebApp/Controller.cs b/csharp/DataCollectorWebApp/Controller.cs new file mode 100644 index 000000000..e5b13d481 --- /dev/null +++ b/csharp/DataCollectorWebApp/Controller.cs @@ -0,0 +1,271 @@ +using System.Text; +using Microsoft.AspNetCore.Mvc; + +namespace DataCollectorWebApp; + +public class LoginResponse +{ + public string Token { get; set; } + public object User { get; set; } // or a User class if needed + public bool AccessToSalimax { get; set; } + public bool AccessToSalidomo { get; set; } + public bool AccessToSodiohome { get; set; } + public bool AccessToSodistoreMax { get; set; } +} + + +public class Installation +{ + //Each installation has 2 roles, a read role and a write role. + //There are 2 keys per role a public key and a secret + //Product can be 0 or 1, 0 for Salimax, 1 for Salidomo + public String Name { get; set; } + public String Location { get; set; } + public String Region { get; set; } = ""; + public String Country { get; set; } = ""; + public String VpnIp { get; set; } = ""; + public String InstallationName { get; set; } = ""; + + public String S3Region { get; set; } = "sos-ch-dk-2"; + public String S3Provider { get; set; } = "exo.io"; + public String S3WriteKey { get; set; } = ""; + public String S3Key { get; set; } = ""; + public String S3WriteSecret { get; set; } = ""; + public String S3Secret { get; set; } = ""; + public int S3BucketId { get; set; } = 0; + public String ReadRoleId { get; set; } = ""; + public String WriteRoleId { get; set; } = ""; + public Boolean TestingMode { get; set; } = false; + public int Status { get; set; } = -1; + public int Product { get; set; } = 0; + public int Device { get; set; } = 0; + public string SerialNumber { get; set; } = ""; + + public String OrderNumbers { get; set; } + public String VrmLink { get; set; } = ""; +} + +[Controller] +public class InstallationsController : Controller +{ + + + +[HttpGet] +[Route("/Installations")] +[Produces("text/html")] +public async Task Index() +{ + const string HtmlHeader = @" + + + Inesco Energy Installations Overview + + + +

Installation Overview

+ + + + + + + + + + + + +"; + + const string HtmlFooter = @" + +
NameProductLocationVPN IPStatus
+ + +"; + + + + string GetProductName(int productId) + { + return productId switch + { + 0 => "Salimax", + 1 => "Salidomo", + 2 => "SodioHome", + 3 => "SodistoreMax", + }; + } + + string GetStatusHtml(int status) + { + if (status == -1) + { + return "×"; + } + + + var statusClass = $"status-{status}"; + var title = status switch + { + 0 => "Online", + 1 => "Warning", + 2 => "Error", + _ => "Unknown" + }; + + return $""; + } + + + string BuildRowHtml(Installation i) => $@" + + {i.Name} + {GetProductName(i.Product)} + {i.Location} + {i.VpnIp} + {GetStatusHtml(i.Status)} + +"; + + var installations = await FetchInstallationsFromApi(); + + var sb = new StringBuilder(); + sb.Append(HtmlHeader); + + foreach (var i in installations) + { + sb.Append(BuildRowHtml(i)); + } + + sb.Append(HtmlFooter); + return Content(sb.ToString(), "text/html"); +} + + + public async Task?> FetchInstallationsFromApi() + { + + var username = "baumgartner@innov.energy"; + var password = "1234"; + + using var http = new HttpClient { BaseAddress = new Uri("https://monitor.inesco.energy/api/") }; + + // Step 1: Login + var loginResponse = await http.PostAsync($"Login?username={username}&password={password}", null); + if (!loginResponse.IsSuccessStatusCode) + { + Console.WriteLine("Login failed with status code {StatusCode}", loginResponse.StatusCode); + return null; + } + + var loginData = await loginResponse.Content.ReadFromJsonAsync(); + if (loginData?.Token is null) + { + Console.WriteLine("Login succeeded but token was missing"); + return null; + } + + var token = loginData.Token; + Console.WriteLine($"Token: {token}"); + var installations = new List(); + + var getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=0&authToken={token}"); + + var newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); + if (newInstallations != null) + { + installations.AddRange(newInstallations); + } + + getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=1&authToken={token}"); + newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); + if (newInstallations != null) + { + installations.AddRange(newInstallations); + } + + getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=2&authToken={token}"); + newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); + if (newInstallations != null) + { + installations.AddRange(newInstallations); + } + + getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=3&authToken={token}"); + newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync>(); + if (newInstallations != null) + { + installations.AddRange(newInstallations); + } + + + //Console.WriteLine("Installations retrieved ",installations); + return installations; + } + +} \ No newline at end of file diff --git a/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj b/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj new file mode 100644 index 000000000..3bde501d5 --- /dev/null +++ b/csharp/DataCollectorWebApp/DataCollectorWebApp.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/csharp/DataCollectorWebApp/Program.cs b/csharp/DataCollectorWebApp/Program.cs new file mode 100644 index 000000000..6420e3bf6 --- /dev/null +++ b/csharp/DataCollectorWebApp/Program.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Builder; + +namespace InnovEnergy.App.DataCollectorWebApp; + +public static class Program +{ + public static async Task Main(string[] args) + { + Console.WriteLine("Starting DataCollectorWebApp"); + + var builder = WebApplication.CreateBuilder(args); + + builder.Services.AddControllers(); + var app = builder.Build(); + + app.MapControllers(); + await app.RunAsync(); + } +} \ No newline at end of file diff --git a/csharp/InnovEnergy.sln b/csharp/InnovEnergy.sln index c5ec2b4fa..b81e69618 100644 --- a/csharp/InnovEnergy.sln +++ b/csharp/InnovEnergy.sln @@ -103,6 +103,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrowattCommunication", "App EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WITGrowatt4-15K", "Lib\Devices\WITGrowatt4-15K\WITGrowatt4-15K.csproj", "{44DD9E5E-2AD3-4579-A47D-7A40FD28D369}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataCollectorWebApp", "DataCollectorWebApp\DataCollectorWebApp.csproj", "{6069D487-DBAB-4253-BFA1-CF994B84BE49}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -274,6 +276,10 @@ Global {44DD9E5E-2AD3-4579-A47D-7A40FD28D369}.Debug|Any CPU.Build.0 = Debug|Any CPU {44DD9E5E-2AD3-4579-A47D-7A40FD28D369}.Release|Any CPU.ActiveCfg = Release|Any CPU {44DD9E5E-2AD3-4579-A47D-7A40FD28D369}.Release|Any CPU.Build.0 = Release|Any CPU + {6069D487-DBAB-4253-BFA1-CF994B84BE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6069D487-DBAB-4253-BFA1-CF994B84BE49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6069D487-DBAB-4253-BFA1-CF994B84BE49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6069D487-DBAB-4253-BFA1-CF994B84BE49}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A} @@ -321,5 +327,6 @@ Global {39B83793-49DB-4940-9C25-A7F944607407} = {145597B4-3E30-45E6-9F72-4DD43194539A} {DC0BE34A-368F-46DC-A081-70C9A1EFE9C0} = {145597B4-3E30-45E6-9F72-4DD43194539A} {44DD9E5E-2AD3-4579-A47D-7A40FD28D369} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} + {6069D487-DBAB-4253-BFA1-CF994B84BE49} = {145597B4-3E30-45E6-9F72-4DD43194539A} EndGlobalSection EndGlobal