Merge remote-tracking branch 'origin/main'

This commit is contained in:
Kim 2023-10-02 15:40:06 +02:00
commit 965a85ba81
71 changed files with 4085 additions and 427 deletions

View File

@ -15,10 +15,10 @@ public class Installation : TreeNode
public Double Lat { get; set; } public Double Lat { get; set; }
public Double Long { get; set; } public Double Long { get; set; }
public String S3Region { get; set; } = ""; public String S3Region { get; set; } = "";
public String S3Provider { get; set; } = ""; public String S3Provider { get; set; } = "";
public String S3WriteKey { get; set; } = ""; public String S3WriteKey { get; set; } = "";
public String S3Key { get; set; } = ""; public String S3Key { get; set; } = "";
public String S3WriteSecret { get; set; } = ""; public String S3WriteSecret { get; set; } = "";
public String S3Secret { get; set; } = ""; public String S3Secret { get; set; } = "";
} }

View File

@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" /> <PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -134,7 +134,7 @@ public static class Program
var company = x.LastOrDefault()?.Split('(').FirstOrDefault()?.Trim() ?? ""; var company = x.LastOrDefault()?.Split('(').FirstOrDefault()?.Trim() ?? "";
var sn = x.LastOrDefault()?.Split('(').LastOrDefault()?.Trim("() ".ToCharArray()) ?? ""; var sn = x.LastOrDefault()?.Split('(').LastOrDefault()?.Trim("() ".ToCharArray()) ?? "";
var created = i.Created.ToUtcDateTime().ToString("dd.MM.yyyy HH:mm"); var created = i.Created.DateTimeFromUnixTime().ToString("dd.MM.yyyy HH:mm");
var line = new[] var line = new[]
{ {

View File

@ -18,7 +18,6 @@
<ProjectReference Include="../../Lib/Devices/Trumpf/SystemControl/SystemControl.csproj" /> <ProjectReference Include="../../Lib/Devices/Trumpf/SystemControl/SystemControl.csproj" />
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj" /> <ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj" />
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj" /> <ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj" />
<ProjectReference Include="../../Lib/Time/Time.csproj" />
<ProjectReference Include="../../Lib/Units/Units.csproj" /> <ProjectReference Include="../../Lib/Units/Units.csproj" />
<ProjectReference Include="../../Lib/Utils/Utils.csproj" /> <ProjectReference Include="../../Lib/Utils/Utils.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -3,11 +3,10 @@ Description=Battery48TL_TCP_Bridge
[Service] [Service]
Type=simple Type=simple
#ExecStart=socat -v -v TCP-LISTEN:6855,nodelay,fork,reuseaddr GOPEN:/dev/ttyUSB0,raw,b115200
#ExecStart=socat -x -d -d TCP-LISTEN:6855,fork,max-children=1 GOPEN:/dev/ttyUSB0,rawer,b115200,cs8,parenb=1,parodd=1
ExecStart=socat -x -d -d TCP-LISTEN:6855 GOPEN:/dev/ttyUSB0,rawer,b115200,cs8,parenb=1,parodd=1 ExecStart=socat -x -d -d TCP-LISTEN:6855 GOPEN:/dev/ttyUSB0,rawer,b115200,cs8,parenb=1,parodd=1
Restart=always Restart=always
RestartSec=500ms RestartSec=500ms
StartLimitInterval=0
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,4 +1,4 @@
using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Utils;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace InnovEnergy.App.SaliMax; namespace InnovEnergy.App.SaliMax;
@ -37,7 +37,7 @@ public class CustomLogger : ILogger
File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine); File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine);
_CurrentFileSizeBytes += logMessage.Length; _CurrentFileSizeBytes += logMessage.Length;
Console.WriteLine(logMessage); //Console.WriteLine(logMessage);
} }
private void RotateLogFile() private void RotateLogFile()
@ -56,7 +56,7 @@ public class CustomLogger : ILogger
File.Delete(logFiles.First()); File.Delete(logFiles.First());
// Rename the current log file with a timestamp // Rename the current log file with a timestamp
var logFileBackupPath = Path.Combine(logFileDir, $"{logFileBaseName}_{UnixTime.Now}{logFileExt}"); var logFileBackupPath = Path.Combine(logFileDir, $"{logFileBaseName}_{DateTime.Now.ToUnixTime()}{logFileExt}");
File.Move(_LogFilePath, logFileBackupPath); File.Move(_LogFilePath, logFileBackupPath);
} }
} }

View File

