WIP
This commit is contained in:
144
TestApp/Configuration/ConfigurationManager.cs
Normal file
144
TestApp/Configuration/ConfigurationManager.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
211
TestApp/Configuration/Settings.cs
Normal file
211
TestApp/Configuration/Settings.cs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
31
TestApp/PidController.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user