From 5277c3518f31f25fbca28aaccc5f445d2de5a24c Mon Sep 17 00:00:00 2001 From: Malte Bitter Date: Tue, 7 Feb 2023 13:08:25 +0100 Subject: [PATCH] WIP --- TestApp/Configuration/ConfigurationManager.cs | 144 +++++++++++ TestApp/Configuration/Settings.cs | 211 +++++++++++++++ TestApp/Driver/VestelEvc04.cs | 56 +++- TestApp/EnergyManager.cs | 242 +++++------------- TestApp/PidController.cs | 31 +++ TestApp/Program.cs | 117 +++++++-- 6 files changed, 584 insertions(+), 217 deletions(-) create mode 100644 TestApp/Configuration/ConfigurationManager.cs create mode 100644 TestApp/Configuration/Settings.cs create mode 100644 TestApp/PidController.cs diff --git a/TestApp/Configuration/ConfigurationManager.cs b/TestApp/Configuration/ConfigurationManager.cs new file mode 100644 index 0000000..ac671f7 --- /dev/null +++ b/TestApp/Configuration/ConfigurationManager.cs @@ -0,0 +1,144 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; + +namespace TestApp.Configuration +{ + internal class ConfigurationManager + { + const string _CONFIGURATION_FILE_PATH = "/data/TestApp.json"; + + private Thread _FileWatcherThread; + private FileInfo _LastFileInfo; + + private Settings _Settings; + public Settings Settings { + get => _Settings; + [MemberNotNull(nameof(_Settings))] + private set { + _Settings = value; + SettingsChanged?.Invoke(this, Settings); + } + } + + public ConfigurationManager() + { + _LastFileInfo = new FileInfo(_CONFIGURATION_FILE_PATH); + LoadSettings(); + _FileWatcherThread = new Thread(FileWatcherLoop) { IsBackground = true }; + _FileWatcherThread.Start(); + } + + private void FileWatcherLoop() + { + while (true) { + var currentFileInfo = new FileInfo(_CONFIGURATION_FILE_PATH); + if (ConfigFileChanged(currentFileInfo)) + { + LoadSettings(); + _LastFileInfo = currentFileInfo; + } + Thread.Sleep(1000); + } + } + + private bool ConfigFileChanged(FileInfo currentFileInfo) { + if (!currentFileInfo.Exists) + { + return _LastFileInfo.Exists; + } + + if (!_LastFileInfo.Exists) + { + return true; + } + + return currentFileInfo.LastWriteTimeUtc != _LastFileInfo.LastWriteTimeUtc; + } + + [MemberNotNull(nameof(_Settings))] + private void LoadSettings() { + Console.WriteLine("(Re)Loading configuration file ..."); + + if (!File.Exists(_CONFIGURATION_FILE_PATH)) + { + InitDefaultSettings(); + SaveSettings(); + return; + } + + try + { + var fileContent = File.ReadAllText(_CONFIGURATION_FILE_PATH); + var fileSettings = JsonSerializer.Deserialize(fileContent) + ?? throw new ArgumentNullException("Deserialization returned null"); + Settings = fileSettings; + } catch (Exception ex) + { + Console.WriteLine($"Error reading configuration file '{_CONFIGURATION_FILE_PATH}': {ex.Message}"); + if (_Settings is null) { + Console.WriteLine("Using build in default settings!"); + InitDefaultSettings(); + } + } + } + + private void SaveSettings() + { + try + { + var fileContent = JsonSerializer.Serialize(Settings); + File.WriteAllText(_CONFIGURATION_FILE_PATH, fileContent); + _LastFileInfo = new FileInfo(_CONFIGURATION_FILE_PATH); + } catch (Exception ex) + { + Console.WriteLine($"Error saving configuration file '{_CONFIGURATION_FILE_PATH}': {ex.Message}"); + } + } + + [MemberNotNull(nameof(_Settings))] + private void InitDefaultSettings() { + Settings = new Settings() { + ChargerDriver = Settings.DEFAULT_CHARGER_DRIVER, + ChargerAddress = Settings.DEFAULT_CHARGER_ADDRESS, + ChargerPort = Settings.DEFAULT_CHARGER_PORT, + Mode = 1, + ModeSettings = new ModeSettings[] { + new ModeSettings + { + ProcessVariable = ProcessVariable.ActivePowerPositive, + SetPoint = 32, + NegativePid = new PidSetting() { + KP = 0, + KI = 1, + KD = 0, + }, + PositivePid = new PidSetting() + { + KP = 0, + KI = 1, + KD = 0, + } + }, + new ModeSettings + { + ProcessVariable = ProcessVariable.ActivePowerNegative, + SetPoint = 100, + NegativePid = new PidSetting() { + KP = 0, + KI = 1, + KD = 0, + }, + PositivePid = new PidSetting() + { + KP = 0, + KI = 1, + KD = 0, + } + }, + } + }; + } + + public event EventHandler? SettingsChanged; + } +} diff --git a/TestApp/Configuration/Settings.cs b/TestApp/Configuration/Settings.cs new file mode 100644 index 0000000..e70542c --- /dev/null +++ b/TestApp/Configuration/Settings.cs @@ -0,0 +1,211 @@ +namespace TestApp.Configuration +{ + public class Settings + { + internal const string DEFAULT_CHARGER_DRIVER = "VestelEvc04"; + internal const string DEFAULT_CHARGER_ADDRESS = "192.168.178.100"; + internal const int DEFAULT_CHARGER_PORT = 512; + + public string? ChargerDriver { get; set; } + public string? ChargerAddress { get; set; } + public int? ChargerPort { get; set; } + + public int Mode { get; set; } + + public ModeSettings[]? ModeSettings { get; set; } + } + + + public class PidSetting { + public double KP { get; set; } + public double KI { get; set; } + public double KD { get; set; } + } + + public class ModeSettings { + public ProcessVariable ProcessVariable { get; set; } + public double SetPoint { get; set; } + public PidSetting? PositivePid { get; set; } + public PidSetting? NegativePid { get; set; } + } + + public enum ProcessVariable + { + /// in W + ActivePowerPositive, + + /// in Wh + ActiveEnergyPositive, + + /// in W + ActivePowerNegative, + + /// in Wh + ActiveEnergyNegative, + + /// in var + ReactivePowerPositive, + + /// in varh + ReactiveEnergyPositive, + + /// in var + ReactivePowerNegative, + + /// in varh + ReactiveEnergyNegative, + + /// in VA + ApparentPowerPositive, + + /// in VAh + ApparentEnergyPositive, + + /// in VA + ApparentPowerNegative, + + /// in VAh + ApparentEnergyNegative, + + /// in - + PowerFactor, + + /// in Hz + SupplyFrequency, + + /// in W + ActivePowerPositiveL1, + + /// in Wh + ActiveEnergyPositiveL1, + + /// in W + ActivePowerNegativeL1, + + /// in Wh + ActiveEnergyNegativeL1, + + /// in var + ReactivePowerPositiveL1, + + /// in varh + ReactiveEnergyPositiveL1, + + /// in var + ReactivePowerNegativeL1, + + /// in varh + ReactiveEnergyNegativeL1, + + /// in VA + ApparentPowerPositiveL1, + + /// in VAh + ApparentEnergyPositiveL1, + + /// in VA + ApparentPowerNegativeL1, + + /// in VAh + ApparentEnergyNegativeL1, + + /// in A + CurrentL1, + + /// in V + VoltageL1, + + /// in - + PowerFactorL1, + + /// in W + ActivePowerPositiveL2, + + /// in Wh + ActiveEnergyPositiveL2, + + /// in W + ActivePowerNegativeL2, + + /// in Wh + ActiveEnergyNegativeL2, + + /// in var + ReactivePowerPositiveL2, + + /// in varh + ReactiveEnergyPositiveL2, + + /// in var + ReactivePowerNegativeL2, + + /// in varh + ReactiveEnergyNegativeL2, + + /// in VA + ApparentPowerPositiveL2, + + /// in VAh + ApparentEnergyPositiveL2, + + /// in VA + ApparentPowerNegativeL2, + + /// in VAh + ApparentEnergyNegativeL2, + + /// in A + CurrentL2, + + /// in V + VoltageL2, + + /// in - + PowerFactorL2, + + /// in W + ActivePowerPositiveL3, + + /// in Wh + ActiveEnergyPositiveL3, + + /// in W + ActivePowerNegativeL3, + + /// in Wh + ActiveEnergyNegativeL3, + + /// in var + ReactivePowerPositiveL3, + + /// in varh + ReactiveEnergyPositiveL3, + + /// in var + ReactivePowerNegativeL3, + + /// in varh + ReactiveEnergyNegativeL3, + + /// in VA + ApparentPowerPositiveL3, + + /// in VAh + ApparentEnergyPositiveL3, + + /// in VA + ApparentPowerNegativeL3, + + /// in VAh + ApparentEnergyNegativeL3, + + /// in A + CurrentL3, + + /// in V + VoltageL3, + + /// in - + PowerFactorL3, + } +} diff --git a/TestApp/Driver/VestelEvc04.cs b/TestApp/Driver/VestelEvc04.cs index 637b797..1bfce1b 100644 --- a/TestApp/Driver/VestelEvc04.cs +++ b/TestApp/Driver/VestelEvc04.cs @@ -3,14 +3,35 @@ using System.Net.Sockets; namespace TestApp.Driver { - internal class VestelEvc04 : IChargerDriver + internal class VestelEvc04 : IChargerDriver, IDisposable { private IModbusMaster? _Modbus; private bool _Running; private Thread _HeartbeatThread; - public string HostAddress { get; set; } = "192.168.178.100"; - public int Port { get; set; } = 502; + private string _HostAddress = "192.168.178.100"; + public string HostAddress + { + get => _HostAddress; + set + { + if (_HostAddress == value) { return; } + _HostAddress = value; + Disconnect(); + } + } + + private int _Port = 502; + public int Port + { + get => _Port; + set + { + if (_Port == value) { return; } + _Port = value; + Disconnect(); + } + } public VestelEvc04() { @@ -22,6 +43,7 @@ namespace TestApp.Driver private void Connect() { + if (!_Running) { return; } var factory = new ModbusFactory(); var tcpClient = new TcpClient(HostAddress, Port); _Modbus = factory.CreateMaster(tcpClient); @@ -36,9 +58,9 @@ namespace TestApp.Driver private void HeartbeatLoop() { - try + while (_Running) { - while (_Running) + try { if (_Modbus is null) { @@ -47,22 +69,24 @@ namespace TestApp.Driver if (_Modbus is null) { continue; } var heardbeatValue = _Modbus.ReadHoldingRegisters(255, 6000, 1)[0]; - if (heardbeatValue == 0) { + if (heardbeatValue == 0) + { _Modbus.WriteSingleRegister(255, 6000, 1); } - - Thread.Sleep(100); } - } - catch (Exception e) - { - System.Diagnostics.Debug.WriteLine(e.Message); - Disconnect(); + catch (Exception e) + { + System.Diagnostics.Debug.WriteLine(e.Message); + Disconnect(); + } + Thread.Sleep(100); } } public void SetLoadingCurrent(double value) { + if (!_Running) { return; } + try { if (_Modbus is null) @@ -80,5 +104,11 @@ namespace TestApp.Driver Disconnect(); } } + + public void Dispose() + { + _Running = false; + Disconnect(); + } } } diff --git a/TestApp/EnergyManager.cs b/TestApp/EnergyManager.cs index 6e472e3..970c8e3 100644 --- a/TestApp/EnergyManager.cs +++ b/TestApp/EnergyManager.cs @@ -1,7 +1,7 @@ using MQTTnet; using MQTTnet.Client; using System.Collections.Immutable; -using System.Runtime.CompilerServices; +using TestApp.Configuration; namespace TestApp { @@ -79,182 +79,70 @@ namespace TestApp return factor * value; } - /// in W - public double ActivePowerPositive => GetValue(1099528667391UL, 0.001); - - /// in Wh - public double ActiveEnergyPositive => GetValue(1099528929535UL, 0.001); - - /// in W - public double ActivePowerNegative => GetValue(1099545444607UL, 0.001); - - /// in Wh - public double ActiveEnergyNegative => GetValue(1099545706751UL, 0.001); - - /// in var - public double ReactivePowerPositive => GetValue(1099562221823UL, 0.001); - - /// in varh - public double ReactiveEnergyPositive => GetValue(1099562483967UL, 0.001); - - /// in var - public double ReactivePowerNegative => GetValue(1099578999039UL, 0.001); - - /// in varh - public double ReactiveEnergyNegative => GetValue(1099579261183UL, 0.001); - - /// in VA - public double ApparentPowerPositive => GetValue(1099662885119UL, 0.001); - - /// in VAh - public double ApparentEnergyPositive => GetValue(1099663147263UL, 0.001); - - /// in VA - public double ApparentPowerNegative => GetValue(1099679662335UL, 0.001); - - /// in VAh - public double ApparentEnergyNegative => GetValue(1099679924479UL, 0.001); - - /// in - - public double PowerFactor => GetValue(1099729993983UL, 0.001); - - /// in Hz - public double SupplyFrequency => GetValue(1099746771199UL, 0.001); - - /// in W - public double ActivePowerPositiveL1 => GetValue(1099864211711UL, 0.001); - - /// in Wh - public double ActiveEnergyPositiveL1 => GetValue(1099864473855UL, 0.001); - - /// in W - public double ActivePowerNegativeL1 => GetValue(1099880988927UL, 0.001); - - /// in Wh - public double ActiveEnergyNegativeL1 => GetValue(1099881251071UL, 0.001); - - /// in var - public double ReactivePowerPositiveL1 => GetValue(1099897766143UL, 0.001); - - /// in varh - public double ReactiveEnergyPositiveL1 => GetValue(1099898028287UL, 0.001); - - /// in var - public double ReactivePowerNegativeL1 => GetValue(1099914543359UL, 0.001); - - /// in varh - public double ReactiveEnergyNegativeL1 => GetValue(1099914805503UL, 0.001); - - /// in VA - public double ApparentPowerPositiveL1 => GetValue(1099998429439UL, 0.001); - - /// in VAh - public double ApparentEnergyPositiveL1 => GetValue(1099998691583UL, 0.001); - - /// in VA - public double ApparentPowerNegativeL1 => GetValue(1100015206655UL, 0.001); - - /// in VAh - public double ApparentEnergyNegativeL1 => GetValue(1100015468799UL, 0.001); - - /// in A - public double CurrentL1 => GetValue(1100031983871UL, 0.001); - - /// in V - public double VoltageL1 => GetValue(1100048761087UL, 0.001); - - /// in - - public double PowerFactorL1 => GetValue(1100065538303UL, 0.001); - - /// in W - public double ActivePowerPositiveL2 => GetValue(1100199756031UL, 0.001); - - /// in Wh - public double ActiveEnergyPositiveL2 => GetValue(1100200018175UL, 0.001); - - /// in W - public double ActivePowerNegativeL2 => GetValue(1100216533247UL, 0.001); - - /// in Wh - public double ActiveEnergyNegativeL2 => GetValue(1100216795391UL, 0.001); - - /// in var - public double ReactivePowerPositiveL2 => GetValue(1100233310463UL, 0.001); - - /// in varh - public double ReactiveEnergyPositiveL2 => GetValue(1100233572607UL, 0.001); - - /// in var - public double ReactivePowerNegativeL2 => GetValue(1100250087679UL, 0.001); - - /// in varh - public double ReactiveEnergyNegativeL2 => GetValue(1100250349823UL, 0.001); - - /// in VA - public double ApparentPowerPositiveL2 => GetValue(1100333973759UL, 0.001); - - /// in VAh - public double ApparentEnergyPositiveL2 => GetValue(1100334235903UL, 0.001); - - /// in VA - public double ApparentPowerNegativeL2 => GetValue(1100350750975UL, 0.001); - - /// in VAh - public double ApparentEnergyNegativeL2 => GetValue(1100351013119UL, 0.001); - - /// in A - public double CurrentL2 => GetValue(1100367528191UL, 0.001); - - /// in V - public double VoltageL2 => GetValue(1100384305407UL, 0.001); - - /// in - - public double PowerFactorL2 => GetValue(1100401082623UL, 0.001); - - /// in W - public double ActivePowerPositiveL3 => GetValue(1100535300351UL, 0.001); - - /// in Wh - public double ActiveEnergyPositiveL3 => GetValue(1100535562495UL, 0.001); - - /// in W - public double ActivePowerNegativeL3 => GetValue(1100552077567UL, 0.001); - - /// in Wh - public double ActiveEnergyNegativeL3 => GetValue(1100552339711UL, 0.001); - - /// in var - public double ReactivePowerPositiveL3 => GetValue(1100568854783UL, 0.001); - - /// in varh - public double ReactiveEnergyPositiveL3 => GetValue(1100569116927UL, 0.001); - - /// in var - public double ReactivePowerNegativeL3 => GetValue(1100585631999UL, 0.001); - - /// in varh - public double ReactiveEnergyNegativeL3 => GetValue(1100585894143UL, 0.001); - - /// in VA - public double ApparentPowerPositiveL3 => GetValue(1100669518079UL, 0.001); - - /// in VAh - public double ApparentEnergyPositiveL3 => GetValue(1100669780223UL, 0.001); - - /// in VA - public double ApparentPowerNegativeL3 => GetValue(1100686295295UL, 0.001); - - /// in VAh - public double ApparentEnergyNegativeL3 => GetValue(1100686557439UL, 0.001); - - /// in A - public double CurrentL3 => GetValue(1100703072511UL, 0.001); - - /// in V - public double VoltageL3 => GetValue(1100719849727UL, 0.001); - - /// in - - public double PowerFactorL3 => GetValue(1100736626943UL, 0.001); + public double GetValue(ProcessVariable processVariable) { + return processVariable switch { + ProcessVariable.ActivePowerPositive => GetValue(1099528667391UL, 0.001), + ProcessVariable.ActiveEnergyPositive => GetValue(1099528929535UL, 0.001), + ProcessVariable.ActivePowerNegative => GetValue(1099545444607UL, 0.001), + ProcessVariable.ActiveEnergyNegative => GetValue(1099545706751UL, 0.001), + ProcessVariable.ReactivePowerPositive => GetValue(1099562221823UL, 0.001), + ProcessVariable.ReactiveEnergyPositive => GetValue(1099562483967UL, 0.001), + ProcessVariable.ReactivePowerNegative => GetValue(1099578999039UL, 0.001), + ProcessVariable.ReactiveEnergyNegative => GetValue(1099579261183UL, 0.001), + ProcessVariable.ApparentPowerPositive => GetValue(1099662885119UL, 0.001), + ProcessVariable.ApparentEnergyPositive => GetValue(1099663147263UL, 0.001), + ProcessVariable.ApparentPowerNegative => GetValue(1099679662335UL, 0.001), + ProcessVariable.ApparentEnergyNegative => GetValue(1099679924479UL, 0.001), + ProcessVariable.PowerFactor => GetValue(1099729993983UL, 0.001), + ProcessVariable.SupplyFrequency => GetValue(1099746771199UL, 0.001), + ProcessVariable.ActivePowerPositiveL1 => GetValue(1099864211711UL, 0.001), + ProcessVariable.ActiveEnergyPositiveL1 => GetValue(1099864473855UL, 0.001), + ProcessVariable.ActivePowerNegativeL1 => GetValue(1099880988927UL, 0.001), + ProcessVariable.ActiveEnergyNegativeL1 => GetValue(1099881251071UL, 0.001), + ProcessVariable.ReactivePowerPositiveL1 => GetValue(1099897766143UL, 0.001), + ProcessVariable.ReactiveEnergyPositiveL1 => GetValue(1099898028287UL, 0.001), + ProcessVariable.ReactivePowerNegativeL1 => GetValue(1099914543359UL, 0.001), + ProcessVariable.ReactiveEnergyNegativeL1 => GetValue(1099914805503UL, 0.001), + ProcessVariable.ApparentPowerPositiveL1 => GetValue(1099998429439UL, 0.001), + ProcessVariable.ApparentEnergyPositiveL1 => GetValue(1099998691583UL, 0.001), + ProcessVariable.ApparentPowerNegativeL1 => GetValue(1100015206655UL, 0.001), + ProcessVariable.ApparentEnergyNegativeL1 => GetValue(1100015468799UL, 0.001), + ProcessVariable.CurrentL1 => GetValue(1100031983871UL, 0.001), + ProcessVariable.VoltageL1 => GetValue(1100048761087UL, 0.001), + ProcessVariable.PowerFactorL1 => GetValue(1100065538303UL, 0.001), + ProcessVariable.ActivePowerPositiveL2 => GetValue(1100199756031UL, 0.001), + ProcessVariable.ActiveEnergyPositiveL2 => GetValue(1100200018175UL, 0.001), + ProcessVariable.ActivePowerNegativeL2 => GetValue(1100216533247UL, 0.001), + ProcessVariable.ActiveEnergyNegativeL2 => GetValue(1100216795391UL, 0.001), + ProcessVariable.ReactivePowerPositiveL2 => GetValue(1100233310463UL, 0.001), + ProcessVariable.ReactiveEnergyPositiveL2 => GetValue(1100233572607UL, 0.001), + ProcessVariable.ReactivePowerNegativeL2 => GetValue(1100250087679UL, 0.001), + ProcessVariable.ReactiveEnergyNegativeL2 => GetValue(1100250349823UL, 0.001), + ProcessVariable.ApparentPowerPositiveL2 => GetValue(1100333973759UL, 0.001), + ProcessVariable.ApparentEnergyPositiveL2 => GetValue(1100334235903UL, 0.001), + ProcessVariable.ApparentPowerNegativeL2 => GetValue(1100350750975UL, 0.001), + ProcessVariable.ApparentEnergyNegativeL2 => GetValue(1100351013119UL, 0.001), + ProcessVariable.CurrentL2 => GetValue(1100367528191UL, 0.001), + ProcessVariable.VoltageL2 => GetValue(1100384305407UL, 0.001), + ProcessVariable.PowerFactorL2 => GetValue(1100401082623UL, 0.001), + ProcessVariable.ActivePowerPositiveL3 => GetValue(1100535300351UL, 0.001), + ProcessVariable.ActiveEnergyPositiveL3 => GetValue(1100535562495UL, 0.001), + ProcessVariable.ActivePowerNegativeL3 => GetValue(1100552077567UL, 0.001), + ProcessVariable.ActiveEnergyNegativeL3 => GetValue(1100552339711UL, 0.001), + ProcessVariable.ReactivePowerPositiveL3 => GetValue(1100568854783UL, 0.001), + ProcessVariable.ReactiveEnergyPositiveL3 => GetValue(1100569116927UL, 0.001), + ProcessVariable.ReactivePowerNegativeL3 => GetValue(1100585631999UL, 0.001), + ProcessVariable.ReactiveEnergyNegativeL3 => GetValue(1100585894143UL, 0.001), + ProcessVariable.ApparentPowerPositiveL3 => GetValue(1100669518079UL, 0.001), + ProcessVariable.ApparentEnergyPositiveL3 => GetValue(1100669780223UL, 0.001), + ProcessVariable.ApparentPowerNegativeL3 => GetValue(1100686295295UL, 0.001), + ProcessVariable.ApparentEnergyNegativeL3 => GetValue(1100686557439UL, 0.001), + ProcessVariable.CurrentL3 => GetValue(1100703072511UL, 0.001), + ProcessVariable.VoltageL3 => GetValue(1100719849727UL, 0.001), + ProcessVariable.PowerFactorL3 => GetValue(1100736626943UL, 0.001), + _ => double.NaN, + }; + } } } diff --git a/TestApp/PidController.cs b/TestApp/PidController.cs new file mode 100644 index 0000000..f43c0fd --- /dev/null +++ b/TestApp/PidController.cs @@ -0,0 +1,31 @@ +using TestApp.Configuration; + +namespace TestApp +{ + internal class PidController + { + private DateTime _PreviousUpdateTime; + private double _PreviousError; + private double _Integral; + + public PidSetting PositiveSettings { get; set; } = new PidSetting(); + public PidSetting NegativeSettings { get; set; } = new PidSetting(); + public double SetPoint { get; set; } + + public double Update(double measurement) { + var error = measurement - SetPoint; + var dt = (DateTime.UtcNow - _PreviousUpdateTime).TotalSeconds; + + var settings = error > 0 ? PositiveSettings : NegativeSettings; + + var proportional = error * settings.KP; + _Integral += error * settings.KI * dt; + var differential = (error - _PreviousError) * settings.KD / dt; + + _PreviousError = error; + _PreviousUpdateTime = DateTime.UtcNow; + + return proportional + _Integral + differential; + } + } +} diff --git a/TestApp/Program.cs b/TestApp/Program.cs index c04dc75..e87258b 100644 --- a/TestApp/Program.cs +++ b/TestApp/Program.cs @@ -1,55 +1,118 @@ -using NModbus; -using System.Net.Sockets; +using System.Reflection; +using TestApp.Configuration; +using TestApp.Driver; namespace TestApp { internal class Program { + private static ConfigurationManager _ConfigurationManager; + private static IChargerDriver? _Driver; private static EnergyManager _EnergyManager; + private static ProcessVariable _ProcessVariable; + private static PidController _PidController; static async Task Main(string[] args) { + _ConfigurationManager = new ConfigurationManager(); + _ConfigurationManager.SettingsChanged += OnSettingsChanged; + try + { + OnSettingsChanged(_ConfigurationManager, _ConfigurationManager.Settings); + } catch (Exception ex) + { + Console.WriteLine($"Error applying settings: {ex.Message}"); + } + _EnergyManager = await EnergyManager.Create(); while (true) { try { - ConnectToModBus(); - } - catch (Exception ex) + // TODO I1, I2, I3 von Wallbox mit I1-3 von Zähler per Kombinatorik max vom Haus ausrechnen + // newLoadingCurrent = maxSicherung - maxHaus + var measurement = _EnergyManager.GetValue(_ProcessVariable); + var newLoadingCurrent = _PidController.Update(measurement); + //if (newLoadingCurrent < 6) { newLoadingCurrent = 0; } + _Driver?.SetLoadingCurrent(newLoadingCurrent); + } catch (Exception ex) { - Console.WriteLine($"Error connecting to ModBus slave: {ex.Message}"); - Thread.Sleep(5000); + Console.WriteLine($"Error in main loop: {ex.Message}"); } + Thread.Sleep(1000); } } - private static void ConnectToModBus() + private static void OnSettingsChanged(object? sender, Settings settings) { - var factory = new ModbusFactory(); - var tcpClient = new TcpClient("192.168.188.21", 502); - var master = factory.CreateMaster(tcpClient); - Console.WriteLine("ModBus TCP Connection established!"); + UpdateDriverSettings(settings); + UpdateControllerSettings(settings); + } - while (tcpClient.Connected) + private static void UpdateDriverSettings(Settings settings) { + var dirverName = settings.ChargerDriver ?? Settings.DEFAULT_CHARGER_DRIVER; + + var fullDriverTypeName = "TestApp.Driver." + dirverName; + var driverType = Assembly.GetExecutingAssembly().GetType(fullDriverTypeName); + if (driverType is null) { - try - { - var currentWirkleistung = _EnergyManager.ActivePowerPositive; - - ushort input = master.ReadHoldingRegisters(1, 1000, 1)[0]; - ushort output = (ushort)(input + currentWirkleistung); - master.WriteMultipleRegisters(1, 1100, new ushort[] { output }); - Thread.Sleep(100); - } - catch (Exception e) - { - Console.WriteLine($"Error updating ModBus registers: {e.Message}"); - Thread.Sleep(1000); - } + throw new ArgumentException($"Charger driver type '{dirverName}' not found!"); } + + if (_Driver is null || !_Driver.GetType().Equals(driverType)) + { + var newDriverObject = Activator.CreateInstance(driverType); + if (newDriverObject is null) + { + throw new InvalidOperationException("Could not instantiate driver!"); + } + if (newDriverObject is not IChargerDriver newDriver) + { + throw new InvalidOperationException("New driver does not implement IChargerDriver!"); + } + + if (_Driver is IDisposable disposable) + { + disposable.Dispose(); + } + _Driver = newDriver; + } + + _Driver.HostAddress = settings.ChargerAddress ?? Settings.DEFAULT_CHARGER_ADDRESS; + _Driver.Port = settings.ChargerPort ?? Settings.DEFAULT_CHARGER_PORT; + } + + private static void UpdateControllerSettings(Settings settings) + { + var allModeSettings = settings.ModeSettings ?? Array.Empty(); + if (!allModeSettings.Any()) { + allModeSettings = new ModeSettings[]{ + new ModeSettings() { + ProcessVariable = ProcessVariable.ActivePowerPositive, + SetPoint = 0, + PositivePid = new PidSetting(), + NegativePid = new PidSetting(), + } + }; + } + + var mode = settings.Mode; + if (mode < 0) { mode = 0; } + if (mode >= allModeSettings.Length) { mode = allModeSettings.Length - 1; } + + var modeSetting = allModeSettings[mode]; + + if (_ProcessVariable != modeSetting.ProcessVariable) + { + _PidController = new PidController(); + _ProcessVariable = modeSetting.ProcessVariable; + } + + _PidController.SetPoint = modeSetting.SetPoint; + _PidController.PositiveSettings = modeSetting.PositivePid ?? new PidSetting(); + _PidController.NegativeSettings = modeSetting.NegativePid ?? new PidSetting(); } } }