@ -6,9 +6,9 @@ public static class Logger
{ {
// Specify the maximum log file size in bytes (e.g., 1 MB) // Specify the maximum log file size in bytes (e.g., 1 MB)
private const Int32 MaxFileSizeBytes = 1024 * 1024; // TODO: move to settings private const Int32 MaxFileSizeBytes = 1024 * 21; // TODO: move to settings
private const Int32 MaxLogFileCount = 1000; // TODO: move to settings private const Int32 MaxLogFileCount = 5000; // TODO: move to settings
private const String LogFilePath = "LogDirectory/log.txt"; // TODO: move to settings private const String LogFilePath = "LogDirectory/log.csv"; // TODO: move to settings
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private static readonly ILogger _logger = new CustomLogger(LogFilePath, MaxFileSizeBytes, MaxLogFileCount); private static readonly ILogger _logger = new CustomLogger(LogFilePath, MaxFileSizeBytes, MaxLogFileCount);

View File

@ -16,18 +16,18 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control;
using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
#pragma warning disable IL2026 #pragma warning disable IL2026
namespace InnovEnergy.App.SaliMax; namespace InnovEnergy.App.SaliMax;
internal static class Program internal static class Program
{ {
private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2); private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
private static readonly IReadOnlyList<Byte> BatteryNodes; private static readonly IReadOnlyList<Byte> BatteryNodes;
@ -148,9 +148,9 @@ internal static class Program
while (true) while (true)
{ {
await Observable await Observable
.Interval(UpdateInterval.ToTimeSpan()) .Interval(UpdateInterval)
.Select(_ => RunIteration()) .Select(_ => RunIteration())
.SelectMany(r => UploadCsv(r, UnixTime.Now.RoundTo(UpdateInterval))) .SelectMany(r => UploadCsv(r, DateTime.UtcNow))
.SelectError() .SelectError()
.ToTask(); .ToTask();
} }
@ -172,13 +172,14 @@ internal static class Program
var record = ReadStatus(); var record = ReadStatus();
// If control Special Error return true, we must stop the system.(break;) // If control Special Error return true, we must stop the system.(break;)
var specialErrorOccured = record.ControlSpecialError(); var alarmCondition = record.DetectAlarmStates();
if (specialErrorOccured.Item1)
if (alarmCondition is not null)
{ {
//stop the system //stop the system
//return //return
specialErrorOccured.Item2.WriteLine(); alarmCondition.LogInfo();
} }
record.ControlConstants(); record.ControlConstants();
@ -186,7 +187,7 @@ internal static class Program
$"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo(); $"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo();
var essControl = record.ControlEss().LogInfo(); var essControl = record.ControlEss().WriteLine().LogInfo();
record.EssControl = essControl; record.EssControl = essControl;
@ -212,36 +213,13 @@ internal static class Program
// ReSharper disable once FunctionNeverReturns // ReSharper disable once FunctionNeverReturns
} }
private static (Boolean, String) ControlSpecialError (this StatusRecord r) // to find a better name private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch
{ {
// Those errors must not occurs. Stop the system in case this is happen. { K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ",
var isSpecialErrorOccured = false; { K1GridBusIsConnectedToGrid : false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: K1 is open but the K2 is still close ",
var errorMessage = "No Error"; { FiError: true, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: Fi error occured but the K2 is still close ",
_ => null
// 1. relay0 is open and K2 Close. };
if (r.Relays is { K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } )
{
isSpecialErrorOccured = true;
errorMessage = " Contradiction: R0 is opening the K2 but the K2 is still close ";
}
// 2. If K1 is open, K2 must be open .
if (r.Relays is { K1GridBusIsConnectedToGrid: false, K2IslandBusIsConnectedToGridBus: true } )
{
isSpecialErrorOccured = true;
errorMessage = " Contradiction: K1 is open but the K2 is still close ";
}
// 3. If FI error is occured, K2 must be open.
if (r.Relays is { FiError: true, K2IslandBusIsConnectedToGridBus: true } )
{
isSpecialErrorOccured = true;
errorMessage = " Contradiction: Fi error occured but the K2 is still close ";
}
return (isSpecialErrorOccured, errorMessage);
}
private static void ControlConstants(this StatusRecord r) private static void ControlConstants(this StatusRecord r)
{ {
@ -335,7 +313,7 @@ internal static class Program
sc.ResetAlarmsAndWarnings = true; sc.ResetAlarmsAndWarnings = true;
} }
private static async Task<Boolean> UploadCsv(StatusRecord status, UnixTime timeStamp) private static async Task<Boolean> UploadCsv(StatusRecord status, DateTime timeStamp)
{ {
var s3Config = status.Config.S3; var s3Config = status.Config.S3;
if (s3Config is null) if (s3Config is null)

View File

@ -2,7 +2,7 @@ namespace InnovEnergy.App.SaliMax.SystemConfig;
public class AcDcConfig public class AcDcConfig
{ {
public required Double MaxDcLinkVoltage { get; set; } public required Double MaxDcLinkVoltage { get; init; }
public required Double MinDcLinkVoltage { get; set; } public required Double MinDcLinkVoltage { get; init; }
public required Double ReferenceDcLinkVoltage { get; set; } public required Double ReferenceDcLinkVoltage { get; init; }
} }

View File

@ -1,5 +1,4 @@
using System.Text.Json; using System.Text.Json;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using static System.Text.Json.JsonSerializer; using static System.Text.Json.JsonSerializer;
@ -27,7 +26,6 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
public required Double MaxBatteryDischargingCurrent { get; set; } public required Double MaxBatteryDischargingCurrent { get; set; }
public required Double MaxDcPower { get; set; } public required Double MaxDcPower { get; set; }
public required Double MaxChargeBatteryVoltage { get; set; } public required Double MaxChargeBatteryVoltage { get; set; }
public required Double MinDischargeBatteryVoltage { get; set; } public required Double MinDischargeBatteryVoltage { get; set; }
@ -49,16 +47,16 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
{ {
AcDc = new () AcDc = new ()
{ {
MinDcLinkVoltage = 690, MinDcLinkVoltage = 690,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
MaxDcLinkVoltage = 810, MaxDcLinkVoltage = 810,
}, },
DcDc = new () DcDc = new ()
{ {
UpperDcLinkVoltage = 50, UpperDcLinkVoltage = 50,
LowerDcLinkVoltage = 50, LowerDcLinkVoltage = 50,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
}, },
}, },
@ -66,25 +64,26 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
{ {
AcDc = new () AcDc = new ()
{ {
MinDcLinkVoltage = 720, MinDcLinkVoltage = 720,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
MaxDcLinkVoltage = 810, MaxDcLinkVoltage = 810,
}, },
DcDc = new () DcDc = new ()
{ {
UpperDcLinkVoltage = 50, UpperDcLinkVoltage = 50,
LowerDcLinkVoltage = 50, LowerDcLinkVoltage = 50,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
}, },
}, },
MaxBatteryChargingCurrent = 210, MaxBatteryChargingCurrent = 210,
MaxBatteryDischargingCurrent = 210, MaxBatteryDischargingCurrent = 210,
MaxDcPower = 10000, MaxDcPower = 10000,
MaxChargeBatteryVoltage = 57,
MinDischargeBatteryVoltage = 0,
MaxChargeBatteryVoltage = 57,
MinDischargeBatteryVoltage = 0,
Devices = new () Devices = new ()
{ {
TruConvertAcIp = new() { Host = "localhost", Port = 5001}, TruConvertAcIp = new() { Host = "localhost", Port = 5001},
@ -111,16 +110,16 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
{ {
AcDc = new () AcDc = new ()
{ {
MinDcLinkVoltage = 690, MinDcLinkVoltage = 690,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
MaxDcLinkVoltage = 810, MaxDcLinkVoltage = 810,
}, },
DcDc = new () DcDc = new ()
{ {
UpperDcLinkVoltage = 50, UpperDcLinkVoltage = 50,
LowerDcLinkVoltage = 50, LowerDcLinkVoltage = 50,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
}, },
}, },
@ -128,26 +127,26 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
{ {
AcDc = new () AcDc = new ()
{ {
MinDcLinkVoltage = 720, MinDcLinkVoltage = 720,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
MaxDcLinkVoltage = 780, MaxDcLinkVoltage = 780,
}, },
DcDc = new () DcDc = new ()
{ {
UpperDcLinkVoltage = 20, UpperDcLinkVoltage = 20,
LowerDcLinkVoltage = 20, LowerDcLinkVoltage = 20,
ReferenceDcLinkVoltage = 750, ReferenceDcLinkVoltage = 750,
}, },
}, },
MaxBatteryChargingCurrent = 210, MaxBatteryChargingCurrent = 210,
MaxBatteryDischargingCurrent = 210, MaxBatteryDischargingCurrent = 210,
MaxDcPower = 10000, MaxDcPower = 10000,
MaxChargeBatteryVoltage = 57, MaxChargeBatteryVoltage = 57,
MinDischargeBatteryVoltage = 0, MinDischargeBatteryVoltage = 0,
S3 = new() S3 = new()
{ {
Bucket = "saliomameiringen", Bucket = "saliomameiringen",

View File

@ -2,7 +2,7 @@ namespace InnovEnergy.App.SaliMax.SystemConfig;
public class DcDcConfig public class DcDcConfig
{ {
public required Double LowerDcLinkVoltage { get; set; } public required Double LowerDcLinkVoltage { get; init; }
public required Double ReferenceDcLinkVoltage { get; set; } public required Double ReferenceDcLinkVoltage { get; init; }
public required Double UpperDcLinkVoltage { get; set; } public required Double UpperDcLinkVoltage { get; init; }
} }

View File

@ -2,6 +2,6 @@ namespace InnovEnergy.App.SaliMax.SystemConfig;
public class DevicesConfig public class DevicesConfig
{ {
public required AcDcConfig AcDc { get; set; } public required AcDcConfig AcDc { get; init; }
public required DcDcConfig DcDc { get; set; } public required DcDcConfig DcDc { get; init; }
} }

View File

@ -72,7 +72,7 @@ public static class Topology
var h = g; var h = g;
var i = status.PvOnDc?.Dc.Power.Value; var i = status.PvOnDc?.Dc.Power.Value;
var k = status.DcDc.Dc.Link.Power.Value; var k = status.DcDc.Dc.Link.Power.Value;
var l = k; var l = status.Battery is not null ? status.Battery.Dc.Power.Value : 0;
var j = status.LoadOnDc?.Power.Value; var j = status.LoadOnDc?.Power.Value;
var d = status.AcGridToAcIsland?.Power.Active; var d = status.AcGridToAcIsland?.Power.Active;
var c = status.LoadOnAcGrid?.Power.Active; var c = status.LoadOnAcGrid?.Power.Active;
@ -260,9 +260,9 @@ public static class Topology
.Apply(TextBlock.AlignLeft) .Apply(TextBlock.AlignLeft)
.TitleBox("AC/DC"); .TitleBox("AC/DC");
var gridFlow = Flow.Horizontal(h); var dcFlow = Flow.Horizontal(h);
return TextBlock.AlignCenterVertical(inverterBox, gridFlow); return TextBlock.AlignCenterVertical(inverterBox, dcFlow);
} }
@ -285,7 +285,7 @@ public static class Topology
return Switch.Open("K3"); return Switch.Open("K3");
} }
private static TextBlock CreateDcDcColumn(this StatusRecord status, ActivePower? l) private static TextBlock CreateDcDcColumn(this StatusRecord status, ActivePower? p)
{ {
var dc48Voltage = status.DcDc.Dc.Battery.Voltage.ToDisplayString(); var dc48Voltage = status.DcDc.Dc.Battery.Voltage.ToDisplayString();
@ -293,7 +293,7 @@ public static class Topology
.AlignLeft(dc48Voltage) .AlignLeft(dc48Voltage)
.TitleBox("DC/DC"); .TitleBox("DC/DC");
var busFlow = Flow.Horizontal(l); var busFlow = Flow.Horizontal(p);
return TextBlock.AlignCenterVertical(busBox, busFlow); return TextBlock.AlignCenterVertical(busBox, busFlow);
} }
@ -371,6 +371,7 @@ public static class Topology
return batteryAvgBox; // TODO: individualBatteries hidden atm return batteryAvgBox; // TODO: individualBatteries hidden atm
#pragma warning disable CS0162
var batteryBoxes = bat var batteryBoxes = bat
.Devices .Devices
.Select(CreateBatteryBox) .Select(CreateBatteryBox)
@ -386,6 +387,8 @@ public static class Topology
batteryAvgBox , batteryAvgBox ,
individualBatteries individualBatteries
); );
#pragma warning enable CS0162
} }
private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords? bat) private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords? bat)
@ -394,11 +397,12 @@ public static class Topology
return TextBlock.AlignLeft("no battery").Box(); return TextBlock.AlignLeft("no battery").Box();
var voltage = bat.Dc.Voltage.ToDisplayString(); var voltage = bat.Dc.Voltage.ToDisplayString();
var soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; var soc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; // TODO
var current = bat.Dc.Current.ToDisplayString(); var current = bat.Dc.Current.ToDisplayString();
var busCurrent = bat.Devices.Any() ? bat.Devices.Sum(b => b.BusCurrent).A().ToDisplayString() : "0";
var temp = bat.Temperature.ToDisplayString(); var temp = bat.Temperature.ToDisplayString();
var heatingPower = bat.HeatingPower.ToDisplayString(); var heatingPower = bat.HeatingPower.ToDisplayString();
var alarms = bat.Alarms.Count + " Alarms"; var alarms = bat.Alarms.Count + " Alarms";
var warnings = bat.Warnings.Count + " Warnings"; var warnings = bat.Warnings.Count + " Warnings";
var nBatteries = bat.Devices.Count; var nBatteries = bat.Devices.Count;
@ -408,6 +412,7 @@ public static class Topology
voltage, voltage,
soc, soc,
current, current,
busCurrent,
temp, temp,
heatingPower, heatingPower,
warnings, warnings,
@ -447,7 +452,7 @@ public static class Topology
battery.Soc.ToDisplayString(), battery.Soc.ToDisplayString(),
battery.Dc.Current.ToDisplayString() + " C/D", battery.Dc.Current.ToDisplayString() + " C/D",
battery.Temperatures.Cells.Average.ToDisplayString(), battery.Temperatures.Cells.Average.ToDisplayString(),
battery.BusCurrent.ToDisplayString() + " T", battery.BusCurrent.ToDisplayString() ,
batteryWarnings, batteryWarnings,
batteryAlarms, batteryAlarms,
battery.HeatingCurrent.ToDisplayString() + " H" battery.HeatingCurrent.ToDisplayString() + " H"

