This commit is contained in:
2023-02-07 13:08:25 +01:00
parent 31bf148e0f
commit 5277c3518f
6 changed files with 584 additions and 217 deletions

View File

@@ -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<Settings>(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<Settings>? SettingsChanged;
}
}

View File

@@ -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
{
/// <summary>in W</summary>
ActivePowerPositive,
/// <summary>in Wh</summary>
ActiveEnergyPositive,
/// <summary>in W</summary>
ActivePowerNegative,
/// <summary>in Wh</summary>
ActiveEnergyNegative,
/// <summary>in var</summary>
ReactivePowerPositive,
/// <summary>in varh</summary>
ReactiveEnergyPositive,
/// <summary>in var</summary>
ReactivePowerNegative,
/// <summary>in varh</summary>
ReactiveEnergyNegative,
/// <summary>in VA</summary>
ApparentPowerPositive,
/// <summary>in VAh</summary>
ApparentEnergyPositive,
/// <summary>in VA</summary>
ApparentPowerNegative,
/// <summary>in VAh</summary>
ApparentEnergyNegative,
/// <summary>in -</summary>
PowerFactor,
/// <summary>in Hz</summary>
SupplyFrequency,
/// <summary>in W</summary>
ActivePowerPositiveL1,
/// <summary>in Wh</summary>
ActiveEnergyPositiveL1,
/// <summary>in W</summary>
ActivePowerNegativeL1,
/// <summary>in Wh</summary>
ActiveEnergyNegativeL1,
/// <summary>in var</summary>
ReactivePowerPositiveL1,
/// <summary>in varh</summary>
ReactiveEnergyPositiveL1,
/// <summary>in var</summary>
ReactivePowerNegativeL1,
/// <summary>in varh</summary>
ReactiveEnergyNegativeL1,
/// <summary>in VA</summary>
ApparentPowerPositiveL1,
/// <summary>in VAh</summary>
ApparentEnergyPositiveL1,
/// <summary>in VA</summary>
ApparentPowerNegativeL1,
/// <summary>in VAh</summary>
ApparentEnergyNegativeL1,
/// <summary>in A</summary>
CurrentL1,
/// <summary>in V</summary>
VoltageL1,
/// <summary>in -</summary>
PowerFactorL1,
/// <summary>in W</summary>
ActivePowerPositiveL2,
/// <summary>in Wh</summary>
ActiveEnergyPositiveL2,
/// <summary>in W</summary>
ActivePowerNegativeL2,
/// <summary>in Wh</summary>
ActiveEnergyNegativeL2,
/// <summary>in var</summary>
ReactivePowerPositiveL2,
/// <summary>in varh</summary>
ReactiveEnergyPositiveL2,
/// <summary>in var</summary>
ReactivePowerNegativeL2,
/// <summary>in varh</summary>
ReactiveEnergyNegativeL2,
/// <summary>in VA</summary>
ApparentPowerPositiveL2,
/// <summary>in VAh</summary>
ApparentEnergyPositiveL2,
/// <summary>in VA</summary>
ApparentPowerNegativeL2,
/// <summary>in VAh</summary>
ApparentEnergyNegativeL2,
/// <summary>in A</summary>
CurrentL2,
/// <summary>in V</summary>
VoltageL2,
/// <summary>in -</summary>
PowerFactorL2,
/// <summary>in W</summary>
ActivePowerPositiveL3,
/// <summary>in Wh</summary>
ActiveEnergyPositiveL3,
/// <summary>in W</summary>
ActivePowerNegativeL3,
/// <summary>in Wh</summary>
ActiveEnergyNegativeL3,
/// <summary>in var</summary>
ReactivePowerPositiveL3,
/// <summary>in varh</summary>
ReactiveEnergyPositiveL3,
/// <summary>in var</summary>
ReactivePowerNegativeL3,
/// <summary>in varh</summary>
ReactiveEnergyNegativeL3,
/// <summary>in VA</summary>
ApparentPowerPositiveL3,
/// <summary>in VAh</summary>
ApparentEnergyPositiveL3,
/// <summary>in VA</summary>
ApparentPowerNegativeL3,
/// <summary>in VAh</summary>
ApparentEnergyNegativeL3,
/// <summary>in A</summary>
CurrentL3,
/// <summary>in V</summary>
VoltageL3,
/// <summary>in -</summary>
PowerFactorL3,
}
}

View File

@@ -3,14 +3,35 @@ using System.Net.Sockets;
namespace TestApp.Driver namespace TestApp.Driver
{ {
internal class VestelEvc04 : IChargerDriver internal class VestelEvc04 : IChargerDriver, IDisposable
{ {
private IModbusMaster? _Modbus; private IModbusMaster? _Modbus;
private bool _Running; private bool _Running;
private Thread _HeartbeatThread; private Thread _HeartbeatThread;
public string HostAddress { get; set; } = "192.168.178.100"; private string _HostAddress = "192.168.178.100";
public int Port { get; set; } = 502; 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() public VestelEvc04()
{ {
@@ -22,6 +43,7 @@ namespace TestApp.Driver
private void Connect() private void Connect()
{ {
if (!_Running) { return; }
var factory = new ModbusFactory(); var factory = new ModbusFactory();
var tcpClient = new TcpClient(HostAddress, Port); var tcpClient = new TcpClient(HostAddress, Port);
_Modbus = factory.CreateMaster(tcpClient); _Modbus = factory.CreateMaster(tcpClient);
@@ -35,10 +57,10 @@ namespace TestApp.Driver
} }
private void HeartbeatLoop() private void HeartbeatLoop()
{
try
{ {
while (_Running) while (_Running)
{
try
{ {
if (_Modbus is null) if (_Modbus is null)
{ {
@@ -47,22 +69,24 @@ namespace TestApp.Driver
if (_Modbus is null) { continue; } if (_Modbus is null) { continue; }
var heardbeatValue = _Modbus.ReadHoldingRegisters(255, 6000, 1)[0]; var heardbeatValue = _Modbus.ReadHoldingRegisters(255, 6000, 1)[0];
if (heardbeatValue == 0) { if (heardbeatValue == 0)
{
_Modbus.WriteSingleRegister(255, 6000, 1); _Modbus.WriteSingleRegister(255, 6000, 1);
} }
Thread.Sleep(100);
}
} }
catch (Exception e) catch (Exception e)
{ {
System.Diagnostics.Debug.WriteLine(e.Message); System.Diagnostics.Debug.WriteLine(e.Message);
Disconnect(); Disconnect();
} }
Thread.Sleep(100);
}
} }
public void SetLoadingCurrent(double value) public void SetLoadingCurrent(double value)
{ {
if (!_Running) { return; }
try try
{ {
if (_Modbus is null) if (_Modbus is null)
@@ -80,5 +104,11 @@ namespace TestApp.Driver
Disconnect(); Disconnect();
} }
} }
public void Dispose()
{
_Running = false;
Disconnect();
}
} }
} }

View File

@@ -1,7 +1,7 @@
using MQTTnet; using MQTTnet;
using MQTTnet.Client; using MQTTnet.Client;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Runtime.CompilerServices; using TestApp.Configuration;
namespace TestApp namespace TestApp
{ {
@@ -79,182 +79,70 @@ namespace TestApp
return factor * value; return factor * value;
} }
/// <summary>in W</summary> public double GetValue(ProcessVariable processVariable) {
public double ActivePowerPositive => GetValue(1099528667391UL, 0.001); return processVariable switch {
ProcessVariable.ActivePowerPositive => GetValue(1099528667391UL, 0.001),
/// <summary>in Wh</summary> ProcessVariable.ActiveEnergyPositive => GetValue(1099528929535UL, 0.001),
public double ActiveEnergyPositive => GetValue(1099528929535UL, 0.001); ProcessVariable.ActivePowerNegative => GetValue(1099545444607UL, 0.001),
ProcessVariable.ActiveEnergyNegative => GetValue(1099545706751UL, 0.001),
/// <summary>in W</summary> ProcessVariable.ReactivePowerPositive => GetValue(1099562221823UL, 0.001),
public double ActivePowerNegative => GetValue(1099545444607UL, 0.001); ProcessVariable.ReactiveEnergyPositive => GetValue(1099562483967UL, 0.001),
ProcessVariable.ReactivePowerNegative => GetValue(1099578999039UL, 0.001),
/// <summary>in Wh</summary> ProcessVariable.ReactiveEnergyNegative => GetValue(1099579261183UL, 0.001),
public double ActiveEnergyNegative => GetValue(1099545706751UL, 0.001); ProcessVariable.ApparentPowerPositive => GetValue(1099662885119UL, 0.001),
ProcessVariable.ApparentEnergyPositive => GetValue(1099663147263UL, 0.001),
/// <summary>in var</summary> ProcessVariable.ApparentPowerNegative => GetValue(1099679662335UL, 0.001),
public double ReactivePowerPositive => GetValue(1099562221823UL, 0.001); ProcessVariable.ApparentEnergyNegative => GetValue(1099679924479UL, 0.001),
ProcessVariable.PowerFactor => GetValue(1099729993983UL, 0.001),
/// <summary>in varh</summary> ProcessVariable.SupplyFrequency => GetValue(1099746771199UL, 0.001),
public double ReactiveEnergyPositive => GetValue(1099562483967UL, 0.001); ProcessVariable.ActivePowerPositiveL1 => GetValue(1099864211711UL, 0.001),
ProcessVariable.ActiveEnergyPositiveL1 => GetValue(1099864473855UL, 0.001),
/// <summary>in var</summary> ProcessVariable.ActivePowerNegativeL1 => GetValue(1099880988927UL, 0.001),
public double ReactivePowerNegative => GetValue(1099578999039UL, 0.001); ProcessVariable.ActiveEnergyNegativeL1 => GetValue(1099881251071UL, 0.001),
ProcessVariable.ReactivePowerPositiveL1 => GetValue(1099897766143UL, 0.001),
/// <summary>in varh</summary> ProcessVariable.ReactiveEnergyPositiveL1 => GetValue(1099898028287UL, 0.001),
public double ReactiveEnergyNegative => GetValue(1099579261183UL, 0.001); ProcessVariable.ReactivePowerNegativeL1 => GetValue(1099914543359UL, 0.001),
ProcessVariable.ReactiveEnergyNegativeL1 => GetValue(1099914805503UL, 0.001),
/// <summary>in VA</summary> ProcessVariable.ApparentPowerPositiveL1 => GetValue(1099998429439UL, 0.001),
public double ApparentPowerPositive => GetValue(1099662885119UL, 0.001); ProcessVariable.ApparentEnergyPositiveL1 => GetValue(1099998691583UL, 0.001),
ProcessVariable.ApparentPowerNegativeL1 => GetValue(1100015206655UL, 0.001),
/// <summary>in VAh</summary> ProcessVariable.ApparentEnergyNegativeL1 => GetValue(1100015468799UL, 0.001),
public double ApparentEnergyPositive => GetValue(1099663147263UL, 0.001); ProcessVariable.CurrentL1 => GetValue(1100031983871UL, 0.001),
ProcessVariable.VoltageL1 => GetValue(1100048761087UL, 0.001),
/// <summary>in VA</summary> ProcessVariable.PowerFactorL1 => GetValue(1100065538303UL, 0.001),
public double ApparentPowerNegative => GetValue(1099679662335UL, 0.001); ProcessVariable.ActivePowerPositiveL2 => GetValue(1100199756031UL, 0.001),
ProcessVariable.ActiveEnergyPositiveL2 => GetValue(1100200018175UL, 0.001),
/// <summary>in VAh</summary> ProcessVariable.ActivePowerNegativeL2 => GetValue(1100216533247UL, 0.001),
public double ApparentEnergyNegative => GetValue(1099679924479UL, 0.001); ProcessVariable.ActiveEnergyNegativeL2 => GetValue(1100216795391UL, 0.001),
ProcessVariable.ReactivePowerPositiveL2 => GetValue(1100233310463UL, 0.001),
/// <summary>in -</summary> ProcessVariable.ReactiveEnergyPositiveL2 => GetValue(1100233572607UL, 0.001),
public double PowerFactor => GetValue(1099729993983UL, 0.001); ProcessVariable.ReactivePowerNegativeL2 => GetValue(1100250087679UL, 0.001),
ProcessVariable.ReactiveEnergyNegativeL2 => GetValue(1100250349823UL, 0.001),
/// <summary>in Hz</summary> ProcessVariable.ApparentPowerPositiveL2 => GetValue(1100333973759UL, 0.001),
public double SupplyFrequency => GetValue(1099746771199UL, 0.001); ProcessVariable.ApparentEnergyPositiveL2 => GetValue(1100334235903UL, 0.001),
ProcessVariable.ApparentPowerNegativeL2 => GetValue(1100350750975UL, 0.001),
/// <summary>in W</summary> ProcessVariable.ApparentEnergyNegativeL2 => GetValue(1100351013119UL, 0.001),
public double ActivePowerPositiveL1 => GetValue(1099864211711UL, 0.001); ProcessVariable.CurrentL2 => GetValue(1100367528191UL, 0.001),
ProcessVariable.VoltageL2 => GetValue(1100384305407UL, 0.001),
/// <summary>in Wh</summary> ProcessVariable.PowerFactorL2 => GetValue(1100401082623UL, 0.001),
public double ActiveEnergyPositiveL1 => GetValue(1099864473855UL, 0.001); ProcessVariable.ActivePowerPositiveL3 => GetValue(1100535300351UL, 0.001),
ProcessVariable.ActiveEnergyPositiveL3 => GetValue(1100535562495UL, 0.001),
/// <summary>in W</summary> ProcessVariable.ActivePowerNegativeL3 => GetValue(1100552077567UL, 0.001),
public double ActivePowerNegativeL1 => GetValue(1099880988927UL, 0.001); ProcessVariable.ActiveEnergyNegativeL3 => GetValue(1100552339711UL, 0.001),
ProcessVariable.ReactivePowerPositiveL3 => GetValue(1100568854783UL, 0.001),
/// <summary>in Wh</summary> ProcessVariable.ReactiveEnergyPositiveL3 => GetValue(1100569116927UL, 0.001),
public double ActiveEnergyNegativeL1 => GetValue(1099881251071UL, 0.001); ProcessVariable.ReactivePowerNegativeL3 => GetValue(1100585631999UL, 0.001),
ProcessVariable.ReactiveEnergyNegativeL3 => GetValue(1100585894143UL, 0.001),
/// <summary>in var</summary> ProcessVariable.ApparentPowerPositiveL3 => GetValue(1100669518079UL, 0.001),
public double ReactivePowerPositiveL1 => GetValue(1099897766143UL, 0.001); ProcessVariable.ApparentEnergyPositiveL3 => GetValue(1100669780223UL, 0.001),
ProcessVariable.ApparentPowerNegativeL3 => GetValue(1100686295295UL, 0.001),
/// <summary>in varh</summary> ProcessVariable.ApparentEnergyNegativeL3 => GetValue(1100686557439UL, 0.001),
public double ReactiveEnergyPositiveL1 => GetValue(1099898028287UL, 0.001); ProcessVariable.CurrentL3 => GetValue(1100703072511UL, 0.001),
ProcessVariable.VoltageL3 => GetValue(1100719849727UL, 0.001),
/// <summary>in var</summary> ProcessVariable.PowerFactorL3 => GetValue(1100736626943UL, 0.001),
public double ReactivePowerNegativeL1 => GetValue(1099914543359UL, 0.001); _ => double.NaN,
};
/// <summary>in varh</summary> }
public double ReactiveEnergyNegativeL1 => GetValue(1099914805503UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerPositiveL1 => GetValue(1099998429439UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyPositiveL1 => GetValue(1099998691583UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerNegativeL1 => GetValue(1100015206655UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyNegativeL1 => GetValue(1100015468799UL, 0.001);
/// <summary>in A</summary>
public double CurrentL1 => GetValue(1100031983871UL, 0.001);
/// <summary>in V</summary>
public double VoltageL1 => GetValue(1100048761087UL, 0.001);
/// <summary>in -</summary>
public double PowerFactorL1 => GetValue(1100065538303UL, 0.001);
/// <summary>in W</summary>
public double ActivePowerPositiveL2 => GetValue(1100199756031UL, 0.001);
/// <summary>in Wh</summary>
public double ActiveEnergyPositiveL2 => GetValue(1100200018175UL, 0.001);
/// <summary>in W</summary>
public double ActivePowerNegativeL2 => GetValue(1100216533247UL, 0.001);
/// <summary>in Wh</summary>
public double ActiveEnergyNegativeL2 => GetValue(1100216795391UL, 0.001);
/// <summary>in var</summary>
public double ReactivePowerPositiveL2 => GetValue(1100233310463UL, 0.001);
/// <summary>in varh</summary>
public double ReactiveEnergyPositiveL2 => GetValue(1100233572607UL, 0.001);
/// <summary>in var</summary>
public double ReactivePowerNegativeL2 => GetValue(1100250087679UL, 0.001);
/// <summary>in varh</summary>
public double ReactiveEnergyNegativeL2 => GetValue(1100250349823UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerPositiveL2 => GetValue(1100333973759UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyPositiveL2 => GetValue(1100334235903UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerNegativeL2 => GetValue(1100350750975UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyNegativeL2 => GetValue(1100351013119UL, 0.001);
/// <summary>in A</summary>
public double CurrentL2 => GetValue(1100367528191UL, 0.001);
/// <summary>in V</summary>
public double VoltageL2 => GetValue(1100384305407UL, 0.001);
/// <summary>in -</summary>
public double PowerFactorL2 => GetValue(1100401082623UL, 0.001);
/// <summary>in W</summary>
public double ActivePowerPositiveL3 => GetValue(1100535300351UL, 0.001);
/// <summary>in Wh</summary>
public double ActiveEnergyPositiveL3 => GetValue(1100535562495UL, 0.001);
/// <summary>in W</summary>
public double ActivePowerNegativeL3 => GetValue(1100552077567UL, 0.001);
/// <summary>in Wh</summary>
public double ActiveEnergyNegativeL3 => GetValue(1100552339711UL, 0.001);
/// <summary>in var</summary>
public double ReactivePowerPositiveL3 => GetValue(1100568854783UL, 0.001);
/// <summary>in varh</summary>
public double ReactiveEnergyPositiveL3 => GetValue(1100569116927UL, 0.001);
/// <summary>in var</summary>
public double ReactivePowerNegativeL3 => GetValue(1100585631999UL, 0.001);
/// <summary>in varh</summary>
public double ReactiveEnergyNegativeL3 => GetValue(1100585894143UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerPositiveL3 => GetValue(1100669518079UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyPositiveL3 => GetValue(1100669780223UL, 0.001);
/// <summary>in VA</summary>
public double ApparentPowerNegativeL3 => GetValue(1100686295295UL, 0.001);
/// <summary>in VAh</summary>
public double ApparentEnergyNegativeL3 => GetValue(1100686557439UL, 0.001);
/// <summary>in A</summary>
public double CurrentL3 => GetValue(1100703072511UL, 0.001);
/// <summary>in V</summary>
public double VoltageL3 => GetValue(1100719849727UL, 0.001);
/// <summary>in -</summary>
public double PowerFactorL3 => GetValue(1100736626943UL, 0.001);
} }
} }

31
TestApp/PidController.cs Normal file
View File

@@ -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;
}
}
}

View File

@@ -1,55 +1,118 @@
using NModbus; using System.Reflection;
using System.Net.Sockets; using TestApp.Configuration;
using TestApp.Driver;
namespace TestApp namespace TestApp
{ {
internal class Program internal class Program
{ {
private static ConfigurationManager _ConfigurationManager;
private static IChargerDriver? _Driver;
private static EnergyManager _EnergyManager; private static EnergyManager _EnergyManager;
private static ProcessVariable _ProcessVariable;
private static PidController _PidController;
static async Task Main(string[] args) 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(); _EnergyManager = await EnergyManager.Create();
while (true) while (true)
{ {
try try
{ {
ConnectToModBus(); // TODO I1, I2, I3 von Wallbox mit I1-3 von Zähler per Kombinatorik max vom Haus ausrechnen
} // newLoadingCurrent = maxSicherung - maxHaus
catch (Exception ex) 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}"); Console.WriteLine($"Error in main loop: {ex.Message}");
Thread.Sleep(5000);
} }
}
}
private static void ConnectToModBus()
{
var factory = new ModbusFactory();
var tcpClient = new TcpClient("192.168.188.21", 502);
var master = factory.CreateMaster(tcpClient);
Console.WriteLine("ModBus TCP Connection established!");
while (tcpClient.Connected)
{
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); Thread.Sleep(1000);
} }
} }
private static void OnSettingsChanged(object? sender, Settings settings)
{
UpdateDriverSettings(settings);
UpdateControllerSettings(settings);
}
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)
{
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<ModeSettings>();
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();
} }
} }
} }