diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index 14c00da48..71fb0d640 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -2,7 +2,6 @@ using SQLite; namespace InnovEnergy.App.Backend.DataTypes; - public class Installation : TreeNode { public String Location { get; set; } = ""; diff --git a/csharp/App/OpenVpnCertificatesServer/Program.cs b/csharp/App/OpenVpnCertificatesServer/Program.cs index 581699d5f..6189489b3 100644 --- a/csharp/App/OpenVpnCertificatesServer/Program.cs +++ b/csharp/App/OpenVpnCertificatesServer/Program.cs @@ -153,8 +153,6 @@ public static class Program var installations = await vrm.GetInstallations(); - - var installationName = installations .Where(i => i.UniqueId == uniqueId) .Select(i => i.Name) diff --git a/csharp/App/SaliMax/Doc/AllStates.graphml b/csharp/App/SaliMax/Doc/AllStates.graphml new file mode 100644 index 000000000..23d73c65a --- /dev/null +++ b/csharp/App/SaliMax/Doc/AllStates.graphml @@ -0,0 +1,964 @@ + + + + + + + + + + + + + + + + + + + + + + + + 23 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 19 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 3 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 9 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 1 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 13 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 29 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 5 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 7 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + + 11 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 15 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 21 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 17 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 25 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 27 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 31 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 28 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 24 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 8 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 6 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 0 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 4 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 22 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 16 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 20 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 18 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 2 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 10 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 12 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 14 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 26 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 30 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +grid tie + + + + + + + + + + + + K3's open + + + + + + + + + + + + close K2 + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + K3's close + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverter + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + K3's close + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +island mode + + + + + + + + + + + + turn on +inverters + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + K1 closes + + + + + + + + + + + + K1 opens + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + K3's open + + + + + + + + + + + K3's open + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/States_Table.xlsx b/csharp/App/SaliMax/Doc/States_Table.xlsx new file mode 100644 index 000000000..776df481c Binary files /dev/null and b/csharp/App/SaliMax/Doc/States_Table.xlsx differ diff --git a/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml b/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml new file mode 100644 index 000000000..b73645cf6 --- /dev/null +++ b/csharp/App/SaliMax/Doc/TransitionToGridTied.graphml @@ -0,0 +1,501 @@ + + + + + + + + + + + + + + + + + + + + + + + + 19 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 3 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 9 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 1 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 13 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 29 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 23 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 5 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 7 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + + 11 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 15 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 21 + K1 ✓ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 17 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 25 + K1 ✓ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + + 27 + K1 ✓ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + + 31 + K1 ✓ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + switch to +grid tie + + + + + + + + + + + + + + + K3's open + + + + + + + + + + + + close K2 + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + K3's close + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + turn on +Inverters + + + + + + + + + + + + close K2 + + + + + + + + + + + + + + + turn off +inverter + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + + + + turn off +inverters + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/TransitionToIsland.graphml b/csharp/App/SaliMax/Doc/TransitionToIsland.graphml new file mode 100644 index 000000000..800dffa96 --- /dev/null +++ b/csharp/App/SaliMax/Doc/TransitionToIsland.graphml @@ -0,0 +1,487 @@ + + + + + + + + + + + + + + + + + + + + + + + + 28 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 24 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 8 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 6 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 0 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 4 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 22 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 16 + K1 ✘ +K2 ✘ +K3 ✘ + + + + + + + + + + + + + + 20 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 18 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 2 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 10 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 12 + K1 ✘ +K2 ✘ +K3 ✓ + + + + + + + + + + + + + + 14 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + + 26 + K1 ✘ +K2 ✓ +K3 ✘ + + + + + + + + + + + + + + 30 + K1 ✘ +K2 ✓ +K3 ✓ + + + + + + + + + + + + + K3's open + + + + + + + + + + + K3's close + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + + + + switch to +island mode + + + + + + + + + + + + turn on +inverters + + + + + + + + + + + turn off +Inverters + + + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + + + + open K2 + + + + + + + + + + + + open K2 + + + + + + + + + + + + + + + K3 opens + + + + + + + + + + + + open K2 + + + + + + + + + + + + turn off +inverters + + + + + + + + + + + + turn off +inverters + + + + + + + + + diff --git a/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml b/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml deleted file mode 100644 index 1968f44f8..000000000 --- a/csharp/App/SaliMax/Doc/Zustandsdiagramm_IBN_Standard.graphml +++ /dev/null @@ -1,503 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - 2 -Aus (mit Netz) - - - - - - - - - - - 3 - - - - - - - - - - - 4 - - - - - - - - - - - 5 - - - - - - - - - - - 6 - - - - - - - - - - - 7 - - - - - - - - - - - 8 - - - - - - - - - - - 9 - - - - - - - - - - - 4 - - - - - - - - - - - 11 - - - - - - - - - - - 12 - - - - - - - - - - - 13 - - - - - - - - - - - 14 - - - - - - - - - - - 15 - - - - - - - - - - - 16 -Netzparallel - - - - - - - - - - - 17 - - - - - - - - - - - 18 - - - - - - - - - - - 19 - - - - - - - - - - - 20 - - - - - - - - - - - 21 -Inselbetrieb - - - - - - - - - - - 22 - - - - - - - - - - - 23 - - - - - - - - - - - 24 - - - - - - - - - - - 1 - - - - - - - - - - - 9 - - - - - - - - - - - 13 - - - - - - - - - - - 15 - - - - - - - - - - - 17 - - - - - - - - - - - 1 -Aus (ohne Netz) - - - - - - - - - - - K1 schliesst - - - - - - - - - - - Uebergang nach Off - - - - - - - - - - - Uebergang nach Netzparallel - - - - - - - - - - - K3 öffnet - - - - - - - - - - - K2 schliesst - - - - - - - - - - - K3 schliesst - - - - - - - - - - - K1 öffnet - - - - - - - - - - - K2 öffnet - - - - - - - - - - - K3 öffnet - - - - - - - - - - - K3 schliesst - - - - - - - - - - - Uebergang nach Off - - - - - - - - - - - Uebergan nach Inselbetrieb - - - - - - - - - - - - K1 Schliesst - - - - - - - - - - - - - - - Turnoff The inverter - - - - - - - - - diff --git a/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx b/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx deleted file mode 100644 index 6cd1ceb13..000000000 Binary files a/csharp/App/SaliMax/Doc/Zustandsmatrix_Salimax.xlsx and /dev/null differ diff --git a/csharp/App/SaliMax/run (Salimax0003).sh b/csharp/App/SaliMax/run (Salimax0003).sh index ba6f5b68c..a47112ee4 100644 --- a/csharp/App/SaliMax/run (Salimax0003).sh +++ b/csharp/App/SaliMax/run (Salimax0003).sh @@ -18,17 +18,17 @@ echo -e "\n============================ Deploy ============================\n" rsync -v \ ./bin/Release/$dotnet_version/linux-x64/publish/* \ - ie-entwicklung@10.2.4.33:~/salimax + $username@$salimax_ip:~/salimax echo -e "\n============================ Restart Salimax sevice ============================\n" ssh -tt \ - ie-entwicklung@10.2.4.33 \ + $username@$salimax_ip \ sudo systemctl restart salimax.service echo -e "\n============================ Print service output ============================\n" ssh -tt \ - ie-entwicklung@10.2.4.33 \ + $username@$salimax_ip \ journalctl -f -u salimax.service \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Logfile.cs b/csharp/App/SaliMax/src/Logfile.cs index 2d95a323d..6465109df 100644 --- a/csharp/App/SaliMax/src/Logfile.cs +++ b/csharp/App/SaliMax/src/Logfile.cs @@ -42,6 +42,8 @@ public class CustomLogger : ILogger // Write the log message to the file File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine); _CurrentFileSizeBytes += logMessage.Length; + + Console.WriteLine( logMessage + Environment.NewLine); } private void RotateLogFile() diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 665201e29..ed99a83da 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -33,7 +33,7 @@ internal static class Program private const UInt32 UpdateIntervalSeconds = 2; - private static readonly Byte[] BatteryNodes = { 2, 3, 4, 5, 6 }; + private static readonly IReadOnlyList BatteryNodes; private static readonly TcpChannel TruConvertAcChannel ; private static readonly TcpChannel TruConvertDcChannel ; @@ -55,6 +55,12 @@ internal static class Program AmptChannel = new TcpChannel(d.AmptIp); RelaysChannel = new TcpChannel(d.RelaysIp); BatteriesChannel = new TcpChannel(d.BatteryIp); + + BatteryNodes = config + .Devices + .BatteryNodes + .Select(n=>n.ConvertTo()) + .ToArray(config.Devices.BatteryNodes.Length); } public static async Task Main(String[] args) @@ -93,17 +99,30 @@ internal static class Program StatusRecord ReadStatus() { + Console.WriteLine(" Reading AcDC"); var acDc = acDcDevices.Read(); + + Console.WriteLine(" Reading dcDc"); var dcDc = dcDcDevices.Read(); + + Console.WriteLine(" Reading battery"); var battery = batteryDevices.Read(); + + Console.WriteLine(" Reading relays"); var relays = saliMaxRelaysDevice.Read(); + + Console.WriteLine(" loadOnAcIsland"); var loadOnAcIsland = acIslandLoadMeter.Read(); + + Console.WriteLine(" Reading gridMeter"); var gridMeter = gridMeterDevice.Read(); + + Console.WriteLine(" Reading pvOnDc"); var pvOnDc = amptDevice.Read(); var pvOnAcGrid = AcPowerDevice.Null; var pvOnAcIsland = AcPowerDevice.Null; - var gridPower = gridMeter is null ? AcPower.Null : gridMeter.Ac.Power; + var gridPower = gridMeter is null ? AcPower.Null : gridMeter.Ac.Power; var islandLoadPower = loadOnAcIsland is null ? AcPower.Null : loadOnAcIsland.Ac.Power; var inverterAcPower = acDc.Ac.Power; @@ -235,17 +254,15 @@ internal static class Program var dcLinkVoltage = TextBlock.AlignCenterHorizontal("", s.DcDc.Dc.Link.Voltage.ToDisplayString(), ""); - - //var inverterPowerByPhase = new ActivePower[(Int32)s.AcDc.Ac.L1.Power.Active, (Int32)s.AcDc.Ac.L2.Power.Active, (Int32)s.AcDc.Ac.L3.Power.Active]; - - // Voltage Measurement Values - //var inverterVoltage = new Voltage [(Int32)s.AcDc.Ac.L1.Voltage, (Int32)s.AcDc.Ac.L2.Voltage, (Int32)s.AcDc.Ac.L3.Voltage]; - //var dcLinkVoltage = s.DcDc.Dc.Link.Voltage; + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - var batteryVoltage = s.Battery.Dc.Voltage.ToDisplayString(); - var batterySoc = s.Battery.Soc.ToDisplayString(); + var batteryVoltage = s.Battery.Dc.Voltage.Value.RoundToSignificantDigits(3); + var batterySoc = s.Battery.Devices.Any()? s.Battery.Devices.Average(b=>b.Soc).Percent().ToDisplayString() : "0"; var batteryCurrent = s.Battery.Dc.Current.ToDisplayString(); var batteryTemp = s.Battery.Temperature.ToDisplayString(); + var batteryHeatingCurrent = s.Battery.HeatingCurrent.ToDisplayString(); + var anyBatteryAlarm = s.Battery.Alarms.Any(); + var anyBatteryWarning = s.Battery.Warnings.Any(); var gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower); var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower); @@ -263,12 +280,15 @@ internal static class Program var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage, batterySoc, batteryCurrent, - batteryTemp) + batteryTemp, + batteryHeatingCurrent, + anyBatteryWarning, + anyBatteryAlarm) .TitleBox("Battery"); //////////////////// Batteries ///////////////////////// - + IReadOnlyList batteryBoxes = s.Battery .Devices .Select(CreateIndividualBattery) @@ -297,13 +317,17 @@ internal static class Program private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) { + var batteryWarnings = battery.Warnings.Any(); + var batteryAlarms = battery.Alarms.Any(); var content = TextBlock.AlignLeft(battery.Dc.Voltage.ToDisplayString(), battery.Soc.ToDisplayString(), battery.Dc.Current.ToDisplayString(), battery.Temperatures.Cells.Average.ToDisplayString(), - battery.BusCurrent.ToDisplayString(), - battery.HeatingCurrent.ToDisplayString()); + // battery.BusCurrent.ToDisplayString(), + battery.HeatingCurrent.ToDisplayString(), + batteryWarnings, + batteryAlarms); var box = content.TitleBox($"Battery {i + 1}"); diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs index 31afb815c..118d1ed54 100644 --- a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs +++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysDevice.cs @@ -18,7 +18,7 @@ public class RelaysDevice } catch (Exception e) { - $"Failed to read from {nameof(RelaysDevice)}\n{e}".LogInfo(); + $"Failed to read from {nameof(RelaysDevice)}\n{e}".LogError(); // TODO: log return null; @@ -33,7 +33,7 @@ public class RelaysDevice } catch (Exception e) { - $"Failed to write to {nameof(RelaysDevice)}\n{e}".LogInfo(); + $"Failed to write to {nameof(RelaysDevice)}\n{e}".LogError(); } } } diff --git a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs index 092c7cb8f..2f0f98aee 100644 --- a/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs +++ b/csharp/App/SaliMax/src/SaliMaxRelays/RelaysRecord.cs @@ -2,19 +2,11 @@ using InnovEnergy.Lib.Devices.Adam6360D; namespace InnovEnergy.App.SaliMax.SaliMaxRelays; -public enum InvertersAreConnectedToAc -{ - None, - Some, - All -} - public class RelaysRecord { private readonly Adam6360DRegisters _Regs; public RelaysRecord(Adam6360DRegisters regs) => _Regs = regs; - public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput6; public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput4; @@ -39,16 +31,9 @@ public class RelaysRecord public Boolean FiWarning => !_Regs.DigitalInput5; public Boolean FiError => !_Regs.DigitalInput7; - public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;} + public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;} public static implicit operator Adam6360DRegisters(RelaysRecord d) => d._Regs; public static implicit operator RelaysRecord(Adam6360DRegisters d) => new RelaysRecord(d); - // - // public HighActivePinState F1Inverter1 => _Regs.DigitalInput8.ConvertTo(); // 1 = Closed , 0 = open - // public HighActivePinState F2Inverter2 => _Regs.DigitalInput9.ConvertTo(); // 1 = Closed , 0 = open - // public HighActivePinState F3Inverter3 => _Regs.DigitalInput10.ConvertTo(); // 1 = Closed , 0 = open - // public HighActivePinState F4Inverter4 => _Regs.DigitalInput11.ConvertTo(); // 1 = Closed , 0 = open - // - // public HighActivePinState Di12 => _Regs.DigitalInput12.ConvertTo(); } diff --git a/csharp/App/SaliMax/src/SystemConfig/Config.cs b/csharp/App/SaliMax/src/SystemConfig/Config.cs index c75a188d9..294cf1f5d 100644 --- a/csharp/App/SaliMax/src/SystemConfig/Config.cs +++ b/csharp/App/SaliMax/src/SystemConfig/Config.cs @@ -75,7 +75,15 @@ public class Config //TODO: let IE choose from config files (Json) and connect t MinDcBusVoltage = 690, ReferenceDcBusVoltage = 750, MaxDcBusVoltage = 810, - S3 = null, // TODO + S3 = new() + { + Bucket = "saliomameiringen", + Region = "sos-ch-dk-2", + Provider = "exo.io", + ContentType = "text/plain; charset=utf-8", + Key = "EXO2bf0cbd97fbfa75aa36ed46f", + Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs" + }, Devices = new () { RelaysIp = new() { Host = "10.0.1.1", Port = 502}, @@ -85,7 +93,7 @@ public class Config //TODO: let IE choose from config files (Json) and connect t IslandBusLoadMeterIp = new() { Host = "10.0.4.2", Port = 502}, AmptIp = new() { Host = "10.0.5.1", Port = 502}, BatteryIp = new() { Host = "localhost", Port = 6855}, - BatteryNodes = new []{ 2, 3, 4, 5, 6 }, + BatteryNodes = new []{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, }, }; #endif diff --git a/csharp/App/SaliMax/tunnelstoSalimax0001.sh b/csharp/App/SaliMax/tunnelstoSalimax0001.sh index f538423bb..8b5fb2697 100755 --- a/csharp/App/SaliMax/tunnelstoSalimax0001.sh +++ b/csharp/App/SaliMax/tunnelstoSalimax0001.sh @@ -35,6 +35,8 @@ tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 4003 tunnel "Int Emu Meter " 10.0.4.2 502 4004 tunnel "AMPT (modbus) " 10.0.5.1 502 4005 tunnel "Adam " 10.0.1.1 502 4006 +tunnel "Batteries " 127.0.0.1 6855 5007 + diff --git a/csharp/App/SaliMax/tunnelstoSalimax0003.sh b/csharp/App/SaliMax/tunnelstoSalimax0003.sh new file mode 100755 index 000000000..da40a584a --- /dev/null +++ b/csharp/App/SaliMax/tunnelstoSalimax0003.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +host=ie-entwicklung@10.2.4.33 + +tunnel() { + name=$1 + ip=$2 + rPort=$3 + lPort=$4 + + echo -n "localhost:$lPort $name " + ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null & + + until nc -vz 127.0.0.1 $lPort 2> /dev/null + do + echo -n . + sleep 0.3 + done + + echo "ok" +} + +echo "" + +tunnel "Trumpf Inverter (http) " 10.0.2.1 80 6001 +tunnel "Trumpf DCDC (http) " 10.0.3.1 80 6002 +tunnel "Ext Emu Meter (http) " 10.0.4.1 80 6003 +tunnel "Int Emu Meter (http) " 10.0.4.2 80 6004 +tunnel "AMPT (http) " 10.0.5.1 8080 6005 + +tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 3001 +tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 3002 +tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 3003 +tunnel "Int Emu Meter " 10.0.4.2 502 3004 +tunnel "AMPT (modbus) " 10.0.5.1 502 3005 +tunnel "Batteries " 127.0.0.1 6855 5007 + + +echo +echo "press any key to close the tunnels ..." +read -r -n 1 -s +kill $(jobs -p) +echo "done" diff --git a/csharp/InnovEnergy.sln b/csharp/InnovEnergy.sln index 64acaf80f..beaac246d 100644 --- a/csharp/InnovEnergy.sln +++ b/csharp/InnovEnergy.sln @@ -75,6 +75,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Lib\Logging\Logg EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6360D", "Lib\Devices\Adam6360D\Adam6360D.csproj", "{A3C79247-4CAA-44BE-921E-7285AB39E71F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEM3kGridMeter", "Lib\Devices\IEM3kGridMeter\IEM3kGridMeter.csproj", "{1391165D-51F1-45B4-8B7F-042A20AA0277}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -190,6 +192,10 @@ Global {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3C79247-4CAA-44BE-921E-7285AB39E71F}.Release|Any CPU.Build.0 = Release|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1391165D-51F1-45B4-8B7F-042A20AA0277}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A} @@ -223,5 +229,6 @@ Global {B816BB44-E97E-4E02-B80A-BEDB5B923A96} = {DDDBEFD0-5DEA-4C7C-A9F2-FDB4636CF092} {1A56992B-CB72-490F-99A4-DF1186BA3A18} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854} {A3C79247-4CAA-44BE-921E-7285AB39E71F} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} + {1391165D-51F1-45B4-8B7F-042A20AA0277} = {4931A385-24DC-4E78-BFF4-356F8D6D5183} EndGlobalSection EndGlobal diff --git a/csharp/Lib/Devices/AMPT/AmptDevices.cs b/csharp/Lib/Devices/AMPT/AmptDevices.cs index 1750d0029..4e8b47256 100644 --- a/csharp/Lib/Devices/AMPT/AmptDevices.cs +++ b/csharp/Lib/Devices/AMPT/AmptDevices.cs @@ -36,7 +36,7 @@ public class AmptDevices } catch (Exception e) { - Console.WriteLine(e); + Console.WriteLine( "Failed to read Ampt data \n"+ e.Message ); // TODO: log } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs index 19b636678..866d4a49d 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs @@ -18,13 +18,11 @@ public class Battery48TlDevices return new Battery48TlRecords(records); } - catch + catch (Exception e) { - //Console.WriteLine(e); + Console.WriteLine( "Failed to read Battery data \n"+ e.Message ); // TODO: log - "Failed to read Battery data".WriteLine(); - return Battery48TlRecords.Null; } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs index 5d5db55d2..6db267887 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs @@ -9,12 +9,13 @@ public class Battery48TlRecords { var empty = records.Count == 0; - Devices = records; - Eoc = !empty && records.All(r => r.Eoc); - Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList(); - Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList(); - Soc = empty ? 0 : records.Min(r => r.Soc.Value); - Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0; + Devices = records; + Eoc = !empty && records.All(r => r.Eoc); + Warnings = records.SelectMany(r => r.Warnings).Distinct().ToList(); + Alarms = records.SelectMany(r => r.Alarms).Distinct().ToList(); + Soc = empty ? 0 : records.Min(r => r.Soc.Value); + Temperature = records.Any() ? records.Average(b => b.Temperatures.Cells.Average.Value) : 0; + HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0; Dc = empty ? DcBus.FromVoltageCurrent(0, 0) @@ -25,12 +26,13 @@ public class Battery48TlRecords ); } - public DcBus Dc { get; init; } - public Boolean Eoc { get; init; } - public IReadOnlyList Warnings { get; init; } - public IReadOnlyList Alarms { get; init; } - public Percent Soc { get; init; } - public Temperature Temperature { get; init; } + public DcBus Dc { get; init; } + public Boolean Eoc { get; init; } + public IReadOnlyList Warnings { get; init; } + public IReadOnlyList Alarms { get; init; } + public Percent Soc { get; init; } + public Temperature Temperature { get; init; } + public Current HeatingCurrent { get; init; } public IReadOnlyList Devices { get; init; } diff --git a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs index 80c460a8d..aab4ca0fd 100644 --- a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs +++ b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs @@ -28,7 +28,7 @@ public class EmuMeterDevice: ModbusDevice } catch { - "Failed to read data from Inverters".WriteLine(); + "Failed to read data from EmuMeter".WriteLine(); return null; } } diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf new file mode 100644 index 000000000..fc758ccd2 Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/Acti 9 iEM3000_A9MEM3255.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf new file mode 100644 index 000000000..3d954f8cd Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/DOCA0005EN-13.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf b/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf new file mode 100644 index 000000000..9da2ba2a5 Binary files /dev/null and b/csharp/Lib/Devices/IEM3kGridMeter/Doc/NHA15801-06.pdf differ diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj new file mode 100644 index 000000000..622823c62 --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeter.csproj @@ -0,0 +1,14 @@ + + + + + InnovEnergy.Lib.Devices.IEM3kGridMeter + + + + + + + + + diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs new file mode 100644 index 000000000..de9e04b62 --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterDevice.cs @@ -0,0 +1,50 @@ +using InnovEnergy.Lib.Protocols.Modbus.Channels; +using InnovEnergy.Lib.Protocols.Modbus.Clients; +using InnovEnergy.Lib.Protocols.Modbus.Slaves; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.IEM3kGridMeter; + +public class Iem3KGridMeterDevice: ModbusDevice +{ + public Iem3KGridMeterDevice(String hostname, UInt16 port = 502, Byte slaveId = 1) : this(new TcpChannel(hostname, port), slaveId) + { + } + + public Iem3KGridMeterDevice(Channel channel, Byte slaveId = 1) : base(new ModbusTcpClient(channel, slaveId)) + { + } + + public Iem3KGridMeterDevice(ModbusClient client) : base(client) + { + } + + + public new Iem3KGridMeterRegisters? Read() + { + try + { + return base.Read(); + } + catch + { + // TODO: Log + $"Failed to read data from {nameof(Iem3KGridMeterDevice)}".WriteLine(); + return null; + } + } + + + public new void Write(Iem3KGridMeterRegisters registers) + { + try + { + base.Write(registers); + } + catch + { + // TODO: Log + $"Failed to write data to {nameof(Iem3KGridMeterDevice)}".WriteLine(); + } + } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs new file mode 100644 index 000000000..19fe9907a --- /dev/null +++ b/csharp/Lib/Devices/IEM3kGridMeter/IEM3kGridMeterRegisters.cs @@ -0,0 +1,73 @@ +using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes; +using InnovEnergy.Lib.StatusApi.DeviceTypes; +using InnovEnergy.Lib.Units.Composite; + +#pragma warning disable CS0649 + +namespace InnovEnergy.Lib.Devices.IEM3kGridMeter; + +using Float32 = Single; + + +[AddressOffset(-2)] // why? +public class Iem3KGridMeterRegisters : IAc3Meter +{ + + // TODO + // The registers below are kept as example (from EmuMeter) + // replace with actual registers from datasheet + + [HoldingRegister(9002)] private Float32 _ActivePowerL1; + [HoldingRegister(9004)] private Float32 _ActivePowerL2; + [HoldingRegister(9006)] private Float32 _ActivePowerL3; + + [HoldingRegister(9012)] private Float32 _ReactivePowerL1; + [HoldingRegister(9014)] private Float32 _ReactivePowerL2; + [HoldingRegister(9016)] private Float32 _ReactivePowerL3; + + [HoldingRegister(9022)] private Float32 _ApparentPowerL1; + [HoldingRegister(9024)] private Float32 _ApparentPowerL2; + [HoldingRegister(9026)] private Float32 _ApparentPowerL3; + + [HoldingRegister(9102)] private Float32 _CurrentL1; + [HoldingRegister(9104)] private Float32 _CurrentL2; + [HoldingRegister(9106)] private Float32 _CurrentL3; + + [HoldingRegister(9200)] private Float32 _VoltageL1N; + [HoldingRegister(9202)] private Float32 _VoltageL2N; + [HoldingRegister(9204)] private Float32 _VoltageL3N; + + [HoldingRegister(9310)] private Float32 _Frequency; + + public Ac3Bus Ac => Ac3Bus.FromPhasesAndFrequency + ( + l1: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL1N, + _CurrentL1, + _ActivePowerL1, + _ReactivePowerL1, + _ApparentPowerL1 + ), + l2: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL2N, + _CurrentL2, + _ActivePowerL2, + _ReactivePowerL2, + _ApparentPowerL2 + ), + l3: AcPhase.FromVoltageCurrentActiveReactiveApparent + ( + _VoltageL3N, + _CurrentL3, + _ActivePowerL3, + _ReactivePowerL3, + _ApparentPowerL3 + ), + frequency: _Frequency + ); +} + + + diff --git a/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj b/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj index 60ab530b6..31f201d1f 100644 --- a/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj +++ b/csharp/Lib/Devices/Trumpf/SystemControl/SystemControl.csproj @@ -1,9 +1,14 @@ - - - - - + + + + InnovEnergy.Lib.Devices.Trumpf.SystemControl + + + + + + diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj index 74d8eea2f..4969d5dab 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj @@ -1,11 +1,15 @@ - - - - - - - + + + + InnovEnergy.Lib.Devices.Trumpf.TruConvertAc + + + + + + + \ No newline at end of file diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs index 8a049cbb8..31cea2131 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDcDevices.cs @@ -47,8 +47,10 @@ public class TruConvertAcDcDevices return new AcDcDevicesRecord(scStatus, acDcRecords); } - catch + catch (Exception e) { + Console.WriteLine( "Failed to read AcDc data \n"+ e.Message ); + // TODO: log return new AcDcDevicesRecord(null, Array.Empty()); } } diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs index 62a8396e2..c58364efb 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs @@ -106,11 +106,3 @@ public partial class DcDcRecord // // #endregion IDcDc } - - - - - - - - diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj index c365582be..ce1debdf7 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj @@ -1,12 +1,15 @@ + - - - - - - - - + + InnovEnergy.Lib.Devices.Trumpf.TruConvertDc + + + + + + + + diff --git a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs index d026cf6cf..4aa3bd71b 100644 --- a/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs +++ b/csharp/Lib/Protocols/Modbus/Channels/TcpChannel.cs @@ -6,9 +6,15 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Channels; public class TcpChannel : ConnectionChannel { + private const Int32 TimeoutMs = 500; // TODO: parametrize - public TcpChannel(Ip4Address ip4Address,Boolean closeAfterSuccessfulRead = false, - Boolean closeAfterSuccessfulWrite = false) : this(ip4Address.Host, ip4Address.Port, closeAfterSuccessfulRead, closeAfterSuccessfulWrite) + public TcpChannel(Ip4Address ip4Address, + Boolean closeAfterSuccessfulRead = false, + Boolean closeAfterSuccessfulWrite = false) : + this(ip4Address.Host, + ip4Address.Port, + closeAfterSuccessfulRead, + closeAfterSuccessfulWrite) { } @@ -18,7 +24,30 @@ public class TcpChannel : ConnectionChannel Boolean closeAfterSuccessfulRead = false, Boolean closeAfterSuccessfulWrite = false) : base(closeAfterSuccessfulRead, closeAfterSuccessfulWrite) { - TcpClient Open() => new(hostname, port); + TcpClient Open() + { + var tcpClient = new TcpClient + { + LingerState = new LingerOption(false, 0), + NoDelay = true, + ReceiveTimeout = TimeoutMs, + SendTimeout = TimeoutMs + }; + + var cts = new CancellationTokenSource(); + + var connected = tcpClient + .ConnectAsync(hostname, port, cts.Token) + .AsTask() + .Wait(TimeoutMs); + + if (connected) + return tcpClient; + + cts.Cancel(); + throw new Exception($"Failed to connect to {hostname}:{port}"); + } + _Open = Open; } diff --git a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs index 8f0acf770..e04c4bef7 100644 --- a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs +++ b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs @@ -5,7 +5,7 @@ using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; namespace InnovEnergy.Lib.Protocols.Modbus.Slaves; -public class ModbusDevice where R : notnull +public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull { private readonly IReadOnlyList> _Batches; @@ -35,7 +35,7 @@ public class ModbusDevice where R : notnull return Read(r); } - public R Read([DynamicallyAccessedMembers(All)] R record) + public R Read(R record) { foreach (var batch in _Batches) batch.Read(record); @@ -43,7 +43,7 @@ public class ModbusDevice where R : notnull return record; } - public void Write([DynamicallyAccessedMembers(All)] R record) + public void Write(R record) { foreach (var batch in _Batches) batch.Write(record); diff --git a/csharp/Lib/SrcGen/SrcGen.csproj b/csharp/Lib/SrcGen/SrcGen.csproj index 7f0fcac19..92cbc3d8c 100644 --- a/csharp/Lib/SrcGen/SrcGen.csproj +++ b/csharp/Lib/SrcGen/SrcGen.csproj @@ -2,6 +2,12 @@ + + + InnovEnergy.Lib.SrcGen + + + diff --git a/csharp/Lib/Units/Units.cs b/csharp/Lib/Units/Units.cs index 161b52a0e..50fae56a2 100644 --- a/csharp/Lib/Units/Units.cs +++ b/csharp/Lib/Units/Units.cs @@ -21,6 +21,7 @@ public static class Units public static Temperature Celsius(this Double value) => value; public static Energy KWh (this Double value) => value * 1000; public static Energy Wh (this Double value) => value; + public static Percent Percent(this Double value) => value; public static String ToCsv(this Object thing) { diff --git a/csharp/Lib/Victron/VeDBus/VeDBus.csproj b/csharp/Lib/Victron/VeDBus/VeDBus.csproj index 23827110a..ac40b053d 100644 --- a/csharp/Lib/Victron/VeDBus/VeDBus.csproj +++ b/csharp/Lib/Victron/VeDBus/VeDBus.csproj @@ -7,6 +7,10 @@ + + InnovEnergy.Lib.Victron.VeDBus + + diff --git a/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj b/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj index 57128f448..9f1ced645 100644 --- a/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj +++ b/csharp/Lib/Victron/VictronVRM/VictronVRM.csproj @@ -11,6 +11,10 @@ AnyCPU;linux-arm + + InnovEnergy.Lib.Victron.VictronVRM + + diff --git a/typescript/Frontend/public/index.html b/typescript/Frontend/public/index.html index 1ae397d60..1ff97c832 100644 --- a/typescript/Frontend/public/index.html +++ b/typescript/Frontend/public/index.html @@ -1,20 +1,20 @@ - - - - - + + + + + - + - + Innovenergy - - - -
- - + To begin the development, run `npm start` or `yarn start`. + To create a production bundle, use `npm run build` or `yarn build`. +--> + diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index 067d09af0..2272c6a3e 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -2,7 +2,7 @@ import useToken from "./hooks/useToken"; import Login from "./Login"; import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom"; -import { Container, Grid, colors } from "@mui/material"; +import { Container, Grid, colors, Box } from "@mui/material"; import routes from "./routes.json"; import { IntlProvider, MessageFormatElement } from "react-intl"; import { useContext, useState } from "react"; @@ -12,11 +12,11 @@ import fr from "./lang/fr.json"; import LanguageSelect from "./components/Layout/LanguageSelect"; import LogoutButton from "./components/Layout/LogoutButton"; import Users from "./components/Users/Users"; -import NavigationButtons from "./components/Layout/NavigationButtons"; +import NavigationTabs from "./components/Layout/NavigationTabs"; import InstallationPage from "./components/Installations/InstallationPage"; import { UserContext } from "./components/Context/UserContextProvider"; import ResetPassword from "./ResetPassword"; -import innovenergyLogo from "./resources/test.gif"; +import innovenergyLogo from "./resources/innoveng_logo_on_orange.png"; const App = () => { const { token, setToken, removeToken } = useToken(); @@ -48,9 +48,9 @@ const App = () => { locale={language} defaultLocale="EN" > - - - + + + innovenergy logo { - - - + + + + + - - - } - /> - } - /> - } /> - + + + + + } + /> + } + /> + } /> + + + ); diff --git a/typescript/Frontend/src/components/Context/S3CredentialsContextProvider.tsx b/typescript/Frontend/src/components/Context/S3CredentialsContextProvider.tsx index c3f32d8bd..e41be783d 100644 --- a/typescript/Frontend/src/components/Context/S3CredentialsContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/S3CredentialsContextProvider.tsx @@ -1,29 +1,89 @@ import { createContext, ReactNode, useState } from "react"; import { I_User } from "../../util/user.util"; +import { I_Installation, S3Credentials } from "../../util/types"; +import { UnixTime } from "../../dataCache/time"; +import { FetchResult } from "../../dataCache/dataCache"; +import { DataRecord } from "../../dataCache/data"; +import { S3Access } from "../../dataCache/S3/S3Access"; +import { parseCsv } from "../../util/graph.util"; -interface InstallationContextProviderProps { - s3Credentials?: I_User; - setS3Credentials: (value: I_User) => void; +interface S3CredentialsContextProviderProps { + s3Credentials?: S3Credentials; + saveS3Credentials: (credentials: S3Credentials, id: string) => void; + fetchData: (timestamp: UnixTime) => Promise>; } -export const UserContext = createContext({ - s3Credentials: {} as I_User, - setS3Credentials: () => {}, -}); +export const S3CredentialsContext = + createContext({ + s3Credentials: {} as S3Credentials, + saveS3Credentials: () => {}, + fetchData: () => ({} as Promise>), + }); -const UserContextProvider = ({ children }: { children: ReactNode }) => { - const [s3Credentials, setS3Credentials] = useState(); +const S3CredentialsContextProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const [s3Credentials, setS3Credentials] = useState(); + + console.log("creds", s3Credentials); + const saveS3Credentials = (credentials: S3Credentials, id: string) => { + //const s3Bucket = id + "-3e5b3069-214a-43ee-8d85-57d72000c19d"; + /* const s3Bucket = "saliomameiringen"; + + setS3Credentials({ s3Bucket, ...credentials });*/ + setS3Credentials({ + s3Region: "sos-ch-dk-2", + s3Provider: "exo.io", + s3Key: "EXO15c0bf710e158e9b83270f0a", + s3Secret: "Dd5jYSiZtt_Zt5Ba5mDmaiLCdASUaKLfduSKY-SU-lg", + s3Bucket: "saliomameiringen", + }); + }; + + const fetchData = (timestamp: UnixTime): Promise> => { + const s3Path = `${timestamp.ticks}.csv`; + if (s3Credentials && s3Credentials.s3Bucket) { + const s3Access = new S3Access( + s3Credentials.s3Bucket, + s3Credentials.s3Region, + s3Credentials.s3Provider, + s3Credentials.s3Key, + s3Credentials.s3Secret + ); + return s3Access + .get(s3Path) + .then(async (r) => { + if (r.status === 404) { + return Promise.resolve(FetchResult.notAvailable); + } else if (r.status === 200) { + const text = await r.text(); + return parseCsv(text); + } else { + console.error("unexpected status code"); + return Promise.resolve(FetchResult.notAvailable); + } + }) + .catch((e) => { + console.log("catch", e); + return Promise.resolve(FetchResult.tryLater); + }); + } + return Promise.resolve(FetchResult.tryLater); + }; return ( - {children} - + ); }; -export default UserContextProvider; +export default S3CredentialsContextProvider; diff --git a/typescript/Frontend/src/components/Groups/Groups.tsx b/typescript/Frontend/src/components/Groups/Groups.tsx index 9c56e56fd..337bdd205 100644 --- a/typescript/Frontend/src/components/Groups/Groups.tsx +++ b/typescript/Frontend/src/components/Groups/Groups.tsx @@ -7,8 +7,27 @@ import GroupContextProvider from "../Context/GroupContextProvider"; import GroupTree from "./Tree/GroupTree"; import AccessManagement from "./AccessManagement/AccessManagement"; import Installation from "../Installations/Installation"; +import useRouteMatch from "../../hooks/useRouteMatch"; +import useInstallation from "../../hooks/useInstallation"; +import { useEffect } from "react"; const Groups = () => { + const routeMatch = useRouteMatch([ + routes.installations + routes.tree + ":view/:id", + ]); + const { + data: currentInstallation, + loading, + error, + getInstallation, + } = useInstallation(); + + const id = routeMatch?.params?.id; + + useEffect(() => { + // TODO remove if + getInstallation(id); + }, [id]); return ( @@ -25,7 +44,14 @@ const Groups = () => { /> } + element={ + + } /> diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx index b839a2337..140cb219f 100644 --- a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx +++ b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx @@ -11,6 +11,7 @@ import { instanceOfFolder } from "../../../util/group.util"; import { Grid, CircularProgress, useTheme } from "@mui/material"; import { useIntl } from "react-intl"; import { colors } from "../../.."; +import { color } from "chart.js/helpers"; const GroupTree = () => { const { setCurrentType, fetchData, data, loading } = useContext(GroupContext); @@ -54,7 +55,14 @@ const GroupTree = () => { label={element.name} onClick={() => setCurrentType(element.type)} sx={{ - ".MuiTreeItem-content": { paddingY: "5px" }, + ".MuiTreeItem-content": { py: "10px" }, + ".Mui-selected": { + backgroundColor: theme.palette.secondary.main, + }, + ".Mui-selected:hover": { + backgroundColor: theme.palette.secondary.main, + }, + ".Mui-focused": { backgroundColor: theme.palette.secondary.main }, bgcolor: colors.greyDark, borderRadius: 2, }} @@ -68,7 +76,7 @@ const GroupTree = () => { if (loading) { return ( - + ); } diff --git a/typescript/Frontend/src/components/Installations/Installation.tsx b/typescript/Frontend/src/components/Installations/Installation.tsx index ae6a2973b..a54b3c96b 100644 --- a/typescript/Frontend/src/components/Installations/Installation.tsx +++ b/typescript/Frontend/src/components/Installations/Installation.tsx @@ -1,35 +1,24 @@ import { Alert, Box, CircularProgress, useTheme } from "@mui/material"; import { AxiosError } from "axios"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import axiosConfig from "../../config/axiosConfig"; import { I_Installation } from "../../util/types"; import InstallationForm from "./InstallationForm"; import { colors } from "../.."; +import { S3CredentialsContext } from "../Context/S3CredentialsContextProvider"; interface I_InstallationProps { + loading?: boolean; + values?: I_Installation; + error?: AxiosError; hasMoveButton?: boolean; } -const Installation = (props: I_InstallationProps) => { - const { id } = useParams(); - const [values, setValues] = useState(); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(); - const theme = useTheme(); - useEffect(() => { - setLoading(true); - axiosConfig - .get("/GetInstallationById?id=" + id) - .then((res) => { - setValues(res.data); - setLoading(false); - }) - .catch((err: AxiosError) => { - setError(err); - setLoading(false); - }); - }, [id]); +const Installation = (props: I_InstallationProps) => { + const { hasMoveButton, values, loading, error } = props; + const { id } = useParams(); + const theme = useTheme(); if (values && values.id && values.id.toString() === id) { return ( @@ -51,7 +40,7 @@ const Installation = (props: I_InstallationProps) => { ); @@ -60,7 +49,7 @@ const Installation = (props: I_InstallationProps) => { - + ); } else if (error) { diff --git a/typescript/Frontend/src/components/Installations/InstallationList.tsx b/typescript/Frontend/src/components/Installations/InstallationList.tsx index 810ea29db..648f2ab62 100644 --- a/typescript/Frontend/src/components/Installations/InstallationList.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationList.tsx @@ -70,13 +70,12 @@ const InstallationList = (props: InstallationListProps) => { { to={ getPathWithoutId(routeMatch?.pattern?.path) + installation.id } - style={{ textDecoration: "none", color: colors.blueBlack }} + style={{ textDecoration: "none", color: colors.black }} > { return ( <> - + } /> - } /> + + + + } + /> ); diff --git a/typescript/Frontend/src/components/Installations/InstallationTabs.tsx b/typescript/Frontend/src/components/Installations/InstallationTabs.tsx index 1ebc80a36..4f69755b8 100644 --- a/typescript/Frontend/src/components/Installations/InstallationTabs.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationTabs.tsx @@ -25,75 +25,71 @@ const InstallationTabs = () => { if (id) { return ( - - - + + - - - - + + + ); } diff --git a/typescript/Frontend/src/components/Installations/Installations.tsx b/typescript/Frontend/src/components/Installations/Installations.tsx index cc53ca86c..6e3b822ad 100644 --- a/typescript/Frontend/src/components/Installations/Installations.tsx +++ b/typescript/Frontend/src/components/Installations/Installations.tsx @@ -13,20 +13,47 @@ import LogContextProvider from "../Context/LogContextProvider"; import useRouteMatch from "../../hooks/useRouteMatch"; import { Background } from "reactflow"; import { red } from "@mui/material/colors"; +import S3CredentialsContextProvider, { + S3CredentialsContext, +} from "../Context/S3CredentialsContextProvider"; +import { UnixTime } from "../../dataCache/time"; +import { FetchResult } from "../../dataCache/dataCache"; +import { DataRecord } from "../../dataCache/data"; +import { S3Access } from "../../dataCache/S3/S3Access"; +import { parseCsv } from "../../util/graph.util"; +import { useParams } from "react-router-dom"; +import { useContext, useEffect, useState } from "react"; +import axiosConfig from "../../config/axiosConfig"; +import { I_Installation } from "../../util/types"; +import { AxiosError } from "axios/index"; +import useInstallation from "../../hooks/useInstallation"; const Installations = () => { const routeMatch = useRouteMatch([ - routes.installations + routes.list + routes.log + ":id", + routes.installations + routes.list + ":view/:id", ]); + const { + data: currentInstallation, + loading, + error, + getInstallation, + } = useInstallation(); + const id = routeMatch?.params?.id; + + useEffect(() => { + // TODO remove if + getInstallation(id); + }, [id]); + return ( - + @@ -35,7 +62,13 @@ const Installations = () => { } + element={ + + } index /> } /> diff --git a/typescript/Frontend/src/components/Installations/LiveView.tsx b/typescript/Frontend/src/components/Installations/LiveView.tsx index 7d01b20e7..c1702d29d 100644 --- a/typescript/Frontend/src/components/Installations/LiveView.tsx +++ b/typescript/Frontend/src/components/Installations/LiveView.tsx @@ -1,7 +1,28 @@ import TopologyView from "./Log/TopologyView"; +import { Box, useTheme } from "@mui/material"; +import { colors } from "../../index"; const LiveView = () => { - return ; + const theme = useTheme(); + return ( + + + + ); }; export default LiveView; diff --git a/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx b/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx index 2caba1254..a06a7488b 100644 --- a/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx +++ b/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx @@ -49,7 +49,6 @@ const CheckboxTree = () => { ) => { event.stopPropagation(); }; - const theme = useTheme(); const renderTree = (data: TreeElement[]): ReactNode => { return data.map((element) => { const checked = checkedToggles.find((toggle) => element.id === toggle); @@ -66,19 +65,20 @@ const CheckboxTree = () => { handleClick(e, element, checked)} - style={{ - color: theme.palette.text.primary, - }} sx={{ paddingY: 0, }} + color="secondary" /> )} {splitName[splitName.length - 1]} } sx={{ - ".MuiTreeItem-content": { paddingY: "5px" }, + "& .MuiTreeItem-content": { + paddingY: "5px", + minHeight: "52px", + }, bgcolor: colors.greyDark, }} > diff --git a/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx b/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx index 7cebdd71e..3a25c78d5 100644 --- a/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx +++ b/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx @@ -14,6 +14,7 @@ interface DateRangePickerProps { range: Date[]; getCacheSeries: (xaxisRange0: Date, xaxisRange1: Date) => void; } + const DateRangePicker = (props: DateRangePickerProps) => { const theme = useTheme(); const { setRange, range, getCacheSeries } = props; @@ -42,10 +43,6 @@ const DateRangePicker = (props: DateRangePickerProps) => { label="From date" value={dayjs(range[0])} sx={{ - ".MuiInputLabel-root": { - color: colors.blueBlack, - }, - ".Mui-disabled": { color: "red", }, @@ -69,13 +66,12 @@ const DateRangePicker = (props: DateRangePickerProps) => { ".MuiInputLabel-root": { color: colors.blueBlack, }, - ".Mui-disabled": { color: "red", }, ".Mui-focused fieldset.MuiOutlinedInput-notchedOutline": { - borderColor: theme.palette.secondary.main, }, - + borderColor: theme.palette.secondary.main, + }, }} onChange={(newValue) => { if (newValue) { diff --git a/typescript/Frontend/src/components/Installations/Log/Log.tsx b/typescript/Frontend/src/components/Installations/Log/Log.tsx index d1156f14e..2cc0cdb1a 100644 --- a/typescript/Frontend/src/components/Installations/Log/Log.tsx +++ b/typescript/Frontend/src/components/Installations/Log/Log.tsx @@ -1,11 +1,28 @@ import React from "react"; import ScalarGraph from "./ScalarGraph"; +import { colors } from "../../../index"; +import { Box, useTheme } from "@mui/material"; const Log = () => { + const theme = useTheme(); return ( - <> + - + ); }; diff --git a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx index e507209aa..81ff0af62 100644 --- a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx +++ b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx @@ -22,40 +22,10 @@ import { FormattedMessage } from "react-intl"; import DateRangePicker from "./DateRangePicker"; import { LocalizationProvider } from "@mui/x-date-pickers"; import { Alert, useTheme } from "@mui/material"; +import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider"; const NUMBER_OF_NODES = 100; -const s3Access = new S3Access( - "saliomameiringen", - "sos-ch-dk-2", - "exo.io", - "EXO464a9ff62fdfa407aa742855", // key - "f2KtCWN4EHFqtvH2kotdyI0w5SjjdHVPAADdcD3ik8g" // secret -); - -export const fetchData = ( - timestamp: UnixTime -): Promise> => { - const s3Path = `${timestamp.ticks}.csv`; - return s3Access - .get(s3Path) - .then(async (r) => { - if (r.status === 404) { - return Promise.resolve(FetchResult.notAvailable); - } else if (r.status === 200) { - const text = await r.text(); - console.log("parsecsv", text, parseCsv(text)); - return parseCsv(text); - } else { - console.error("unexpected status code"); - return Promise.resolve(FetchResult.notAvailable); - } - }) - .catch((e) => { - return Promise.resolve(FetchResult.tryLater); - }); -}; - const ScalarGraph = () => { const timeRange = createTimes( UnixTime.now() /* .fromTicks(1682085650) */ @@ -70,6 +40,7 @@ const ScalarGraph = () => { const [plotTitles, setPlotTitles] = useState([]); const { toggles, setToggles, checkedToggles } = useContext(LogContext); + const { fetchData } = useContext(S3CredentialsContext); const times$ = useMemo(() => new BehaviorSubject(timeRange), []); @@ -93,10 +64,9 @@ const ScalarGraph = () => { return () => subscription.unsubscribe(); }, [toggles]); - const cache = useMemo( - () => new DataCache(fetchData, TimeSpan.fromSeconds(2)), - [] - ); + const cache = useMemo(() => { + return new DataCache(fetchData, TimeSpan.fromSeconds(2)); + }, []); const transformToGraphData = (input: RecordSeries): GraphData => { const transformedObject: any = {}; diff --git a/typescript/Frontend/src/components/Installations/Log/TopologyBox.tsx b/typescript/Frontend/src/components/Installations/Log/TopologyBox.tsx index 9f6a87ac2..1a6d8c5f8 100644 --- a/typescript/Frontend/src/components/Installations/Log/TopologyBox.tsx +++ b/typescript/Frontend/src/components/Installations/Log/TopologyBox.tsx @@ -20,7 +20,7 @@ const isInt = (value: number) => { return value % 1 === 0; }; -export const BOX_SIZE = 85; +export const BOX_SIZE = 75; const TopologyBox = (props: TopologyBoxProps) => { const { titleColor, boxColor } = getBoxColor(props.title); return ( diff --git a/typescript/Frontend/src/components/Installations/Log/TopologyView.tsx b/typescript/Frontend/src/components/Installations/Log/TopologyView.tsx index de1aed4be..fb5bec340 100644 --- a/typescript/Frontend/src/components/Installations/Log/TopologyView.tsx +++ b/typescript/Frontend/src/components/Installations/Log/TopologyView.tsx @@ -7,13 +7,14 @@ import { getAmount, getHighestConnectionValue, } from "../../../util/graph.util"; -import { fetchData } from "./ScalarGraph"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { FetchResult } from "../../../dataCache/dataCache"; import { FormattedMessage } from "react-intl"; +import { S3CredentialsContext } from "../../Context/S3CredentialsContextProvider"; const TopologyView = () => { const [values, setValues] = useState(null); + const { fetchData } = useContext(S3CredentialsContext); useEffect(() => { const interval = setInterval(() => { @@ -229,6 +230,7 @@ const TopologyView = () => { /> ); + return
TopologyView
; }; export default TopologyView; diff --git a/typescript/Frontend/src/components/Layout/InnovenergyTab.tsx b/typescript/Frontend/src/components/Layout/InnovenergyTab.tsx index 500058971..49336713e 100644 --- a/typescript/Frontend/src/components/Layout/InnovenergyTab.tsx +++ b/typescript/Frontend/src/components/Layout/InnovenergyTab.tsx @@ -17,13 +17,13 @@ const InnovenergyTab = (props: any) => { marginRight: theme.spacing(1), background: "0 0", border: "2px solid transparent", - bgcolor: theme.palette.primary.light, borderTopLeftRadius: "0.3rem", borderTopRightRadius: "0.3rem", padding: ".5rem 1rem", textDecoration: "none", transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`, "&.Mui-selected": { + backgroundColor: "#eaecf1", color: colors.black, borderColor: theme.palette.text.disabled, marginTop: "1px", diff --git a/typescript/Frontend/src/components/Layout/InnovenergyTabs.tsx b/typescript/Frontend/src/components/Layout/InnovenergyTabs.tsx index 714afad3d..bef153708 100644 --- a/typescript/Frontend/src/components/Layout/InnovenergyTabs.tsx +++ b/typescript/Frontend/src/components/Layout/InnovenergyTabs.tsx @@ -5,7 +5,7 @@ import { colors } from "index"; interface AntTabsProps { id: string; value?: string; - sx?: SxProps; + sx?: any; children: ReactNode; } @@ -24,7 +24,7 @@ const InnovenergyTabs = (props: AntTabsProps) => { }, "&.Mui-selected": { color: colors.black, - backgroundColor: " #90A7c5", + backgroundColor: "#eaecf1", borderColor: `#90A7c5 #90A7c5 #fff`, }, "& .MuiTabs-indicator": { @@ -34,6 +34,7 @@ const InnovenergyTabs = (props: AntTabsProps) => { }, "&.MuiTabs-root": { width: "100%", + ...(props.sx ? props.sx["&.MuiTabs-root"] : {}), }, }} > diff --git a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx index 7e2cd6f1f..c342118e2 100644 --- a/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx +++ b/typescript/Frontend/src/components/Layout/InnovenergyTextfield.tsx @@ -11,6 +11,7 @@ import { TextField, useTheme, } from "@mui/material"; +import { colors } from "../../index"; export interface I_InnovenergyTextfieldProps { id: string; @@ -28,18 +29,6 @@ export interface I_InnovenergyTextfieldProps { export const IePropertyGrid = (props: { rows: I_InnovenergyTextfieldProps[]; }) => { - const theme = useTheme(); - - const findLongestLength = () => { - return ( - 11 * - props.rows.reduce( - (acc, curr) => (acc > curr.label.length ? acc : curr.label.length), - 0 - ) - ); - }; - return (
{props.rows.map((prop) => ( - {prop.label} + {prop.label} ))}
{props.rows.map((element) => { return ( { +const NavigationTabs = () => { const theme = useTheme(); const routeMatch = useRouteMatch([ routes.installations + "*", routes.users + "*", ]); - const intl = useIntl(); return ( <> @@ -25,7 +29,7 @@ const NavigationButtons = () => { sx={{ bgcolor: theme.palette.primary.main, "&.Mui-selected": { - backgroundColor: theme.palette.primary.main, + backgroundColor: "#eaecf1", borderColor: theme.palette.text.disabled, borderBottom: 0, mb: -1 / 8, @@ -46,7 +50,7 @@ const NavigationButtons = () => { sx={{ bgcolor: theme.palette.primary.main, "&.Mui-selected": { - backgroundColor: theme.palette.primary.main, + backgroundColor: "#eaecf1", borderColor: theme.palette.text.disabled, borderBottom: 0, mb: -1 / 8, @@ -63,4 +67,4 @@ const NavigationButtons = () => { ); }; -export default NavigationButtons; +export default NavigationTabs; diff --git a/typescript/Frontend/src/components/Layout/Search.tsx b/typescript/Frontend/src/components/Layout/Search.tsx index 8b510b540..851d0042f 100644 --- a/typescript/Frontend/src/components/Layout/Search.tsx +++ b/typescript/Frontend/src/components/Layout/Search.tsx @@ -10,7 +10,6 @@ import { colors } from "index"; import { FC, useState } from "react"; import { useIntl } from "react-intl"; - interface SearchSidebarProps { listComponent: FC<{ searchQuery: string }>; id: string; @@ -22,7 +21,6 @@ const SearchInputTextfield = styled((props: TextFieldProps) => ( InputProps={{ disableUnderline: true } as Partial} {...props} /> - ))(({ theme }) => ({ "& .MuiFilledInput-root": { overflow: "hidden", @@ -41,11 +39,9 @@ const SearchInputTextfield = styled((props: TextFieldProps) => ( backgroundColor: theme.palette.primary.dark, }, "&.Mui-focused": { - backgroundColor: theme.palette.primary.dark, borderColor: theme.palette.secondary.main, }, - }, })); @@ -55,28 +51,17 @@ const SearchSidebar = (props: SearchSidebarProps) => { const intl = useIntl(); const theme = useTheme(); - - return (
- {/* setSearchQuery(e.target.value)} - /> */} setSearchQuery(e.target.value)} diff --git a/typescript/Frontend/src/config/axiosConfig.tsx b/typescript/Frontend/src/config/axiosConfig.tsx index 38e7e1f81..75b3924a1 100644 --- a/typescript/Frontend/src/config/axiosConfig.tsx +++ b/typescript/Frontend/src/config/axiosConfig.tsx @@ -1,25 +1,25 @@ -import axios from "axios"; - -export const axiosConfigWithoutToken = axios.create({ - baseURL: "https://localhost:7087/api", -}); - -const axiosConfig = axios.create({ - baseURL: "https://localhost:7087/api", -}); -axiosConfig.defaults.params = {}; -axiosConfig.interceptors.request.use( - (config) => { - const tokenString = localStorage.getItem("token"); - const token = tokenString !== null ? JSON.parse(tokenString) : ""; - if (token) { - config.params["authToken"] = token; - } - return config; - }, - (error) => { - return Promise.reject(error); - } -); - -export default axiosConfig; +import axios from "axios"; + +export const axiosConfigWithoutToken = axios.create({ + baseURL: "https://localhost:7087/api", +}); + +const axiosConfig = axios.create({ + baseURL: "https://localhost:7087/api", +}); +axiosConfig.defaults.params = {}; +axiosConfig.interceptors.request.use( + (config) => { + const tokenString = localStorage.getItem("token"); + const token = tokenString !== null ? JSON.parse(tokenString) : ""; + if (token) { + config.params["authToken"] = token; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +export default axiosConfig; diff --git a/typescript/Frontend/src/dataCache/dataCache.ts b/typescript/Frontend/src/dataCache/dataCache.ts index 33ba43dab..7a35dd8bb 100644 --- a/typescript/Frontend/src/dataCache/dataCache.ts +++ b/typescript/Frontend/src/dataCache/dataCache.ts @@ -138,10 +138,13 @@ export default class DataCache> { const onSuccess = (data: FetchResult) => { if (data === FetchResult.tryLater) { console.warn(FetchResult.tryLater); - return; } - const value = data === FetchResult.notAvailable ? undefined : data; + const value = + data === FetchResult.notAvailable || data === FetchResult.tryLater + ? undefined + : data; + // @ts-ignore this.cache.insert(value, t); }; diff --git a/typescript/Frontend/src/hooks/useInstallation.tsx b/typescript/Frontend/src/hooks/useInstallation.tsx new file mode 100644 index 000000000..369c611c3 --- /dev/null +++ b/typescript/Frontend/src/hooks/useInstallation.tsx @@ -0,0 +1,43 @@ +import { useContext, useState } from "react"; +import axiosConfig from "../config/axiosConfig"; +import { I_Installation } from "../util/types"; +import { AxiosError } from "axios"; +import { S3CredentialsContext } from "../components/Context/S3CredentialsContextProvider"; + +const useInstallation = () => { + const [data, setData] = useState(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + + const { saveS3Credentials } = useContext(S3CredentialsContext); + + const getInstallation = (id?: string) => { + if (id) { + setLoading(true); + axiosConfig + .get("/GetInstallationById?id=" + id) + .then(({ data }: { data: I_Installation }) => { + const { s3Region, s3Provider, s3Secret, s3Key } = data; + setData(data); + setLoading(false); + // TODO remove if + if (id) { + saveS3Credentials({ s3Region, s3Provider, s3Secret, s3Key }, id); + } + }) + .catch((err: AxiosError) => { + setError(err); + setLoading(false); + }); + } + }; + + return { + data, + loading, + error, + getInstallation, + }; +}; + +export default useInstallation; diff --git a/typescript/Frontend/src/index.tsx b/typescript/Frontend/src/index.tsx index 9f610827c..e4bff8ba6 100644 --- a/typescript/Frontend/src/index.tsx +++ b/typescript/Frontend/src/index.tsx @@ -3,67 +3,66 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; -import {createTheme, ThemeProvider} from "@mui/material"; +import { createTheme, ThemeProvider } from "@mui/material"; import UserContextProvider from "./components/Context/UserContextProvider"; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement + document.getElementById("root") as HTMLElement ); export const colors = { - black: "#000000", - blueBlack: "#2b3e54", - borderColor: "#a8b0be", + black: "#000000", + blueBlack: "#2b3e54", + borderColor: "#a8b0be", - background: "#f3f4f7", - //change this color in index.css too + background: "#f3f4f7", + //change this color in index.css too - greyDark: "#d1d7de", - greyLight: "#e1e4e7", + greyDark: "#d1d7de", + greyLight: "#e1e4e7", - orangeSelected: "#ffd280", - orangeHover: "#ffe4b3", + orangeSelected: "#f7b34d", + orangeHover: "#fad399", - orangeDark: "#ffc04d", + orangeDark: "#ffc04d", }; const theme = createTheme({ - components: { - MuiButtonBase: { - defaultProps: { - disableRipple: true, - }, - }, + components: { + MuiButtonBase: { + defaultProps: { + disableRipple: true, + }, }, - palette: { - text: { - primary: colors.blueBlack, - secondary: "#000000", - disabled: colors.borderColor, - }, - primary: { - main: colors.background, - light: colors.greyLight, - dark: colors.greyDark, - }, - secondary: { - main: colors.orangeSelected, - light: colors.orangeHover, - dark: colors.orangeDark, - }, + }, + palette: { + text: { + primary: colors.black, + disabled: colors.borderColor, }, - typography: { - fontFamily: `"Ubuntu", sans-serif`, - fontWeightRegular: 300, + primary: { + main: colors.background, + light: colors.greyLight, + dark: colors.greyDark, }, + secondary: { + main: colors.orangeSelected, + light: colors.orangeHover, + dark: colors.orangeDark, + }, + }, + typography: { + fontFamily: `"Ubuntu", sans-serif`, + fontWeightRegular: 300, + }, }); root.render( - - - - - + + + + + ); // If you want to start measuring performance in your app, pass a function diff --git a/typescript/Frontend/src/resources/innoveng_logo_on_orange.png b/typescript/Frontend/src/resources/innoveng_logo_on_orange.png new file mode 100644 index 000000000..4f1714ecd Binary files /dev/null and b/typescript/Frontend/src/resources/innoveng_logo_on_orange.png differ diff --git a/typescript/Frontend/src/resources/test.gif b/typescript/Frontend/src/resources/test.gif deleted file mode 100644 index 3b135931e..000000000 Binary files a/typescript/Frontend/src/resources/test.gif and /dev/null differ diff --git a/typescript/Frontend/src/util/types.tsx b/typescript/Frontend/src/util/types.tsx index 5b6bfcc25..81ff48c34 100644 --- a/typescript/Frontend/src/util/types.tsx +++ b/typescript/Frontend/src/util/types.tsx @@ -1,5 +1,13 @@ // TODO add if required or not -export interface I_Installation { +export interface S3Credentials { + s3Region: string; + s3Provider: string; + s3Key: string; + s3Secret: string; + s3Bucket?: string; +} + +export interface I_Installation extends S3Credentials { type: string; title?: string; status?: number; @@ -15,12 +23,6 @@ export interface I_Installation { name: string; information: string; parentId: number; - - s3Bucket: string; - s3Region: string; - s3Provider: string; - s3Key: string; - s3Secret: string; } export interface I_Folder {