View File

@ -1,12 +1,8 @@
using CliWrap; using CliWrap;
using CliWrap.Buffered;
using HandlebarsDotNet; using HandlebarsDotNet;
using InnovEnergy.App.VrmGrabber.Database; using InnovEnergy.App.VrmGrabber.Database;
using InnovEnergy.App.VrmGrabber.DataTypes;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VictronVRM;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
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;

View File

@ -1,4 +1,3 @@
using InnovEnergy.Lib.Victron.VictronVRM;
using SQLite; using SQLite;
namespace InnovEnergy.App.VrmGrabber.DataTypes; namespace InnovEnergy.App.VrmGrabber.DataTypes;

View File

@ -1,6 +1,5 @@
using InnovEnergy.App.VrmGrabber.DataTypes; using InnovEnergy.App.VrmGrabber.DataTypes;
namespace InnovEnergy.App.VrmGrabber.Database; namespace InnovEnergy.App.VrmGrabber.Database;

View File

@ -7,7 +7,6 @@ using CliWrap.Buffered;
using InnovEnergy.App.RemoteSupportConsole; using InnovEnergy.App.RemoteSupportConsole;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Victron.VictronVRM; using InnovEnergy.Lib.Victron.VictronVRM;
using Newtonsoft.Json;
using SQLite; using SQLite;
using static System.Text.Json.JsonSerializer; using static System.Text.Json.JsonSerializer;
using static InnovEnergy.App.VrmGrabber.Database.Systemd; using static InnovEnergy.App.VrmGrabber.Database.Systemd;

View File

@ -1,6 +1,5 @@
using InnovEnergy.App.VrmGrabber.DataTypes; using InnovEnergy.App.VrmGrabber.DataTypes;
namespace InnovEnergy.App.VrmGrabber.Database; namespace InnovEnergy.App.VrmGrabber.Database;

View File

@ -1,8 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="../InnovEnergy.App.props" />
<PropertyGroup> <PropertyGroup>
<PublishTrimmed>false</PublishTrimmed> <PublishTrimmed>false</PublishTrimmed>
<RootNamespace>InnovEnergy.App.VrmGrabber</RootNamespace>
</PropertyGroup> </PropertyGroup>
<Import Project="../InnovEnergy.App.props" />
<ItemGroup> <ItemGroup>
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
@ -30,12 +33,12 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Lib/Utils/Utils.csproj" /> <ProjectReference Include="../../Lib/Utils/Utils.csproj" />
<ProjectReference Include="..\..\Lib\Victron\VictronVRM\VictronVRM.csproj" /> <ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
<ProjectReference Include="..\RemoteSupportConsole\RemoteSupportConsole.csproj" /> <ProjectReference Include="../RemoteSupportConsole/RemoteSupportConsole.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<_ContentIncludedByDefault Remove="wwwroot\index.html" /> <_ContentIncludedByDefault Remove="wwwroot/index.html" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -10,8 +10,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SysTools", "Lib/SysTools/Sy
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebServer", "Lib/WebServer/WebServer.csproj", "{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebServer", "Lib/WebServer/WebServer.csproj", "{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Time", "Lib/Time/Time.csproj", "{442A8366-C177-48FE-84A7-BDF6470A09FF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeterDriver", "App/EmuMeterDriver/EmuMeterDriver.csproj", "{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeterDriver", "App/EmuMeterDriver/EmuMeterDriver.csproj", "{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BmsTunnel", "App/BmsTunnel/BmsTunnel.csproj", "{40B45363-BE34-420B-8F87-775EE6EE3513}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BmsTunnel", "App/BmsTunnel/BmsTunnel.csproj", "{40B45363-BE34-420B-8F87-775EE6EE3513}"
@ -81,6 +79,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3Explorer", "App\S3Explore
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VrmGrabber", "App\VrmGrabber\VrmGrabber.csproj", "{88633C71-D701-49B3-A6DE-9D7CED9046E3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VrmGrabber", "App\VrmGrabber\VrmGrabber.csproj", "{88633C71-D701-49B3-A6DE-9D7CED9046E3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3Utils", "Lib\S3Utils\S3Utils.csproj", "{E3914C76-808C-48E0-B5EC-FB24DD999CCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mailer", "Lib\Mailer\Mailer.csproj", "{73B97F6E-2BDC-40DA-84A7-7FB0264387D6}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mailer", "Lib\Mailer\Mailer.csproj", "{73B97F6E-2BDC-40DA-84A7-7FB0264387D6}"
EndProject EndProject
@ -110,10 +110,6 @@ Global
{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Release|Any CPU.Build.0 = Release|Any CPU {B2627B9F-41DF-44F7-A0D1-CA71FF4A007A}.Release|Any CPU.Build.0 = Release|Any CPU
{442A8366-C177-48FE-84A7-BDF6470A09FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{442A8366-C177-48FE-84A7-BDF6470A09FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{442A8366-C177-48FE-84A7-BDF6470A09FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{442A8366-C177-48FE-84A7-BDF6470A09FF}.Release|Any CPU.Build.0 = Release|Any CPU
{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -210,6 +206,10 @@ Global
{88633C71-D701-49B3-A6DE-9D7CED9046E3}.Debug|Any CPU.Build.0 = Debug|Any CPU {88633C71-D701-49B3-A6DE-9D7CED9046E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88633C71-D701-49B3-A6DE-9D7CED9046E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {88633C71-D701-49B3-A6DE-9D7CED9046E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88633C71-D701-49B3-A6DE-9D7CED9046E3}.Release|Any CPU.Build.0 = Release|Any CPU {88633C71-D701-49B3-A6DE-9D7CED9046E3}.Release|Any CPU.Build.0 = Release|Any CPU
{E3914C76-808C-48E0-B5EC-FB24DD999CCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3914C76-808C-48E0-B5EC-FB24DD999CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3914C76-808C-48E0-B5EC-FB24DD999CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3914C76-808C-48E0-B5EC-FB24DD999CCC}.Release|Any CPU.Build.0 = Release|Any CPU
{73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Debug|Any CPU.Build.0 = Debug|Any CPU {73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Release|Any CPU.ActiveCfg = Release|Any CPU {73B97F6E-2BDC-40DA-84A7-7FB0264387D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -218,7 +218,6 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A} {CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{B1268C03-66EB-4486-8BFC-B439225D9D54} = {145597B4-3E30-45E6-9F72-4DD43194539A} {B1268C03-66EB-4486-8BFC-B439225D9D54} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{442A8366-C177-48FE-84A7-BDF6470A09FF} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{40B45363-BE34-420B-8F87-775EE6EE3513} = {145597B4-3E30-45E6-9F72-4DD43194539A} {40B45363-BE34-420B-8F87-775EE6EE3513} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{B2627B9F-41DF-44F7-A0D1-CA71FF4A007A} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854} {B2627B9F-41DF-44F7-A0D1-CA71FF4A007A} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7} = {145597B4-3E30-45E6-9F72-4DD43194539A} {F65F33B0-3522-4008-8D1E-47EF8E4C7AC7} = {145597B4-3E30-45E6-9F72-4DD43194539A}
@ -250,6 +249,7 @@ Global
{1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} {1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183}
{EB56EF94-D8A7-4111-A8E7-A87EF13596DA} = {145597B4-3E30-45E6-9F72-4DD43194539A} {EB56EF94-D8A7-4111-A8E7-A87EF13596DA} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{88633C71-D701-49B3-A6DE-9D7CED9046E3} = {145597B4-3E30-45E6-9F72-4DD43194539A} {88633C71-D701-49B3-A6DE-9D7CED9046E3} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{E3914C76-808C-48E0-B5EC-FB24DD999CCC} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{73B97F6E-2BDC-40DA-84A7-7FB0264387D6} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854} {73B97F6E-2BDC-40DA-84A7-7FB0264387D6} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -20,7 +20,7 @@ public partial class Battery48TlRecord
public Boolean Eoc => Leds is { Green: On, Amber: Off, Blue : Off }; public Boolean Eoc => Leds is { Green: On, Amber: Off, Blue : Off };
public String SerialNumber => $"{_SerialNum1:D4}{_SerialNum2:D4}{_SerialNum3:D4}{_SerialNum4:D4}".TrimStart('0'); public String SerialNumber => $"{_SerialNum1:X4} {_SerialNum2:X4} {_SerialNum3:X4} {_SerialNum4:X4}".TrimStart('0');
public String FwVersion => _FwVersion.ToString("X4"); public String FwVersion => _FwVersion.ToString("X4");
public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList(); public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList();

View File

@ -71,8 +71,8 @@ public static class TypeToSignature
private static Signature? ParseTerminalSignature(Type type) private static Signature? ParseTerminalSignature(Type type)
{ {
return Signature return Signature
.Terminals .Terminals
.FirstOrDefault(s => s.Type.IsAssignableFrom(type)); .FirstOrDefault(s => s.Type.IsAssignableFrom(type));
} }
private static IReadOnlyList<Type> GetStructElements(Type type) private static IReadOnlyList<Type> GetStructElements(Type type)

View File

@ -16,7 +16,7 @@ public class ArraySignature : ContainerTypeSignature
var list = (IList) Activator.CreateInstance(listType)!; var list = (IList) Activator.CreateInstance(listType)!;
var length = reader.ReadInt32(); var length = reader.ReadInt32();
var end = reader.BytesRead + length; var end = reader.BytesRead + length;
while (reader.BytesRead < end) while (reader.BytesRead < end)
{ {
//reader.AlignForComposite(); // NOT ALIGNED! //reader.AlignForComposite(); // NOT ALIGNED!

View File

@ -2,7 +2,4 @@ namespace InnovEnergy.Lib.Protocols.DBus.Protocol.DataTypes.Signatures.Specializ
public abstract class BasicTypeSignature : Signature public abstract class BasicTypeSignature : Signature
{ {
} }

View File

@ -176,11 +176,11 @@ public readonly struct Message
var raw = reader.ReadSegment((Int32)payloadLength); var raw = reader.ReadSegment((Int32)payloadLength);
var br = new DBusBufferReader(raw, reader.SwapEndian);
if (signature.IsEmpty) if (signature.IsEmpty)
return (raw.ToArray(), true); // no signature, but data available. should probably not happen. return (raw.ToArray(), true); // no signature, but data available. should probably not happen.
var br = new DBusBufferReader(raw, reader.SwapEndian);
// try // try
// { // {
return (signature.Read(br), false); return (signature.Read(br), false);

View File

@ -5,7 +5,7 @@ using InnovEnergy.Lib.Protocols.DBus.Protocol.Header;
namespace InnovEnergy.Lib.Protocols.DBus.Protocol; namespace InnovEnergy.Lib.Protocols.DBus.Protocol;
using HeaderType = ValueTuple<Byte, Byte, Byte, Byte, UInt32, UInt32, IReadOnlyList<(Byte, Variant)>>; using HeaderType = ValueTuple<Byte, Byte, Byte, Byte, UInt32, UInt32, IReadOnlyList<(Byte, Variant)>>;
public static class Replies public static class Replies
{ {
@ -53,11 +53,11 @@ public static class Replies
HeaderType header = HeaderType header =
( (
(Byte)Env.Endianness, (Byte) Env.Endianness,
(Byte)MessageType.MethodReturn, (Byte) MessageType.MethodReturn,
(Byte)HeaderFlags.NoReplyExpected, (Byte) HeaderFlags.NoReplyExpected,
Message.ProtocolVersion, Message.ProtocolVersion,
(UInt32)signature.MeasureSize(payload), (UInt32) signature.MeasureSize(payload),
SerialSource.Next(), SerialSource.Next(),
fields fields
); );

View File

@ -0,0 +1,6 @@
namespace InnovEnergy.Lib.S3Utils.Data;
public record S3Bucket : S3RegionCredentials
{
public required String Bucket { get; init; }
}

View File

@ -0,0 +1,47 @@
using static System.Environment;
namespace InnovEnergy.Lib.S3Utils.Data;
public record S3Credentials
{
protected static readonly String S3CfgFile = GetFolderPath(SpecialFolder.UserProfile).TrimEnd('\\', '/') + "/.s3cfg";
public required String Key { get; init; }
public required String Secret { get; init; }
public static S3Credentials? FromS3Cfg() => FromFile(S3CfgFile);
public static S3Credentials? FromFile(String file)
{
// [default]
// host_base = sos-ch-dk-2.exo.io
// host_bucket = %(bucket)s.sos-ch-dk-2.exo.io
// access_key = xxxxxxxxxxxxxxx
// secret_key = xxxxxxxxxxxxxxx
// use_https = True
try
{
var cfg = ParseFile(file);
return new S3Credentials
{
Key = cfg["access_key"],
Secret = cfg["secret_key"]
};
}
catch
{
return null;
}
}
protected static Dictionary<String, String> ParseFile(String cfgFile)
{
return File
.ReadAllLines(cfgFile)
.Where(l => l.Contains("="))
.Select(l => l.Split("="))
.ToDictionary(l => l[0].Trim(), l => l[1].Trim());
}
}

View File

@ -0,0 +1,6 @@
namespace InnovEnergy.Lib.S3Utils.Data;
public record S3Path : S3Bucket
{
public required String Path { get; init; }
}

View File

@ -0,0 +1,49 @@
using Amazon.Runtime;
using Amazon.S3;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.S3Utils.Data;
public record S3RegionCredentials : S3Credentials
{
public required String Region { get; init; }
private AmazonS3Client? _Client;
internal AmazonS3Client Client => _Client ??= new AmazonS3Client
(
credentials: new BasicAWSCredentials(Key, Secret),
clientConfig: new()
{
ServiceURL = Region.EnsureStartsWith("https://"),
ForcePathStyle = true,
}
);
public new static S3RegionCredentials? FromS3Cfg() => FromFile(S3CfgFile);
public new static S3RegionCredentials? FromFile(String file)
{
// [default]
// host_base = sos-ch-dk-2.exo.io
// host_bucket = %(bucket)s.sos-ch-dk-2.exo.io
// access_key = xxxxxxxxxxxxxxx
// secret_key = xxxxxxxxxxxxxxx
// use_https = True
try
{
var cfg = ParseFile(file);
return new S3RegionCredentials
{
Key = cfg["access_key"],
Secret = cfg["secret_key"],
Region = cfg["host_base"],
};
}
catch
{
return null;
}
}
}

View File

@ -0,0 +1,21 @@
using InnovEnergy.Lib.S3Utils.Data;
namespace InnovEnergy.Lib.S3Utils.ExoScale;
public static class DefaultCredentials
{
public static S3Credentials ReadOnly => new S3Credentials
{
Key = "EXOb6d6dc1880cdd51f1ebc6692",
Secret = "kpIey4QJlQFuWG_WoTazcY7kBEjN2f_ll2cDBeg64m4",
};
public static S3Credentials ReadWrite => new S3Credentials
{
Key = "EXO87ca85e29dd412f1238f1cf0",
Secret = "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU",
};
}

View File

@ -0,0 +1,6 @@
namespace InnovEnergy.Lib.S3Utils.ExoScale;
public static class Regions
{
public static String Default => "sos-ch-dk-2.exo.io";
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" ?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

View File

@ -0,0 +1,4 @@
{
"Key": "EXO1abcb772bf43ab72951ba1dc",
"Secret": "_ym1KsGBSp90S5dwhZn18XD-u9Y4ghHvyIxg5gv5fHw"
}

View File

@ -0,0 +1,5 @@
{
"Key": "EXOb6d6dc1880cdd51f1ebc6692",
"Secret": "kpIey4QJlQFuWG_WoTazcY7kBEjN2f_ll2cDBeg64m4",
"Region": "sos-ch-dk-2.exo.io"
}

View File

@ -0,0 +1,5 @@
{
"Key": "EXO87ca85e29dd412f1238f1cf0",
"Secret": "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU",
"Region": "sos-ch-dk-2.exo.io"
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
{
"Url": "mail.agenturserver.de",
"Port": 587,
"Username": "p518526p69",
"Password": "i;b*xqm4iB5uhl"
}

View File

@ -0,0 +1,6 @@
{
"ReadOnlyS3Key": "EXO44d2979c8e570eae81ead564",
"ReadOnlyS3Secret": "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU" ,
"ReadWriteS3Key": "EXO87ca85e29dd412f1238f1cf0",
"ReadWriteS3Secret": "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"
}

90
csharp/Lib/S3Utils/S3.cs Normal file
View File

@ -0,0 +1,90 @@
using Amazon.S3.Model;
using InnovEnergy.Lib.S3Utils.Data;
using InnovEnergy.Lib.Utils;
using S3Bucket = InnovEnergy.Lib.S3Utils.Data.S3Bucket;
namespace InnovEnergy.Lib.S3Utils;
public static class S3
{
public static S3RegionCredentials Region(this S3Credentials credentials, String region) => new()
{
Secret = credentials.Secret,
Key = credentials.Key,
Region = region,
};
public static S3Bucket Bucket(this S3RegionCredentials region, String bucket) => new()
{
Region = region.Region,
Secret = region.Secret,
Key = region.Key,
Bucket = bucket,
};
public static S3Path Path(this S3Bucket bucket, String path) => new()
{
Bucket = bucket.Bucket,
Region = bucket.Region,
Secret = bucket.Secret,
Key = bucket.Key,
Path = path
};
public static IAsyncEnumerable<S3Path> ListObjects(this S3Bucket bucketOrPrefixPath)
{
var path = bucketOrPrefixPath as S3Path ?? bucketOrPrefixPath.Path(null!);
var request = new ListObjectsV2Request
{
BucketName = path.Bucket,
Prefix = path.Path
};
return bucketOrPrefixPath
.Client
.Paginators
.ListObjectsV2(request)
.Responses
.SelectMany(r => r.S3Objects)
.Select(o => path with { Path = o.Key });
}
public static async Task<String> GetObject(this S3Path path)
{
var request = new GetObjectRequest
{
BucketName = path.Bucket,
Key = path.Path
};
using var response = await path.Client.GetObjectAsync(request);
await using var responseStream = response.ResponseStream;
using var reader = new StreamReader(responseStream);
return await reader.ReadToEndAsync();
}
public static async IAsyncEnumerable<String> GetObjectLineByLine(this S3Path path)
{
var request = new GetObjectRequest
{
BucketName = path.Bucket,
Key = path.Path
};
using var response = await path.Client.GetObjectAsync(request);
await using var responseStream = response.ResponseStream;
using var reader = new StreamReader(responseStream);
while (true)
{
var line = await reader.ReadLineAsync();
if (line is not null)
yield return line;
else
yield break;
}
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../InnovEnergy.Lib.props" />
<PropertyGroup>
<RootNamespace>InnovEnergy.Lib.S3Utils</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Utils/Utils.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.203.12" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>
</Project>

View File

@ -9,4 +9,8 @@
<RootNamespace>InnovEnergy.Lib.Time</RootNamespace> <RootNamespace>InnovEnergy.Lib.Time</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -1,7 +1,7 @@
namespace InnovEnergy.Lib.Time.Unix; // namespace InnovEnergy.Lib.Time.Unix;
//
public readonly partial struct UnixTime : IComparable<UnixTime>, IEquatable<UnixTime> // public readonly partial struct UnixTime : IComparable<UnixTime>, IEquatable<UnixTime>
{ // {
Int32 IComparable<UnixTime>.CompareTo(UnixTime other) => Ticks.CompareTo(other.Ticks); // Int32 IComparable<UnixTime>.CompareTo(UnixTime other) => Ticks.CompareTo(other.Ticks);
Boolean IEquatable<UnixTime>.Equals(UnixTime other) => Ticks == other.Ticks; // Boolean IEquatable<UnixTime>.Equals(UnixTime other) => Ticks == other.Ticks;
} // }

View File

@ -1,20 +1,19 @@
namespace InnovEnergy.Lib.Time.Unix; // namespace InnovEnergy.Lib.Time.Unix;
//
public readonly partial struct UnixTime // public readonly partial struct UnixTime
{ // {
private UnixTime(UInt32 ticks) => Ticks = ticks; // public static UnixTime FromTicks(Int32 ticks) => new UnixTime { Ticks = ticks };
//
public static UnixTime FromTicks(UInt32 ticks) => new UnixTime(ticks); // public static UnixTime FromUtcDateTime(DateTime dateTime)
public static UnixTime FromTicks(Int32 ticks) => new UnixTime((UInt32) ticks); // {
// var timeSinceEpoch = dateTime - DateTime.UnixEpoch;
public static UnixTime FromUtcDateTime(DateTime dateTime) // var ticks = (Int32) timeSinceEpoch.TotalSeconds;
{ //
var timeSinceEpoch = dateTime - DateTime.UnixEpoch; // return new UnixTime{ Ticks = ticks};
var ticks = (UInt32) timeSinceEpoch.TotalSeconds; // }
//
return new UnixTime(ticks); // public static UnixTime Epoch => new UnixTime { Ticks = 0 };
} // public static UnixTime Now => FromUtcDateTime(DateTime.UtcNow);
//
public static UnixTime Epoch => new UnixTime(0); //
public static UnixTime Now => FromUtcDateTime(DateTime.UtcNow); // }
}

View File

@ -1,9 +1,9 @@
namespace InnovEnergy.Lib.Time.Unix; // namespace InnovEnergy.Lib.Time.Unix;
//
public readonly partial struct UnixTime // public readonly partial struct UnixTime
{ // {
public DateTime ToUtcDateTime() => DateTime.UnixEpoch + TimeSpan.FromSeconds(Ticks); // public DateTime ToUtcDateTime() => DateTime.UnixEpoch + TimeSpan.FromSeconds(Ticks);
//
public static implicit operator DateTime(UnixTime unixTimeSpan) => unixTimeSpan.ToUtcDateTime(); // public static implicit operator DateTime(UnixTime unixTimeSpan) => unixTimeSpan.ToUtcDateTime();
public static implicit operator UnixTime(DateTime dateTime) => FromUtcDateTime(dateTime); // public static implicit operator UnixTime(DateTime dateTime) => FromUtcDateTime(dateTime);
} // }

View File

@ -1,20 +1,27 @@
namespace InnovEnergy.Lib.Time.Unix; // using InnovEnergy.Lib.Utils;
//
public readonly partial struct UnixTime // namespace InnovEnergy.Lib.Time.Unix;
{ //
public static UnixTime operator -(UnixTime a, UnixTimeSpan b) => FromTicks(a.Ticks - b.Ticks); // public readonly partial struct UnixTime
public static UnixTime operator +(UnixTime a, UnixTimeSpan b) => FromTicks(a.Ticks + b.Ticks); // {
public static UnixTime operator +(UnixTimeSpan a, UnixTime b) => FromTicks(a.Ticks + b.Ticks); // public static UnixTime operator -(UnixTime a, TimeSpan b) => FromTicks(a.Ticks - GetUnixTicks(b));
// public static UnixTime operator +(UnixTime a, TimeSpan b) => FromTicks(a.Ticks + GetUnixTicks(b));
public static UInt32 operator /(UnixTime a, UnixTimeSpan b) => a.Ticks / b.Ticks; // public static UnixTime operator +(TimeSpan a, UnixTime b) => FromTicks(GetUnixTicks(a) + b.Ticks);
public static UInt32 operator %(UnixTime a, UnixTimeSpan b) => a.Ticks % b.Ticks; //
// public static Double operator /(UnixTime a, TimeSpan b) => a.Ticks.ConvertTo<Double>() / b.Ticks;
public static Boolean operator < (UnixTime l, UnixTime r) => l.Ticks < r.Ticks; // public static Int32 operator %(UnixTime a, TimeSpan b) => a.Ticks % GetUnixTicks(b);
public static Boolean operator > (UnixTime l, UnixTime r) => l.Ticks > r.Ticks; //
public static Boolean operator <= (UnixTime l, UnixTime r) => l.Ticks <= r.Ticks; // public static Boolean operator < (UnixTime l, UnixTime r) => l.Ticks < r.Ticks;
public static Boolean operator >= (UnixTime l, UnixTime r) => l.Ticks >= r.Ticks; // public static Boolean operator > (UnixTime l, UnixTime r) => l.Ticks > r.Ticks;
public static Boolean operator == (UnixTime l, UnixTime r) => l.Ticks == r.Ticks; // public static Boolean operator <= (UnixTime l, UnixTime r) => l.Ticks <= r.Ticks;
public static Boolean operator != (UnixTime l, UnixTime r) => l.Ticks != r.Ticks; // public static Boolean operator >= (UnixTime l, UnixTime r) => l.Ticks >= r.Ticks;
// public static Boolean operator == (UnixTime l, UnixTime r) => l.Ticks == r.Ticks;
public static UnixTimeSpan operator -(UnixTime a, UnixTime b) => UnixTimeSpan.FromTicks(a.Ticks - b.Ticks); // public static Boolean operator != (UnixTime l, UnixTime r) => l.Ticks != r.Ticks;
} //
// public static TimeSpan operator -(UnixTime a, UnixTime b) => TimeSpan.FromSeconds(a.Ticks - b.Ticks);
//
// private static Int32 GetUnixTicks(TimeSpan ts)
// {
// return (Int32) (ts.Ticks / 10000000L);
// }
// }

View File

@ -1,8 +1,8 @@
namespace InnovEnergy.Lib.Time.Unix; // namespace InnovEnergy.Lib.Time.Unix;
//
public readonly partial struct UnixTime // public readonly partial struct UnixTime
{ // {
public override Boolean Equals(Object? obj) => obj is UnixTime other && Ticks == other.Ticks; // public override Boolean Equals(Object? obj) => obj is UnixTime other && Ticks == other.Ticks;
public override Int32 GetHashCode() => (Int32) (Ticks & 0x7F_FF_FF_FF); // public override Int32 GetHashCode() => (Int32) (Ticks & 0x7F_FF_FF_FF);
public override String ToString() => Ticks.ToString(); // public override String ToString() => Ticks.ToString();
} // }

View File

@ -1,14 +1,6 @@
// namespace InnovEnergy.Lib.Time.Unix;
//
// ReSharper disable ArrangeStaticMemberQualifier // public readonly partial struct UnixTime
// {
namespace InnovEnergy.Lib.Time.Unix; // public required Int32 Ticks { get; init; }
// }
public readonly partial struct UnixTime
{
// IMPORTANT: init is necessary for JSON deserializer
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
public UInt32 Ticks { get; init; }
public UnixTime RoundTo(UnixTimeSpan span) => Epoch + this / span * span;
}

View File

@ -1,7 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan : IComparable<UnixTimeSpan>, IEquatable<UnixTimeSpan>
{
Int32 IComparable<UnixTimeSpan>.CompareTo(UnixTimeSpan other) => Ticks.CompareTo(other.Ticks);
Boolean IEquatable<UnixTimeSpan>.Equals(UnixTimeSpan other) => Ticks == other.Ticks;
}

View File

@ -1,26 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan
{
private UnixTimeSpan(UInt32 ticks) => Ticks = ticks;
public static UnixTimeSpan FromTicks(UInt32 ticks) => new UnixTimeSpan(ticks);
public static UnixTimeSpan FromTimeSpan(TimeSpan timeSpan)
{
var ticks = (UInt32) timeSpan.TotalSeconds;
return FromTicks(ticks);
}
public static UnixTimeSpan FromSeconds(UInt32 s) => FromTicks (s);
public static UnixTimeSpan FromMinutes(UInt32 m) => FromSeconds(m * 60);
public static UnixTimeSpan FromHours (UInt32 h) => FromMinutes(h * 60);
public static UnixTimeSpan FromDays (UInt32 d) => FromHours (d * 24);
public static UnixTimeSpan FromWeeks (UInt32 w) => FromDays (w * 7);
public static UnixTimeSpan FromSeconds(Int32 s) => FromSeconds((UInt32) s);
public static UnixTimeSpan FromMinutes(Int32 m) => FromMinutes((UInt32) m);
public static UnixTimeSpan FromHours (Int32 h) => FromHours ((UInt32) h);
public static UnixTimeSpan FromDays (Int32 d) => FromDays ((UInt32) d);
public static UnixTimeSpan FromWeeks (Int32 w) => FromWeeks ((UInt32) w);
}

View File

@ -1,9 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan
{
public TimeSpan ToTimeSpan() => TimeSpan.FromSeconds(Ticks);
public static implicit operator TimeSpan(UnixTimeSpan unixTimeSpan) => unixTimeSpan.ToTimeSpan();
public static implicit operator UnixTimeSpan(TimeSpan timeSpan) => FromTimeSpan(timeSpan);
}

View File

@ -1,29 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan
{
public static Boolean operator < (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks < r.Ticks;
public static Boolean operator > (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks > r.Ticks;
public static Boolean operator <= (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks <= r.Ticks;
public static Boolean operator >= (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks >= r.Ticks;
public static Boolean operator == (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks == r.Ticks;
public static Boolean operator != (UnixTimeSpan l, UnixTimeSpan r) => l.Ticks != r.Ticks;
public static UnixTimeSpan operator +(UnixTimeSpan a, UnixTimeSpan b) => new UnixTimeSpan(a.Ticks + b.Ticks);
public static UnixTimeSpan operator -(UnixTimeSpan a, UnixTimeSpan b) => new UnixTimeSpan(a.Ticks - b.Ticks);
public static UnixTimeSpan operator *(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks * b);
public static UnixTimeSpan operator *(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks * (UInt32) b);
public static UnixTimeSpan operator *(UInt32 a, UnixTimeSpan b) => new UnixTimeSpan(a * b.Ticks);
public static UnixTimeSpan operator *(Int32 a, UnixTimeSpan b) => new UnixTimeSpan((UInt32) a * b.Ticks);
public static UnixTimeSpan operator /(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks / b);
public static UnixTimeSpan operator /(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks / (UInt32)b);
public static UInt32 operator /(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks / b.Ticks;
public static UnixTimeSpan operator %(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks % b);
public static UnixTimeSpan operator %(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b);
public static UInt32 operator %(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks % b.Ticks;
}

View File

@ -1,50 +0,0 @@
using System.Text;
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan
{
public override String ToString()
{
var sb = new StringBuilder();
var dt = ToTimeSpan();
var weeks = dt.Days / 7;
var days = dt.Days % 7;
if (weeks > 0)
{
sb.Append(weeks);
sb.Append("w ");
}
if (days > 0)
{
sb.Append(days);
sb.Append("d ");
}
if (dt.Hours > 0)
{
sb.Append(dt.Hours);
sb.Append("h ");
}
if (dt.Minutes > 0)
{
sb.Append(dt.Minutes);
sb.Append("m ");
}
if (dt.Seconds > 0)
{
sb.Append(dt.Seconds);
sb.Append("s");
}
return sb.ToString().Trim();
}
public override Boolean Equals(Object? obj) => obj is UnixTime other && Ticks == other.Ticks;
public override Int32 GetHashCode() => (Int32) (Ticks & 0x7F_FF_FF_FF);
public static UnixTimeSpan Forever { get; } = FromTicks(UInt32.MaxValue) / 1.Weeks() * 1.Weeks();
}

View File

@ -1,8 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public readonly partial struct UnixTimeSpan
{
public UInt32 Ticks { get; }
}

View File

@ -1,16 +0,0 @@
namespace InnovEnergy.Lib.Time.Unix;
public static class UnixTimeSpanExtensions
{
public static UnixTimeSpan Seconds(this Int32 s) => UnixTimeSpan.FromSeconds(s);
public static UnixTimeSpan Minutes(this Int32 m) => UnixTimeSpan.FromMinutes(m);
public static UnixTimeSpan Hours (this Int32 h) => UnixTimeSpan.FromHours (h);
public static UnixTimeSpan Days (this Int32 d) => UnixTimeSpan.FromDays (d);
public static UnixTimeSpan Weeks (this Int32 w) => UnixTimeSpan.FromWeeks (w);
public static UnixTimeSpan Seconds(this UInt32 s) => UnixTimeSpan.FromSeconds(s);
public static UnixTimeSpan Minutes(this UInt32 m) => UnixTimeSpan.FromMinutes(m);
public static UnixTimeSpan Hours (this UInt32 h) => UnixTimeSpan.FromHours (h);
public static UnixTimeSpan Days (this UInt32 d) => UnixTimeSpan.FromDays (d);
public static UnixTimeSpan Weeks (this UInt32 w) => UnixTimeSpan.FromWeeks (w);
}

View File

@ -8,7 +8,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Utils/Utils.csproj" /> <ProjectReference Include="../Utils/Utils.csproj" />
<ProjectReference Include="../Time/Time.csproj" />
</ItemGroup> </ItemGroup>
<!-- <Target Name="PreBuild" BeforeTargets="PreBuildEvent">--> <!-- <Target Name="PreBuild" BeforeTargets="PreBuildEvent">-->

View File

@ -5,6 +5,12 @@ namespace InnovEnergy.Lib.Utils;
public static class AsyncEnumerableEx public static class AsyncEnumerableEx
{ {
public static IAsyncEnumerable<R> SelectMany<T, R>(this IAsyncEnumerable<T> ts, Func<T, IEnumerable<R>> map)
{
return ts.SelectMany<T, R>(t => map(t).ToAsyncEnumerable());
}
public static async IAsyncEnumerable<T> Throttle<T>(this IAsyncEnumerable<T> ts, public static async IAsyncEnumerable<T> Throttle<T>(this IAsyncEnumerable<T> ts,
TimeSpan dueTime, TimeSpan dueTime,
[EnumeratorCancellation] CancellationToken cancel = default) [EnumeratorCancellation] CancellationToken cancel = default)

View File

@ -0,0 +1,38 @@
namespace InnovEnergy.Lib.Utils;
public static class DateTimeUtils
{
public static DateTime Epoch { get; } = new DateTime
(
year: 1970,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
DateTimeKind.Utc
);
public static Int64 ToUnixTime(this DateTime dateTime)
{
var timeSinceEpoch = dateTime.ToUniversalTime() - Epoch;
return timeSinceEpoch.Ticks / TimeSpan.TicksPerSecond;
}
public static DateTime DateTimeFromUnixTime(this IConvertible unixTime)
{
var epochTicks = Epoch.Ticks + unixTime.ConvertTo<Double>() * TimeSpan.TicksPerSecond;
return new DateTime(epochTicks.ConvertTo<Int64>(), DateTimeKind.Utc);
}
public static DateTime Round(this DateTime date, TimeSpan interval)
{
var i = interval.Ticks;
var t = date.Ticks;
var intervalTicks = (t + i / 2 + 1) / i * i;
return new DateTime(intervalTicks, date.Kind);
}
}

View File

@ -6,7 +6,7 @@ public static class GraphTraversal
{ {
public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(T root, public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(T root,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
return Traverse(root, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer); return Traverse(root, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer);
} }
@ -14,7 +14,7 @@ IEqualityComparer<T>? comparer = null)
public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(T root, public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(T root,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
return Traverse(root, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer); return Traverse(root, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer);
} }
@ -28,7 +28,7 @@ IEqualityComparer<T>? comparer = null)
public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(IEnumerable<T> sources, public static IEnumerable<T> TraverseDepthFirstPreOrder<T>(IEnumerable<T> sources,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
return Traverse(sources, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer); return Traverse(sources, TreeTraversal.TraverseDepthFirstPreOrder, getChildren, comparer);
} }
@ -36,14 +36,14 @@ IEqualityComparer<T>? comparer = null)
public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(IEnumerable<T> sources, public static IEnumerable<T> TraverseDepthFirstPostOrder<T>(IEnumerable<T> sources,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
return Traverse(sources, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer); return Traverse(sources, TreeTraversal.TraverseDepthFirstPostOrder, getChildren, comparer);
} }
public static IEnumerable<T> TraverseBreadthFirst<T>(IEnumerable<T> sources, public static IEnumerable<T> TraverseBreadthFirst<T>(IEnumerable<T> sources,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
return Traverse(sources, TreeTraversal.TraverseBreadthFirst, getChildren, comparer); return Traverse(sources, TreeTraversal.TraverseBreadthFirst, getChildren, comparer);
} }
@ -52,7 +52,7 @@ IEqualityComparer<T>? comparer = null)
private static IEnumerable<T> Traverse<T>(T root, private static IEnumerable<T> Traverse<T>(T root,
Func<T , Func<T, IEnumerable<T>>,IEnumerable<T>> traversor, Func<T , Func<T, IEnumerable<T>>,IEnumerable<T>> traversor,
Func<T, IEnumerable<T>> getChildren, Func<T, IEnumerable<T>> getChildren,
IEqualityComparer<T>? comparer = null) IEqualityComparer<T>? comparer = null)
{ {
var getUniqueChildren = GetUniqueChildren(getChildren, root, comparer); var getUniqueChildren = GetUniqueChildren(getChildren, root, comparer);
return traversor(root, getUniqueChildren); return traversor(root, getUniqueChildren);
@ -98,15 +98,15 @@ IEqualityComparer<T>? comparer = null)
} }
private static Func<T, IEnumerable<T>> GetUniqueChildren<T>(Func<T, IEnumerable<T>> getChildren, private static Func<T, IEnumerable<T>> GetUniqueChildren<T>(Func<T, IEnumerable<T>> getChildren,
T root, T root,
IEqualityComparer<T>? comparer) IEqualityComparer<T>? comparer)
{ {
return GetUniqueChildren(getChildren, root.AsSingleEnumerable(), comparer); return GetUniqueChildren(getChildren, root.AsSingleEnumerable(), comparer);
} }
private static Func<T, IEnumerable<T>> GetUniqueChildren<T>(Func<T, IEnumerable<T>> getChildren, private static Func<T, IEnumerable<T>> GetUniqueChildren<T>(Func<T, IEnumerable<T>> getChildren,
IEnumerable<T> sources, IEnumerable<T> sources,
IEqualityComparer<T>? comparer) IEqualityComparer<T>? comparer)
{ {
var set = new HashSet<T>(sources, comparer ?? EqualityComparer<T>.Default); var set = new HashSet<T>(sources, comparer ?? EqualityComparer<T>.Default);
return n => getChildren(n).Where(set.Add); return n => getChildren(n).Where(set.Add);

View File

@ -2,20 +2,7 @@ namespace InnovEnergy.Lib.Utils;
public static class NullableUtils public static class NullableUtils
{ {
public static T? Nullable<T>(this T t) where T : struct => t; // ReSharper disable once ReturnTypeCanBeNotNullable
public static T? AsNullable<T>(this T t) => t;
public static IEnumerable<T> Enumerable<T>(this T? t) where T : struct
{
if (t.HasValue)
yield return t.Value;
}
public static T ThrowIfNull<T>(this T? t, String message) where T:struct
{
if (!t.HasValue)
throw new NullReferenceException(message);
return t.Value;
}
} }

View File

@ -0,0 +1,26 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.Lib.Utils;
public static class NullableUtilsClass
{
public static IEnumerable<T> ToEnumerable<T>(this T? t) where T : class
{
if (t is not null)
yield return t;
}
public static T ThrowIfNull<T>(this T? t) where T : class => ThrowIfNull(t, null);
public static T ThrowIfNull<T>(this T? t, String? message) where T : class
{
if (t is null)
throw new NullReferenceException(message);
return t;
}
public static Boolean IsNull<T>([NotNullWhen(returnValue: false)] this T? t) where T : class => t is null;
public static T? As<T>(this Object t) where T : class => t as T;
}

View File

@ -0,0 +1,24 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.Lib.Utils;
public static class NullableUtilsStruct
{
public static IEnumerable<T> ToEnumerable<T>(this T? t) where T : struct
{
if (t.HasValue)
yield return t.Value;
}
public static T ThrowIfNull<T>(this T? t) where T : struct => ThrowIfNull(t, null);
public static T ThrowIfNull<T>(this T? t, String? message) where T : struct
{
if (!t.HasValue)
throw new NullReferenceException(message);
return t.Value;
}
public static Boolean IsNull<T>([NotNullWhen(returnValue: false)] this T? t) where T : struct => t.HasValue;
}

104
csharp/Lib/Utils/Result.cs Normal file
View File

@ -0,0 +1,104 @@
namespace InnovEnergy.Lib.Utils;
// TODO: discriminated union
public abstract record Result<T>
{
public sealed record Success(T Value) : Result<T>;
public sealed record Failure(String Error) : Result<T>;
public Boolean IsError => this is Failure;
public Boolean IsOk => this is Success;
public static implicit operator Result<T>(T t) => new Success(t);
public static implicit operator Result<T>(String e) => new Failure(e);
}
public abstract record Result<T, E>
{
public sealed record Success(T Value) : Result<T, E>;
public sealed record Failure(E Error) : Result<T, E>;
public Boolean IsError => this is Failure;
public Boolean IsOk => this is Success;
public static implicit operator Result<T, E>(T t) => new Success(t);
public static implicit operator Result<T, E>(E e) => new Failure(e);
public R Map<R>(Func<T, R> onSuccess, Func<E, R> onFailure) => this switch
{
Success s => onSuccess(s.Value),
Failure f => onFailure(f.Error),
_ => throw new ArgumentOutOfRangeException()
};
public Result<R, E> OnSuccess<R>(Func<T, R> onOk) => this switch
{
Success s => onOk(s.Value),
Failure f => f.Error,
_ => throw new ArgumentOutOfRangeException()
};
public Result<T, R> OnFailure<R>(Func<E, R> onError) => this switch
{
Success s => s.Value,
Failure f => onError(f.Error),
_ => throw new ArgumentOutOfRangeException()
};
}
public static class Result
{
public static Result<T, Exception> Try<T>(Func<T> func)
{
try
{
return func();
}
catch (Exception e)
{
return e;
}
}
public static String? GetError<T>(this Result<T> r)
{
return r is Result<T>.Failure err
? err.Error
: null;
}
}
public static class ResultClass
{
public static T? GetValue<T>(this Result<T> r) where T : class
{
return r is Result<T>.Success ok
? ok.Value
: null;
}
public static E? GetError<T, E>(this Result<T, E> r) where E : class
{
return r is Result<T, E>.Failure err
? err.Error
: null;
}
}
public static class ResultStruct
{
public static T? GetValue<T>(this Result<T> r) where T : struct
{
return r is Result<T>.Success ok
? ok.Value
: null;
}
public static E? GetError<T, E>(this Result<T, E> r) where E : struct
{
return r is Result<T,E>.Failure err
? err.Error
: null;
}
}

View File

@ -376,5 +376,10 @@ public static class StringUtils
return $"{prefix}{target.TrimStart(prefix)}"; return $"{prefix}{target.TrimStart(prefix)}";
} }
public static String EnsureEndsWith(this String target, String postfix)
{
return $"{target.TrimEnd(postfix)}{postfix}";
}
} }

View File

@ -6,9 +6,10 @@ using static System.Runtime.CompilerServices.MethodImplOptions;
namespace InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Utils;
public static class Utils public static class Utils
{ {
public static Boolean IsNull<T>([NotNullWhen(returnValue: false)] this T t) => t is null;
public static IEnumerable<String> GetEnumStrings<T>(this T e) where T : Enum public static IEnumerable<String> GetEnumStrings<T>(this T e) where T : Enum
{ {
@ -169,9 +170,5 @@ public static class Utils
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name)); CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
} }
public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName; public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName;
} }

View File

@ -1,8 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configurations>Debug;Release;Release-Server</Configurations>
<Platforms>AnyCPU;linux-arm</Platforms>
</PropertyGroup>
<Import Project="../InnovEnergy.Lib.props" /> <Import Project="../InnovEnergy.Lib.props" />
<PropertyGroup> <PropertyGroup>
@ -12,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.0" /> <PackageReference Include="CliWrap" Version="3.6.0" />
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2" /> <PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Reactive.Linq" Version="5.0.0" /> <PackageReference Include="System.Reactive.Linq" Version="5.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,9 +0,0 @@
namespace InnovEnergy.Lib.Utils.WIP;
// TODO: discriminated union
public abstract record Result
{
public sealed record Ok(Object Result) : Result;
public sealed record Error(String Message) : Result;
}

View File

@ -1,5 +1,4 @@
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
// ReSharper disable StringLiteralTypo // ReSharper disable StringLiteralTypo
@ -9,8 +8,8 @@ namespace InnovEnergy.Lib.Victron.VictronVRM;
public readonly partial record struct Installation(VrmAccount VrmAccount, JsonNode Json) public readonly partial record struct Installation(VrmAccount VrmAccount, JsonNode Json)
{ {
public UInt64 IdSite => Json.GetUInt64("idSite"); public UInt64 IdSite => Json.GetUInt64("idSite");
public UnixTime Created => Json.GetUInt32("syscreated").Apply(UnixTime.FromTicks); public UInt32 Created => Json.GetUInt32("syscreated");
// Settings // Settings

View File

@ -17,7 +17,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Utils/Utils.csproj" /> <ProjectReference Include="../../Utils/Utils.csproj" />
<ProjectReference Include="../../Time/Time.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -11,10 +11,9 @@ public static class HttpExtensions
{ {
var headers = req.Headers; var headers = req.Headers;
return headers return headers
.AllKeys .AllKeys
.EmptyIfNull() .EmptyIfNull()
.Where(k => k != null) .Select(k => new HttpHeader(k!, headers[k]!));
.Select(k => new HttpHeader(k!, headers[k]!));
} }