Fixed a bug that stopped the updating of installations

This commit is contained in:
Kim 2023-05-25 09:18:41 +02:00
parent 5eb373b78e
commit c969b72a82
2 changed files with 71 additions and 29 deletions

View File

@ -1,16 +1,15 @@
using System.Data.Common; using CliWrap;
using System.IdentityModel.Tokens.Jwt; using CliWrap.Buffered;
using System.Web;
using HandlebarsDotNet; using HandlebarsDotNet;
using InnovEnergy.App.RemoteSupportConsole;
using InnovEnergy.App.VrmGrabber.Database; using InnovEnergy.App.VrmGrabber.Database;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using FILE=System.IO.File; using FILE=System.IO.File;
using VrmInstallation = InnovEnergy.Lib.Victron.VictronVRM.Installation; using VrmInstallation = InnovEnergy.Lib.Victron.VictronVRM.Installation;
namespace InnovEnergy.App.VrmGrabber; namespace InnovEnergy.App.VrmGrabber;
public record Install( public record InstallationToHtmlInterface(
String Name, String Name,
String Ip, String Ip,
Int64 Vrm, Int64 Vrm,
@ -26,12 +25,17 @@ public record Install(
[Controller] [Controller]
public class Controller : ControllerBase public class Controller : ControllerBase
{ {
//Todo automatically grab newest version?
private const String FirmwareVersion = "VERSION";
[HttpGet] [HttpGet]
[Route("/")] [Route("/")]
[Produces("text/html")] [Produces("text/html")]
public ActionResult Index() public ActionResult Index()
{ {
String source = @"<head> const String source = @"<head>
<style> <style>
tbody { tbody {
background-color: #e4f0f5; background-color: #e4f0f5;
@ -84,8 +88,9 @@ public class Controller : ControllerBase
</table> </table>
<div id='managerTable'>"; <div id='managerTable'>";
String partialSource =
@"<tr><td>{{Name}}</td>
const String partialSource = @"<tr><td>{{Name}}</td>
<td><a target='_blank' href=http://{{Ip}}>{{online}} {{Ip}}</a></td> <td><a target='_blank' href=http://{{Ip}}>{{online}} {{Ip}}</a></td>
<td><a target='_blank' href=https://vrm.victronenergy.com/installation/{{Vrm}}/dashboard>VRM</a></td> <td><a target='_blank' href=https://vrm.victronenergy.com/installation/{{Vrm}}/dashboard>VRM</a></td>
<td><a target='_blank' href='https://salidomo.innovenergy.ch/d/ENkNRQXmk/installation?refresh=5s&orgId=1&var-Installation={{EscapedName}}&kiosk=tv'>Grafana</a></td> <td><a target='_blank' href='https://salidomo.innovenergy.ch/d/ENkNRQXmk/installation?refresh=5s&orgId=1&var-Installation={{EscapedName}}&kiosk=tv'>Grafana</a></td>
@ -94,11 +99,11 @@ public class Controller : ControllerBase
<td>{{Serial}}</td> <td>{{Serial}}</td>
<td>{{NumBatteries}}</td> <td>{{NumBatteries}}</td>
<td>{{BatteryVersion}}</td> <td>{{BatteryVersion}}</td>
<td><a target='_blank' href=http://{{serverIp}}/UpdateBatteryFirmware/{{Ip}}>⬆️{{firmwareVersion}}</a></td>
</tr>"; </tr>";
var installationsInDb = Db.Installations.ToList();
var instList = Db.Installations.ToList(); if (installationsInDb.Count == 0) return new ContentResult
if (instList.Count == 0) return new ContentResult
{ {
ContentType = "text/html", ContentType = "text/html",
Content = "<p>Please wait page is still loading</p>" Content = "<p>Please wait page is still loading</p>"
@ -106,7 +111,7 @@ public class Controller : ControllerBase
Handlebars.RegisterTemplate("installations", partialSource); Handlebars.RegisterTemplate("installations", partialSource);
var template = Handlebars.Compile(source); var template = Handlebars.Compile(source);
var insts = instList.Select(i => new Install( var installsForHtml = installationsInDb.Select(i => new InstallationToHtmlInterface(
i.Name, i.Name,
i.Ip, i.Ip,
i.Vrm, i.Vrm,
@ -120,7 +125,9 @@ public class Controller : ControllerBase
var data = new var data = new
{ {
inst = insts inst = installsForHtml,
serverIp = "10.2.0.1",
firmwareVersion = FirmwareVersion
}; };
var result = template(data); var result = template(data);
@ -132,6 +139,32 @@ public class Controller : ControllerBase
}; };
} }
[HttpGet(nameof(UpdateBatteryFirmware))]
public async Task<ActionResult> UpdateBatteryFirmware(String installationIp, Int64 numOfBatteries)
{
//We need the DeviceName of the battery (ttyUSB?)
var pathToBattery = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText");
if (pathToBattery == "Failed") return new BadRequestResult();
SendNewBatteryFirmware(installationIp);
for (var batteryId = 0; batteryId < numOfBatteries; batteryId++)
{
var updateCommand = await Db.ExecuteBufferedAsyncCommandOnIp(installationIp, $"/opt/innovenergy/scripts/upload-bms-firmware {pathToBattery} {batteryId} /opt/innovenergy/bms-firmware/{FirmwareVersion}.bin");
if (updateCommand == "Failed") return new BadRequestResult();
}
return new AcceptedResult();
}
private void SendNewBatteryFirmware(String installationIp)
{
Cli.Wrap("scp")
.WithArguments($@"{FirmwareVersion}.bin")
.AppendArgument($@"root@{installationIp}:/opt/innovenergy/bms-firmware/{FirmwareVersion}.bin")
.WithValidation(CommandResultValidation.None).ExecuteBufferedAsync();
}
// [HttpGet(nameof(GetInstallation))] // [HttpGet(nameof(GetInstallation))]
// [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")] // [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
// public Object GetInstallation(UInt64 serialNumber) // public Object GetInstallation(UInt64 serialNumber)

View File

@ -1,5 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using System.Web; using System.Web;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
@ -33,7 +35,7 @@ public static partial class Db
public static Dictionary<Installation, InstallationDetails> InstallationsAndDetails; public static Dictionary<Installation, InstallationDetails> InstallationsAndDetails;
internal const String DbPath = "./db.sqlite"; private const String DbPath = "./db.sqlite";
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath); private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
@ -54,10 +56,10 @@ public static partial class Db
public static async Task UpdateDetailsAndInstallations() public static async Task UpdateDetailsAndInstallations()
{ {
while (true) do {
{
await UpdateInstallationsAndDetailsFromVrm(0); await UpdateInstallationsAndDetailsFromVrm(0);
} }
while (true) ;
} }
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")] [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
@ -70,7 +72,7 @@ public static partial class Db
var installations = await user.GetInstallations(); var installations = await user.GetInstallations();
// var returnDictionary = new Dictionary<VrmInstallation, InstallationDetails>(); // var returnDictionary = new Dictionary<VrmInstallation, InstallationDetails>();
foreach (var installation in installations.Take(70)) //TODO REMOVE TAKE foreach (var installation in installations) //TODO REMOVE TAKE
{ {
Console.WriteLine(installation.Name); Console.WriteLine(installation.Name);
var details = await GetInstallationDetails(installation); var details = await GetInstallationDetails(installation);
@ -85,8 +87,8 @@ public static partial class Db
ip[1], ip[1],
details.Details.Last().Json["timestamp"].ToString(), details.Details.Last().Json["timestamp"].ToString(),
Serialize(details.Details), Serialize(details.Details),
await NumberOfBatteries(ip[0]), await NumberOfBatteries(ip[0], ip[1]),
await BatteryFirmwareVersion(ip[0])); await BatteryFirmwareVersion(ip[0], ip[1]));
if (GetInstallationByIdentifier(installation.Identifier) == null) if (GetInstallationByIdentifier(installation.Identifier) == null)
{ {
@ -99,34 +101,41 @@ public static partial class Db
} }
} }
private static async Task<String> BatteryFirmwareVersion(String? ip) private static async Task<String> BatteryFirmwareVersion(String? ip, String? online)
{ {
if (ip is null or "Unknown") return "Unknown"; if (ip is null or "Unknown" || online == "❌") return "Unknown";
var pathToBattery = await ExecuteBufferedAsyncCommandOnIp(ip, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText"); var pathToBattery = await ExecuteBufferedAsyncCommandOnIp(ip, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText");
if (pathToBattery.StartsWith("Error")) return "Unknown";
var command = $"dbus-send --system --dest={pathToBattery} --type=method_call --print-reply /FirmwareVersion com.victronenergy.BusItem.GetText"; var command = $"dbus-send --system --dest={pathToBattery} --type=method_call --print-reply /FirmwareVersion com.victronenergy.BusItem.GetText";
return await ExecuteBufferedAsyncCommandOnIp(ip, command); //todo fill me return await ExecuteBufferedAsyncCommandOnIp(ip, command);
} }
private static async Task<Int64> NumberOfBatteries(String? ip) private static async Task<Int64> NumberOfBatteries(String? ip, String? online)
{ {
if (ip is null or "Unknown") return 0; if (ip is null or "Unknown" || online == "❌") return 0;
var pathToBattery = await ExecuteBufferedAsyncCommandOnIp(ip, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText"); var pathToBattery = await ExecuteBufferedAsyncCommandOnIp(ip, "dbus-send --system --dest=com.victronenergy.system --type=method_call --print-reply /ServiceMapping/com_victronenergy_battery_1 com.victronenergy.BusItem.GetText");
if (pathToBattery.StartsWith("Error")) return 0;
var cmd = await ExecuteBufferedAsyncCommandOnIp(ip,$"dbus-send --system --dest={pathToBattery} --type=method_call --print-reply /NbOfBatteries com.victronenergy.BusItem.GetText" ); var cmd = await ExecuteBufferedAsyncCommandOnIp(ip,$"dbus-send --system --dest={pathToBattery} --type=method_call --print-reply /NbOfBatteries com.victronenergy.BusItem.GetText" );
return cmd == "Unknown" ? 0 : Int64.Parse(cmd); //No Batteries can be found return cmd == "Failed" ? 0 : Int64.Parse(cmd); //No Batteries can be found
} }
private static async Task<String> ExecuteBufferedAsyncCommandOnIp(String? ip, String command) public static async Task<String> ExecuteBufferedAsyncCommandOnIp(String? ip, String command)
{ {
if (ip is null or "Unknown") return "Unknown"; if (ip is null or "Unknown") return "Failed";
var cmd = await Cli.Wrap("ssh") var cmd = await Cli.Wrap("ssh")
.WithArguments($@"root@{ip}") .WithArguments($@"root@{ip}")
.AppendArgument("-o StrictHostKeyChecking=accept-new") .AppendArgument("-o StrictHostKeyChecking=accept-new")
.AppendArgument(command) .AppendArgument(command)
.WithValidation(CommandResultValidation.None).ExecuteBufferedAsync(); .WithValidation(CommandResultValidation.None).ExecuteBufferedAsync();
return cmd.StandardOutput.Split('"')[1]; return cmd.ExitCode == 0 ? cmd.StandardOutput.Split('"')[1] : cmd.StandardError;
} }
private static String?[] Ip(InstallationDetails details) private static String?[] Ip(InstallationDetails details)