Add Data Collector App
This commit is contained in:
parent
076dcda4a2
commit
203908152c
|
|
@ -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<IActionResult> Index()
|
||||
{
|
||||
const string HtmlHeader = @"
|
||||
<html>
|
||||
<head>
|
||||
<title>Inesco Energy Installations Overview</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 2rem;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
th, td {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid rgb(200, 200, 200);
|
||||
text-align: left;
|
||||
}
|
||||
thead th {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #EB9486;
|
||||
}
|
||||
tbody tr:nth-child(odd) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
a {
|
||||
color: #0645AD;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.status-circle {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
margin-left: 50%;
|
||||
}
|
||||
.status-0 { background-color: #4CAF50; } /* Green */
|
||||
.status-1 { background-color: #FF9800; } /* Orange */
|
||||
.status-2 { background-color: #F44336; } /* Red */
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.offline-symbol {
|
||||
font-size: 1.2rem;
|
||||
color: #F44336; /* Gray */
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
margin-left: 50%;
|
||||
}
|
||||
|
||||
|
||||
.status-cell {
|
||||
text-align: center; /* center horizontally */
|
||||
vertical-align: middle; /* center vertically */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Installation Overview</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class=""centered"">Name</th>
|
||||
<th class=""centered"">Product</th>
|
||||
<th class=""centered"">Location</th>
|
||||
<th class=""centered"">VPN IP</th>
|
||||
<th class=""centered"">Status</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
";
|
||||
|
||||
const string HtmlFooter = @"
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
";
|
||||
|
||||
|
||||
|
||||
string GetProductName(int productId)
|
||||
{
|
||||
return productId switch
|
||||
{
|
||||
0 => "Salimax",
|
||||
1 => "Salidomo",
|
||||
2 => "SodioHome",
|
||||
3 => "SodistoreMax",
|
||||
};
|
||||
}
|
||||
|
||||
string GetStatusHtml(int status)
|
||||
{
|
||||
if (status == -1)
|
||||
{
|
||||
return "<span class='offline-symbol' title='Offline'>×</span>";
|
||||
}
|
||||
|
||||
|
||||
var statusClass = $"status-{status}";
|
||||
var title = status switch
|
||||
{
|
||||
0 => "Online",
|
||||
1 => "Warning",
|
||||
2 => "Error",
|
||||
_ => "Unknown"
|
||||
};
|
||||
|
||||
return $"<span class='status-circle {statusClass}' title='{title}'></span>";
|
||||
}
|
||||
|
||||
|
||||
string BuildRowHtml(Installation i) => $@"
|
||||
<tr>
|
||||
<td>{i.Name}</td>
|
||||
<td>{GetProductName(i.Product)}</td>
|
||||
<td>{i.Location}</td>
|
||||
<td>{i.VpnIp}</td>
|
||||
<td>{GetStatusHtml(i.Status)}</td>
|
||||
</tr>
|
||||
";
|
||||
|
||||
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<List<Installation>?> 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<LoginResponse>();
|
||||
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<Installation>();
|
||||
|
||||
var getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=0&authToken={token}");
|
||||
|
||||
var newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync<List<Installation>>();
|
||||
if (newInstallations != null)
|
||||
{
|
||||
installations.AddRange(newInstallations);
|
||||
}
|
||||
|
||||
getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=1&authToken={token}");
|
||||
newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync<List<Installation>>();
|
||||
if (newInstallations != null)
|
||||
{
|
||||
installations.AddRange(newInstallations);
|
||||
}
|
||||
|
||||
getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=2&authToken={token}");
|
||||
newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync<List<Installation>>();
|
||||
if (newInstallations != null)
|
||||
{
|
||||
installations.AddRange(newInstallations);
|
||||
}
|
||||
|
||||
getInstallationsRequestResponse = await http.GetAsync($"GetAllInstallationsFromProduct?product=3&authToken={token}");
|
||||
newInstallations= await getInstallationsRequestResponse.Content.ReadFromJsonAsync<List<Installation>>();
|
||||
if (newInstallations != null)
|
||||
{
|
||||
installations.AddRange(newInstallations);
|
||||
}
|
||||
|
||||
|
||||
//Console.WriteLine("Installations retrieved ",installations);
|
||||
return installations;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.OpenApi" Version="1.6.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue