Add ControlPvPower function.

This commit is contained in:
atef 2023-12-18 16:53:19 +01:00
parent 85b248dc6f
commit 91da191874
1 changed files with 273 additions and 240 deletions

View File

@ -1,3 +1,6 @@
#undef Amax
#undef GridLimit
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Threading.Tasks; using System.Reactive.Threading.Tasks;
using Flurl.Http; using Flurl.Http;
@ -21,6 +24,8 @@ using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using InnovEnergy.App.SaliMax.AggregationService; using InnovEnergy.App.SaliMax.AggregationService;
using InnovEnergy.App.SaliMax.DataTypes; using InnovEnergy.App.SaliMax.DataTypes;
using static InnovEnergy.App.SaliMax.AggregationService.Aggregator;
using static InnovEnergy.App.SaliMax.MiddlewareClasses.MiddlewareAgent;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
using DeviceState = InnovEnergy.App.SaliMax.Devices.DeviceState; using DeviceState = InnovEnergy.App.SaliMax.Devices.DeviceState;
@ -45,44 +50,51 @@ internal static class Program
private static readonly Channel RelaysChannel; private static readonly Channel RelaysChannel;
private static readonly Channel BatteriesChannel; private static readonly Channel BatteriesChannel;
private const String VpnServerIp = "10.2.0.11"; private const String VpnServerIp = "10.2.0.11";
private static Boolean _subscribedToQueue = false; private static Boolean _subscribedToQueue = false;
private static Boolean _subscribeToQueueForTheFirstTime = false; private static Boolean _subscribeToQueueForTheFirstTime = false;
private static SalimaxAlarmState _prevSalimaxState = SalimaxAlarmState.Green; private static SalimaxAlarmState _prevSalimaxState = SalimaxAlarmState.Green;
private static Int32 _heartBitInterval = 0; private static Int32 _heartBitInterval = 0;
static Program() static Program()
{ {
var config = Config.Load(); var config = Config.Load();
var d = config.Devices; var d = config.Devices;
Channel CreateChannel(SalimaxDevice device) => device.DeviceState == DeviceState.Disabled Channel CreateChannel(SalimaxDevice device) => device.DeviceState == DeviceState.Disabled
? new NullChannel() ? new NullChannel()
: new TcpChannel(device); : new TcpChannel(device);
TruConvertAcChannel = CreateChannel(d.TruConvertAcIp); TruConvertAcChannel = CreateChannel(d.TruConvertAcIp);
TruConvertDcChannel = CreateChannel(d.TruConvertDcIp); TruConvertDcChannel = CreateChannel(d.TruConvertDcIp);
GridMeterChannel = CreateChannel(d.GridMeterIp); GridMeterChannel = CreateChannel(d.GridMeterIp);
IslandBusLoadChannel = CreateChannel(d.IslandBusLoadMeterIp); IslandBusLoadChannel = CreateChannel(d.IslandBusLoadMeterIp);
PvOnDc = CreateChannel(d.PvOnDc); PvOnDc = CreateChannel(d.PvOnDc);
PvOnAcGrid = CreateChannel(d.PvOnAcGrid); PvOnAcGrid = CreateChannel(d.PvOnAcGrid);
PvOnAcIsland = CreateChannel(d.PvOnAcIsland); PvOnAcIsland = CreateChannel(d.PvOnAcIsland);
RelaysChannel = CreateChannel(d.RelaysIp); RelaysChannel = CreateChannel(d.RelaysIp);
BatteriesChannel = CreateChannel(d.BatteryIp); BatteriesChannel = CreateChannel(d.BatteryIp);
BatteryNodes = config BatteryNodes = config
.Devices .Devices
.BatteryNodes .BatteryNodes
.Select(n => n.ConvertTo<Byte>()) .Select(n => n.ConvertTo<Byte>())
.ToArray(config.Devices.BatteryNodes.Length); .ToArray(config.Devices.BatteryNodes.Length);
} }
public static async Task Main(String[] args) public static async Task Main(String[] args)
{ {
//Do not await //Do not await
Aggregator.HourlyDataAggregationManager().ContinueWith(t => t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning(); HourlyDataAggregationManager()
Aggregator.DailyDataAggregationManager().ContinueWith(t => t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted).SupressAwaitWarning(); .ContinueWith(t=>t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted)
MiddlewareAgent.InitializeCommunicationToMiddleware(); .SupressAwaitWarning();
DailyDataAggregationManager()
.ContinueWith(t=>t.Exception.WriteLine(), TaskContinuationOptions.OnlyOnFaulted)
.SupressAwaitWarning();
InitializeCommunicationToMiddleware();
while (true) while (true)
{ {
try try
@ -92,7 +104,7 @@ internal static class Program
catch (Exception e) catch (Exception e)
{ {
e.LogError(); e.LogError();
} }
} }
} }
@ -104,68 +116,68 @@ internal static class Program
Watchdog.NotifyReady(); Watchdog.NotifyReady();
var battery48TlDevices = BatteryNodes var battery48TlDevices = BatteryNodes
.Select(n => new Battery48TlDevice(BatteriesChannel, n)) .Select(n => new Battery48TlDevice(BatteriesChannel, n))
.ToList(); .ToList();
var batteryDevices = new Battery48TlDevices(battery48TlDevices); var batteryDevices = new Battery48TlDevices(battery48TlDevices);
var acDcDevices = new TruConvertAcDcDevices(TruConvertAcChannel); var acDcDevices = new TruConvertAcDcDevices(TruConvertAcChannel);
var dcDcDevices = new TruConvertDcDcDevices(TruConvertDcChannel); var dcDcDevices = new TruConvertDcDcDevices(TruConvertDcChannel);
var gridMeterDevice = new EmuMeterDevice(GridMeterChannel); var gridMeterDevice = new EmuMeterDevice(GridMeterChannel);
var acIslandLoadMeter = new EmuMeterDevice(IslandBusLoadChannel); var acIslandLoadMeter = new EmuMeterDevice(IslandBusLoadChannel);
var pvOnDcDevice = new AmptDevices(PvOnDc); var pvOnDcDevice = new AmptDevices(PvOnDc);
var pvOnAcGridDevice = new AmptDevices(PvOnAcGrid); var pvOnAcGridDevice = new AmptDevices(PvOnAcGrid);
var pvOnAcIslandDevice = new AmptDevices(PvOnAcIsland); var pvOnAcIslandDevice = new AmptDevices(PvOnAcIsland);
var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel); var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
StatusRecord ReadStatus() StatusRecord ReadStatus()
{ {
var config = Config.Load(); var config = Config.Load();
var devices = config.Devices; var devices = config.Devices;
var acDc = acDcDevices.Read(); var acDc = acDcDevices.Read();
var dcDc = dcDcDevices.Read(); var dcDc = dcDcDevices.Read();
var relays = saliMaxRelaysDevice.Read(); var relays = saliMaxRelaysDevice.Read();
var loadOnAcIsland = acIslandLoadMeter.Read(); var loadOnAcIsland = acIslandLoadMeter.Read();
var gridMeter = gridMeterDevice.Read(); var gridMeter = gridMeterDevice.Read();
var pvOnDc = pvOnDcDevice.Read(); var pvOnDc = pvOnDcDevice.Read();
var battery = batteryDevices.Read(); var battery = batteryDevices.Read();
var pvOnAcGrid = pvOnAcGridDevice.Read(); var pvOnAcGrid = pvOnAcGridDevice.Read();
var pvOnAcIsland = pvOnAcIslandDevice.Read(); var pvOnAcIsland = pvOnAcIslandDevice.Read();
var gridBusToIslandBus = Topology.CalculateGridBusToIslandBusPower(pvOnAcIsland, loadOnAcIsland, acDc); var gridBusToIslandBus = Topology.CalculateGridBusToIslandBusPower(pvOnAcIsland, loadOnAcIsland, acDc);
var gridBusLoad = devices.LoadOnAcGrid.DeviceState == DeviceState.Disabled var gridBusLoad = devices.LoadOnAcGrid.DeviceState == DeviceState.Disabled
? new AcPowerDevice { Power = 0 } ? new AcPowerDevice { Power = 0 }
: Topology.CalculateGridBusLoad(gridMeter, pvOnAcGrid, gridBusToIslandBus); : Topology.CalculateGridBusLoad(gridMeter, pvOnAcGrid, gridBusToIslandBus);
var dcLoad = devices.LoadOnDc.DeviceState == DeviceState.Disabled var dcLoad = devices.LoadOnDc.DeviceState == DeviceState.Disabled
? new DcPowerDevice { Power = 0 } ? new DcPowerDevice { Power = 0 }
: Topology.CalculateDcLoad(acDc, pvOnDc, dcDc); : Topology.CalculateDcLoad(acDc, pvOnDc, dcDc);
var acDcToDcLink = devices.LoadOnDc.DeviceState == DeviceState.Disabled var acDcToDcLink = devices.LoadOnDc.DeviceState == DeviceState.Disabled ?
? Topology.CalculateAcDcToDcLink(pvOnDc, dcDc, acDc) Topology.CalculateAcDcToDcLink(pvOnDc, dcDc, acDc)
: new DcPowerDevice { Power = acDc.Dc.Power }; : new DcPowerDevice{ Power = acDc.Dc.Power};
return new StatusRecord return new StatusRecord
{ {
AcDc = acDc, AcDc = acDc,
DcDc = dcDc, DcDc = dcDc,
Battery = battery, Battery = battery,
Relays = relays, Relays = relays,
GridMeter = gridMeter, GridMeter = gridMeter,
PvOnAcGrid = pvOnAcGrid, PvOnAcGrid = pvOnAcGrid,
PvOnAcIsland = pvOnAcIsland, PvOnAcIsland = pvOnAcIsland,
PvOnDc = pvOnDc, PvOnDc = pvOnDc,
AcGridToAcIsland = gridBusToIslandBus, AcGridToAcIsland = gridBusToIslandBus,
AcDcToDcLink = acDcToDcLink, AcDcToDcLink = acDcToDcLink,
LoadOnAcGrid = gridBusLoad, LoadOnAcGrid = gridBusLoad,
LoadOnAcIsland = loadOnAcIsland, LoadOnAcIsland = loadOnAcIsland,
LoadOnDc = dcLoad, LoadOnDc = dcLoad,
StateMachine = StateMachine.Default, StateMachine = StateMachine.Default,
EssControl = EssControl.Default, EssControl = EssControl.Default,
Log = new SystemLog { SalimaxAlarmState = SalimaxAlarmState.Green, Message = null }, //TODO: Put real stuff Log = new SystemLog { SalimaxAlarmState = SalimaxAlarmState.Green, Message = null }, //TODO: Put real stuff
Config = config // load from disk every iteration, so config can be changed while running Config = config // load from disk every iteration, so config can be changed while running
}; };
} }
@ -183,11 +195,11 @@ internal static class Program
while (true) while (true)
{ {
await Observable await Observable
.Interval(UpdateInterval) .Interval(UpdateInterval)
.Select(_ => RunIteration()) .Select(_ => RunIteration())
.SelectMany(r => UploadCsv(r, DateTime.Now.Round(UpdateInterval))) .SelectMany(r => UploadCsv(r, DateTime.Now.Round(UpdateInterval)))
.SelectError() .SelectError()
.ToTask(); .ToTask();
} }
@ -221,7 +233,7 @@ internal static class Program
record.CreateTopologyTextBlock().WriteLine(); record.CreateTopologyTextBlock().WriteLine();
(record.Relays is null ? "No relay Data available" : record.Relays.FiWarning ? "Alert: Fi Warning Detected" : "No Fi Warning Detected").WriteLine(); (record.Relays is null ? "No relay Data available" : record.Relays.FiWarning ? "Alert: Fi Warning Detected" : "No Fi Warning Detected").WriteLine();
(record.Relays is null ? "No relay Data available" : record.Relays.FiError ? "Alert: Fi Error Detected" : "No Fi Error Detected").WriteLine(); (record.Relays is null ? "No relay Data available" : record.Relays.FiError ? "Alert: Fi Error Detected" : "No Fi Error Detected") .WriteLine();
//record.ApplyConfigFile(minSoc:22, gridSetPoint:1); //record.ApplyConfigFile(minSoc:22, gridSetPoint:1);
@ -235,7 +247,7 @@ internal static class Program
// ReSharper disable once FunctionNeverReturns // ReSharper disable once FunctionNeverReturns
} }
public static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord record) private static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord record)
{ {
var s3Bucket = Config.Load().S3?.Bucket; var s3Bucket = Config.Load().S3?.Bucket;
@ -268,7 +280,7 @@ internal static class Program
} }
//If there is an available message from the RabbitMQ Broker, apply the configuration file //If there is an available message from the RabbitMQ Broker, apply the configuration file
Configuration? config = MiddlewareAgent.SetConfigurationFile(); Configuration? config = SetConfigurationFile();
if (config != null) if (config != null)
{ {
record.ApplyConfigFile(config); record.ApplyConfigFile(config);
@ -278,10 +290,10 @@ internal static class Program
private static StatusMessage GetSalimaxStateAlarm(StatusRecord record) private static StatusMessage GetSalimaxStateAlarm(StatusRecord record)
{ {
var alarmCondition = record.DetectAlarmStates(); var alarmCondition = record.DetectAlarmStates();
var s3Bucket = Config.Load().S3?.Bucket; var s3Bucket = Config.Load().S3?.Bucket;
var alarmList = new List<AlarmOrWarning>(); var alarmList = new List<AlarmOrWarning>();
var warningList = new List<AlarmOrWarning>(); var warningList = new List<AlarmOrWarning>();
if (alarmCondition is not null) if (alarmCondition is not null)
{ {
@ -295,162 +307,180 @@ internal static class Program
Description = alarmCondition Description = alarmCondition
}); });
} }
foreach (var alarm in record.AcDc.Alarms)
{
alarmList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "AcDc",
Description = alarm.ToString()
});
}
foreach (var alarm in record.DcDc.Alarms)
{
alarmList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "DcDc",
Description = alarm.ToString()
});
}
foreach (var alarm in record.AcDc.Alarms) if (record.Battery != null)
{ {
alarmList.Add(new AlarmOrWarning var i = 0;
{
Date = DateTime.Now.ToString("yyyy-MM-dd"), foreach (var battery in record.Battery.Devices)
Time = DateTime.Now.ToString("HH:mm:ss"), {
CreatedBy = "AcDc", foreach (var alarm in battery.Alarms)
Description = alarm.ToString() {
}); alarmList.Add(new AlarmOrWarning
} {
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "Battery node" + record.Config.Devices.BatteryNodes[i],
Description = alarm
});
i++;
}
}
}
foreach (var warning in record.AcDc.Warnings)
{
warningList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "AcDc",
Description = warning.ToString()
});
}
foreach (var warning in record.DcDc.Warnings)
{
warningList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "DcDc",
Description = warning.ToString()
});
}
foreach (var alarm in record.DcDc.Alarms) if (record.Battery != null)
{ {
alarmList.Add(new AlarmOrWarning foreach (var warning in record.Battery.Warnings)
{ {
Date = DateTime.Now.ToString("yyyy-MM-dd"), warningList.Add(new AlarmOrWarning
Time = DateTime.Now.ToString("HH:mm:ss"), {
CreatedBy = "DcDc", Date = DateTime.Now.ToString("yyyy-MM-dd"),
Description = alarm.ToString() Time = DateTime.Now.ToString("HH:mm:ss"),
}); CreatedBy = "Battery",
} Description = warning
});
}
}
if (record.Battery != null) var salimaxAlarmsState = (record.Battery is not null && record.Battery.Warnings.Any())
{ | record.AcDc.Warnings.Any()
var i = 0; | record.AcDc.SystemControl.Warnings.Any()
| record.DcDc.Warnings.Any()
? SalimaxAlarmState.Orange
: SalimaxAlarmState.Green; // this will be replaced by LedState
foreach (var battery in record.Battery.Devices) salimaxAlarmsState = (record.Battery is not null && record.Battery.Alarms.Any())
{ | record.AcDc.Alarms.Any()
i++; | record.AcDc.SystemControl.Alarms.Any()
foreach (var alarm in battery.Alarms) | record.DcDc.Alarms.Any()
{ | alarmCondition is not null
alarmList.Add(new AlarmOrWarning ? SalimaxAlarmState.Red
{ : salimaxAlarmsState; // this will be replaced by LedState
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "Battery" + i,
Description = alarm
});
}
}
}
foreach (var warning in record.AcDc.Warnings) int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
{
warningList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "AcDc",
Description = warning.ToString()
});
}
foreach (var warning in record.DcDc.Warnings) var returnedStatus = new StatusMessage
{ {
warningList.Add(new AlarmOrWarning InstallationId = installationId,
{ Status = salimaxAlarmsState,
Date = DateTime.Now.ToString("yyyy-MM-dd"), Type = MessageType.AlarmOrWarning,
Time = DateTime.Now.ToString("HH:mm:ss"), Alarms = alarmList,
CreatedBy = "DcDc", Warnings = warningList
Description = warning.ToString() };
});
}
if (record.Battery != null) return returnedStatus;
{
foreach (var warning in record.Battery.Warnings)
{
warningList.Add(new AlarmOrWarning
{
Date = DateTime.Now.ToString("yyyy-MM-dd"),
Time = DateTime.Now.ToString("HH:mm:ss"),
CreatedBy = "Battery",
Description = warning
});
}
}
var salimaxAlarmsState = (record.Battery is not null && record.Battery.Warnings.Any())
| record.AcDc.Warnings.Any()
| record.AcDc.SystemControl.Warnings.Any()
| record.DcDc.Warnings.Any()
? SalimaxAlarmState.Orange
: SalimaxAlarmState.Green; // this will be replaced by LedState
salimaxAlarmsState = (record.Battery is not null && record.Battery.Alarms.Any())
| record.AcDc.Alarms.Any()
| record.AcDc.SystemControl.Alarms.Any()
| record.DcDc.Alarms.Any()
| alarmCondition is not null
? SalimaxAlarmState.Red
: salimaxAlarmsState; // this will be replaced by LedState
int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
var returnedStatus = new StatusMessage
{
InstallationId = installationId,
Status = salimaxAlarmsState,
Type = MessageType.AlarmOrWarning,
Alarms = alarmList,
Warnings = warningList
};
return returnedStatus;
} }
private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch
{ {
{ K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ", { K2ConnectIslandBusToGridBus: false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: R0 is opening the K2 but the K2 is still close ",
{ K1GridBusIsConnectedToGrid : false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: K1 is open but the K2 is still close ", { K1GridBusIsConnectedToGrid : false, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: K1 is open but the K2 is still close ",
{ FiError: true, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: Fi error occured but the K2 is still close ", { FiError: true, K2IslandBusIsConnectedToGridBus: true } => " Contradiction: Fi error occured but the K2 is still close ",
_ => null _ => null
}; };
private static void ControlConstants(this StatusRecord r) private static void ControlConstants(this StatusRecord r)
{ {
var inverters = r.AcDc.Devices; var inverters = r.AcDc.Devices;
var dcDevices = r.DcDc.Devices; var dcDevices = r.DcDc.Devices;
var configFile = r.Config; var configFile = r.Config;
var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode
inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage); inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage); inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage); inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal); inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal);
dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = devicesConfig.DcDc.UpperDcLinkVoltage); dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = devicesConfig.DcDc.UpperDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage); dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage); dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = configFile.MaxBatteryChargingCurrent); dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = configFile.MaxBatteryChargingCurrent);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = configFile.MaxBatteryDischargingCurrent); dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = configFile.MaxBatteryDischargingCurrent);
dcDevices.ForEach(d => d.Control.MaxDcPower = configFile.MaxDcPower); dcDevices.ForEach(d => d.Control.MaxDcPower = configFile.MaxDcPower);
dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = configFile.MaxChargeBatteryVoltage); dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = configFile.MaxChargeBatteryVoltage);
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = configFile.MinDischargeBatteryVoltage); dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = configFile.MinDischargeBatteryVoltage);
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop); dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
r.DcDc.ResetAlarms(); r.DcDc.ResetAlarms();
r.AcDc.ResetAlarms(); r.AcDc.ResetAlarms();
} }
// This is will be used for
private static void ControlPvPower(this StatusRecord r, Int16 exportLimit = 100)
{
var inverters = r.AcDc.Devices;
var dcDevices = r.DcDc.Devices;
var configFile = r.Config;
var devicesConfig = configFile.GridTie;
inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = devicesConfig.AcDc.ReferenceDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = devicesConfig.DcDc.UpperDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage);
}
// why this is not in Controller? // why this is not in Controller?
private static void DistributePower(StatusRecord record, EssControl essControl) private static void DistributePower(StatusRecord record, EssControl essControl)
{ {
var nInverters = record.AcDc.Devices.Count; var nInverters = record.AcDc.Devices.Count;
var powerPerInverterPhase = nInverters > 0 var powerPerInverterPhase = nInverters > 0
? essControl.PowerSetpoint / nInverters / 3 ? essControl.PowerSetpoint / nInverters / 3
: 0; : 0;
record.AcDc.Devices.ForEach(d => record.AcDc.Devices.ForEach(d =>
{ {
d.Control.Ac.PhaseControl = PhaseControl.Asymmetric; d.Control.Ac.PhaseControl = PhaseControl.Asymmetric;
@ -464,71 +494,74 @@ internal static class Program
{ {
if (sc is null) if (sc is null)
return; return;
sc.ReferenceFrame = ReferenceFrame.Consumer;
sc.SystemConfig = AcDcAndDcDc;
sc.ReferenceFrame = ReferenceFrame.Consumer; #if DEBUG
sc.SystemConfig = AcDcAndDcDc; sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
#else
#if DEBUG sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
sc.CommunicationTimeout = TimeSpan.FromMinutes(2); #endif
#else
sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
#endif
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate; sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
sc.UseSlaveIdForAddressing = true; sc.UseSlaveIdForAddressing = true;
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed; sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off; sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
sc.ResetAlarmsAndWarnings = true; sc.ResetAlarmsAndWarnings = true;
} }
private static void ApplyDcDcDefaultSettings(this SystemControlRegisters? sc) private static void ApplyDcDcDefaultSettings(this SystemControlRegisters? sc)
{ {
if (sc is null) if (sc is null)
return; return;
sc.SystemConfig = DcDcOnly; sc.SystemConfig = DcDcOnly;
#if DEBUG #if DEBUG
sc.CommunicationTimeout = TimeSpan.FromMinutes(2); sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
#else #else
sc.CommunicationTimeout = TimeSpan.FromSeconds(20); sc.CommunicationTimeout = TimeSpan.FromSeconds(20);
#endif #endif
sc.PowerSetPointActivation = PowerSetPointActivation.Immediate; sc.PowerSetPointActivation = PowerSetPointActivation.Immediate;
sc.UseSlaveIdForAddressing = true; sc.UseSlaveIdForAddressing = true;
sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed; sc.SlaveErrorHandling = SlaveErrorHandling.Relaxed;
sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off; sc.SubSlaveErrorHandling = SubSlaveErrorHandling.Off;
sc.ResetAlarmsAndWarnings = true;
sc.ResetAlarmsAndWarnings = true;
} }
private static async Task<Boolean> UploadCsv(StatusRecord status, DateTime timeStamp) private static async Task<Boolean> UploadCsv(StatusRecord status, DateTime timeStamp)
{ {
var s3Config = status.Config.S3; var s3Config = status.Config.S3;
var csv = status.ToCsv().LogInfo(); var csv = status.ToCsv().LogInfo();
if (s3Config is null) if (s3Config is null)
return false; return false;
var s3Path = timeStamp.ToUnixTime() + ".csv"; var s3Path = timeStamp.ToUnixTime() + ".csv";
var request = s3Config.CreatePutRequest(s3Path); var request = s3Config.CreatePutRequest(s3Path);
var response = await request.PutAsync(new StringContent(csv)); var response = await request.PutAsync(new StringContent(csv));
// This is temporary for Wittman // This is temporary for Wittman
//await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines()); //await File.WriteAllTextAsync("/var/www/html/status.csv", csv.SplitLines().Where(l => !l.Contains("Secret")).JoinLines());
if (response.StatusCode != 200) if (response.StatusCode != 200)
{ {
Console.WriteLine("ERROR: PUT"); Console.WriteLine("ERROR: PUT");
var error = await response.GetStringAsync(); var error = await response.GetStringAsync();
Console.WriteLine(error); Console.WriteLine(error);
} }
return true; return true;
} }
private static void ApplyConfigFile(this StatusRecord status, Configuration? config) private static void ApplyConfigFile(this StatusRecord status, Configuration? config)
{ {
status.Config.MinSoc = config.MinimumSoC; status.Config.MinSoc = config.MinimumSoC;
status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W
status.Config.ForceCalibrationCharge = config.ForceCalibrationCharge; status.Config.ForceCalibrationCharge = config.ForceCalibrationCharge;
} }
} }