implement circular current prevention

This commit is contained in:
ig 2023-06-20 10:21:06 +02:00
parent d88fc677b5
commit dcc4bfa78a
4 changed files with 339 additions and 107 deletions

View File

@ -4,106 +4,174 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.SaliMax.Ess; namespace InnovEnergy.App.SaliMax.Ess;
public static class Controller public static class Controller
{ {
private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config
private static readonly TimeSpan CommunicationTimeout = TimeSpan.FromSeconds(10);
public static EssMode SelectControlMode(this StatusRecord s) public static EssMode SelectControlMode(this StatusRecord s)
{ {
return EssMode.OptimizeSelfConsumption; //return EssMode.OptimizeSelfConsumption;
// return s.SystemState.Id != 16 ? EssMode.Off return s.StateMachine.State != 16 ? EssMode.Off
// : s.MustHeatBatteries() ? EssMode.HeatBatteries : s.MustHeatBatteries() ? EssMode.HeatBatteries
// : s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge : s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge
// : s.MustReachMinSoc() ? EssMode.ReachMinSoc : s.MustReachMinSoc() ? EssMode.ReachMinSoc
// : s.GridMeter is null ? EssMode.NoGridMeter : s.GridMeter is null ? EssMode.NoGridMeter
// : EssMode.OptimizeSelfConsumption; : EssMode.OptimizeSelfConsumption;
} }
public static EssControl ControlEss(this StatusRecord s) public static EssControl ControlEss(this StatusRecord s)
{ {
// var hasPreChargeAlarm = s.HasPreChargeAlarm();
//
// if (hasPreChargeAlarm)
// "PreChargeAlarm".Log();
var mode = s.SelectControlMode(); var mode = s.SelectControlMode();
mode.WriteLine();
if (mode is EssMode.Off or EssMode.NoGridMeter) if (mode is EssMode.Off or EssMode.NoGridMeter)
return new EssControl(mode, EssLimit.NoLimit, PowerCorrection: 0, PowerSetpoint: 0); return new EssControl(mode, EssLimit.NoLimit, PowerCorrection: 0, PowerSetpoint: 0);
var essDelta = s.ComputePowerDelta(mode); var essDelta = s.ComputePowerDelta(mode);
var unlimitedControl = new EssControl(mode, EssLimit.NoLimit, essDelta, 0); var unlimitedControl = new EssControl(mode, EssLimit.NoLimit, essDelta, 0);
var limitedControl = unlimitedControl
.LimitChargePower(s) var limitedControl = unlimitedControl
.LimitDischargePower(s); .LimitChargePower(s)
.LimitDischargePower(s)
.LimitInverterPower(s);
var currentPowerSetPoint = s.CurrentPowerSetPoint(); var currentPowerSetPoint = s.CurrentPowerSetPoint();
var setpoint = currentPowerSetPoint + limitedControl.PowerCorrection;
//var setpoint = -11000;
return limitedControl with { PowerSetpoint = setpoint }; var essControl = limitedControl with { PowerSetpoint = currentPowerSetPoint + limitedControl.PowerCorrection };
essControl.WriteLine();
s.Battery.Soc.WriteLine("Soc");
return essControl;
} }
private static EssControl LimitInverterPower(this EssControl control, StatusRecord s)
{
var powerDelta = control.PowerCorrection.Value;
var acDcs = s.AcDc.Devices;
var nInverters = acDcs.Count;
if (nInverters < 2)
return control; // current loop cannot happen
var nominalPower = acDcs.Average(d => d.Status.Nominal.Power);
var maxStep = nominalPower / 25;
var clampedPowerDelta = powerDelta.Clamp(-maxStep, maxStep);
var dcLimited = acDcs.Any(d => d.Status.PowerLimitedBy == PowerLimit.DcLink);
if (!dcLimited)
return control with { PowerCorrection = clampedPowerDelta };
var maxPower = acDcs.Max(d => d.Status.Ac.Power.Active.Value).WriteLine("Max");
var minPower = acDcs.Min(d => d.Status.Ac.Power.Active.Value).WriteLine("Min");
var powerDifference = maxPower - minPower;
if (powerDifference < maxStep)
return control with { PowerCorrection = clampedPowerDelta };
var correction = powerDifference / 4;
return s.AcDc.Dc.Voltage > s.Config.ReferenceDcBusVoltage
? control with { PowerCorrection = clampedPowerDelta.Clamp(-maxStep, -correction), LimitedBy = EssLimit.ChargeLimitedByMaxDcBusVoltage }
: control with { PowerCorrection = clampedPowerDelta.Clamp(correction, maxStep), LimitedBy = EssLimit.DischargeLimitedByMinDcBusVoltage };
}
// private static Double AdjustMaxChargePower(StatusRecord s, Double powerDelta)
// {
// var acDcs = s.AcDc.Devices;
//
// var nInverters = acDcs.Count;
//
// if (nInverters == 0)
// return 0; // no inverters present: we cannot charge at all
//
// var nominalPower = acDcs.Sum(d => d.Status.Nominal.Power);
//
// if (nInverters == 1)
// return powerDelta; // single inverter: current loop cannot happen
//
// acDcs.ForEach(d => d.Status.PowerLimitedBy.WriteLine());
//
// var dcLimited = acDcs.Any(d => d.Status.PowerLimitedBy == PowerLimit.DcLink);
//
// if (expr)
// {
//
// }
//
//
// var maxPowerDifference = nominalPower / 25;
//
// var maxPower = acDcs.Max(d => d.Status.Ac.Power.Active.Value).WriteLine("Max");
// var minPower = acDcs.Min(d => d.Status.Ac.Power.Active.Value).WriteLine("Min");
//
// var sum = acDcs.Sum(d => d.Status.Ac.Power.Active.Value);
//
// var powerDifference = maxPower - minPower;
//
// if (powerDifference > maxPowerDifference)
// ChargePower = sum - powerDifference / 4;
// else
// ChargePower += maxPowerDifference / 2;
//
// $"HACK : ChargePower {ChargePower} Difference: {powerDifference}".WriteLine();
// }
private static EssControl LimitChargePower(this EssControl control, StatusRecord s) private static EssControl LimitChargePower(this EssControl control, StatusRecord s)
{ {
var maxInverterChargePower = s.ControlInverterPower(s.Config.MaxInverterPower);
//var maxInverterChargePower = s.ControlInverterPower(s.Config.MaxInverterPower);
var maxBatteryChargePower = s.MaxBatteryChargePower(); var maxBatteryChargePower = s.MaxBatteryChargePower();
return control return control
.LimitChargePower(maxInverterChargePower, EssLimit.ChargeLimitedByInverterPower) //.LimitChargePower(, EssLimit.ChargeLimitedByInverterPower)
.LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower); .LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower);
}
private static EssControl LimitChargePower(this EssControl control, Double controlDelta, EssLimit reason)
{
return control.PowerCorrection > controlDelta
? control with { LimitedBy = reason, PowerCorrection = controlDelta }
: control;
} }
private static EssControl LimitDischargePower(this EssControl control, StatusRecord s) private static EssControl LimitDischargePower(this EssControl control, StatusRecord s)
{ {
var maxInverterDischargeDelta = s.ControlInverterPower(-s.Config.MaxInverterPower); //var maxInverterDischargeDelta = s.ControlInverterPower(-s.Config.MaxInverterPower);
var maxBatteryDischargeDelta = s.Battery.Devices.Sum(b => b.MaxDischargePower); var maxBatteryDischargeDelta = s.Battery.Devices.Sum(b => b.MaxDischargePower);
var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower()); var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower());
return control return control
.LimitDischargePower(maxInverterDischargeDelta, EssLimit.DischargeLimitedByInverterPower) // .LimitDischargePower(maxInverterDischargeDelta, EssLimit.DischargeLimitedByInverterPower)
.LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower) .LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower)
.LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc); .LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc);
} }
private static EssControl LimitDischargePower(this EssControl control, Double controlDelta, EssLimit reason)
private static Double ComputePowerDelta(this StatusRecord s, EssMode mode)
{ {
return control.PowerCorrection < controlDelta var chargePower = s.AcDc.Devices.Sum(d => d.Status.Nominal.Power.Value);
? control with { LimitedBy = reason, PowerCorrection = controlDelta }
: control; return mode switch
{
EssMode.HeatBatteries => s.ControlInverterPower(chargePower),
EssMode.ReachMinSoc => s.ControlInverterPower(chargePower),
EssMode.CalibrationCharge => s.ControlInverterPower(chargePower),
EssMode.OptimizeSelfConsumption => s.ControlGridPower(s.Config.GridSetPoint),
_ => throw new ArgumentException(null, nameof(mode))
};
} }
private static Double ComputePowerDelta(this StatusRecord s, EssMode mode) => mode switch
{
EssMode.HeatBatteries => s.ControlInverterPower(s.Config.MaxInverterPower),
EssMode.CalibrationCharge => s.ControlInverterPower(s.Config.MaxInverterPower),
EssMode.ReachMinSoc => s.ControlInverterPower(s.Config.MaxInverterPower),
EssMode.OptimizeSelfConsumption => s.ControlGridPower(s.Config.GridSetPoint),
_ => throw new ArgumentException(null, nameof(mode))
};
private static Boolean HasPreChargeAlarm(this StatusRecord statusRecord) private static Boolean HasPreChargeAlarm(this StatusRecord statusRecord)
{ {
return statusRecord.DcDc.Alarms.Contains(Lib.Devices.Trumpf.TruConvertDc.Status.AlarmMessage.DcDcPrecharge); return statusRecord.DcDc.Alarms.Contains(Lib.Devices.Trumpf.TruConvertDc.Status.AlarmMessage.DcDcPrecharge);
@ -165,20 +233,14 @@ public static class Controller
return UnixTime.Now - statusRecord.Config.LastEoc > MaxTimeWithoutEoc; return UnixTime.Now - statusRecord.Config.LastEoc > MaxTimeWithoutEoc;
} }
private static Double DistributePower(this StatusRecord s, Double powerSetPoint)
{
var inverterPowerSetPoint = powerSetPoint / s.AcDc.Devices.Count;
return inverterPowerSetPoint.Clamp(-s.Config.MaxInverterPower, s.Config.MaxInverterPower);
}
public static Double ControlGridPower(this StatusRecord status, Double targetPower) public static Double ControlGridPower(this StatusRecord status, Double targetPower)
{ {
return ControlPower return ControlPower
( (
measurement: status.GridMeter!.Ac.Power.Active, measurement : status.GridMeter!.Ac.Power.Active,
target: targetPower, target : targetPower,
pConstant: status.Config.PConstant pConstant : status.Config.PConstant
); );
} }
@ -186,9 +248,9 @@ public static class Controller
{ {
return ControlPower return ControlPower
( (
measurement: status.AcDc.Ac.Power.Active, measurement : status.AcDc.Ac.Power.Active,
target: targetInverterPower, target : targetInverterPower,
pConstant: status.Config.PConstant pConstant : status.Config.PConstant
); );
} }
@ -211,7 +273,7 @@ public static class Controller
if (batteries.Count == 0) if (batteries.Count == 0)
return Double.NegativeInfinity; return Double.NegativeInfinity;
var a = -2 * s.Config.SelfDischargePower * batteries.Count / s.Config.HoldSocZone; var a = -2 * s.Config.BatterySelfDischargePower * batteries.Count / s.Config.HoldSocZone;
var b = -a * (s.Config.MinSoc + s.Config.HoldSocZone); var b = -a * (s.Config.MinSoc + s.Config.HoldSocZone);
return batteries.Min(d => d.Soc.Value) * a + b; return batteries.Min(d => d.Soc.Value) * a + b;

View File

@ -4,11 +4,42 @@ namespace InnovEnergy.App.SaliMax.Ess;
public record EssControl public record EssControl
( (
EssMode Mode, EssMode Mode,
EssLimit LimitedBy, EssLimit LimitedBy,
ActivePower PowerCorrection, ActivePower PowerCorrection,
ActivePower PowerSetpoint ActivePower PowerSetpoint
); )
{
public EssControl LimitChargePower(Double controlDelta, EssLimit reason)
{
var overload = PowerCorrection - controlDelta;
if (overload <= 0)
return this;
return this with
{
LimitedBy = reason,
PowerCorrection = controlDelta,
PowerSetpoint = PowerSetpoint - overload
};
}
public EssControl LimitDischargePower(Double controlDelta, EssLimit reason)
{
var overload = PowerCorrection - controlDelta;
if (overload >= 0)
return this;
return this with
{
LimitedBy = reason,
PowerCorrection = controlDelta,
PowerSetpoint = PowerSetpoint - overload
};
}
}

View File

@ -8,6 +8,8 @@ public enum EssLimit
DischargeLimitedByInverterPower, DischargeLimitedByInverterPower,
ChargeLimitedByInverterPower, ChargeLimitedByInverterPower,
ChargeLimitedByBatteryPower, ChargeLimitedByBatteryPower,
ChargeLimitedByMaxDcBusVoltage,
DischargeLimitedByMinDcBusVoltage,
} }

View File

@ -1,13 +1,10 @@
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Flurl.Http; using Flurl.Http;
using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.App.SaliMax.Ess;
using InnovEnergy.App.SaliMax.SaliMaxRelays; using InnovEnergy.App.SaliMax.SaliMaxRelays;
using InnovEnergy.App.SaliMax.System; using InnovEnergy.App.SaliMax.System;
using InnovEnergy.App.SaliMax.SystemConfig; using InnovEnergy.App.SaliMax.SystemConfig;
using InnovEnergy.App.SaliMax.VirtualDevices;
using InnovEnergy.Lib.Devices.AMPT; using InnovEnergy.Lib.Devices.AMPT;
using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Devices.EmuMeter; using InnovEnergy.Lib.Devices.EmuMeter;
@ -19,9 +16,9 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
using AcPower = InnovEnergy.Lib.Units.Composite.AcPower;
using Exception = System.Exception; using Exception = System.Exception;
#pragma warning disable IL2026 #pragma warning disable IL2026
@ -35,7 +32,7 @@ internal static class Program
private const UInt32 UpdateIntervalSeconds = 2; private const UInt32 UpdateIntervalSeconds = 2;
private static readonly Byte[] BatteryNodes = { 2, 3, 4, 5, 6 }; private static readonly Byte[] BatteryNodes = { 2, 3, 4, 5, 6 };
private const String BatteryTty = "/dev/ttyUSB0"; private const String BatteryTty = "/dev/ttyUSB0";
// private const String RelaysIp = "10.0.1.1"; // "192.168.1.242"; // private const String RelaysIp = "10.0.1.1"; // "192.168.1.242";
@ -92,25 +89,141 @@ internal static class Program
.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 criticalLoadMeterDevice = new EmuMeterDevice(AcOutLoadChannel); var acIslandLoadMeter = new EmuMeterDevice(AcOutLoadChannel);
var amptDevice = new AmptDevices(AmptChannel); var amptDevice = new AmptDevices(AmptChannel);
var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel); var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
StatusRecord ReadStatus() => new() StatusRecord ReadStatus()
{ {
AcDc = acDcDevices.Read(), var acDc = acDcDevices.Read();
DcDc = dcDcDevices.Read(), var dcDc = dcDcDevices.Read();
Battery = batteryDevices.Read(), var battery = batteryDevices.Read();
Relays = saliMaxRelaysDevice.Read(), var relays = saliMaxRelaysDevice.Read();
CriticalLoad = criticalLoadMeterDevice.Read(), var loadOnAcIsland = acIslandLoadMeter.Read();
GridMeter = gridMeterDevice.Read(), var gridMeter = gridMeterDevice.Read();
Mppt = amptDevice.Read(), var pvOnDc = amptDevice.Read();
Config = Config.Load() // load from disk every iteration, so config can be changed while running
};
var pvOnAcGrid = AcDevicePower.Null;
var pvOnAcIsland = AcDevicePower.Null;
var loadOnAcGrid = pvOnAcGrid.Power +
pvOnAcIsland.Power +
(gridMeter is null ? AcPower.Null : gridMeter.Ac.Power) +
(loadOnAcIsland is null ? AcPower.Null : loadOnAcIsland.Ac.Power);
var dcPowers = new[]
{
acDc?.Dc.Power.Value,
pvOnDc?.Dc?.Power.Value,
dcDc?.Dc.Link.Power.Value
};
var loadOnDc = dcPowers.Any(p => p is null)
? null
: new DcDevicePower { Power = dcPowers.Sum(p => p)!} ;
return new StatusRecord
{
AcDc = acDc ?? AcDcDevicesRecord.Null,
DcDc = dcDc ?? DcDcDevicesRecord.Null,
Battery = battery ?? Battery48TlRecords.Null,
Relays = relays,
GridMeter = gridMeter,
PvOnAcGrid = pvOnAcGrid,
PvOnAcIsland = pvOnAcIsland,
PvOnDc = pvOnDc ?? AmptStatus.Null,
LoadOnAcGrid = new AcDevicePower { Power = -loadOnAcGrid },
LoadOnAcIsland = loadOnAcIsland,
LoadOnDc = loadOnDc,
Config = Config.Load() // load from disk every iteration, so config can be changed while running
};
}
// async Task<StatusRecord> ReadStatus()
// {
// var acDcTask = Task.Run(() => acDcDevices.Read());
// var dcDcTask = Task.Run(() => dcDcDevices.Read());
// var batteryTask = Task.Run(() => batteryDevices.Read());
// var relaysTask = Task.Run(() => saliMaxRelaysDevice.Read());
// var loadOnAcIslandTask = Task.Run(() => acIslandLoadMeter.Read());
// var gridMeterTask = Task.Run(() => gridMeterDevice.Read());
// var pvOnDcTask = Task.Run(() => amptDevice.Read());
//
//
// var timeout = Task.Delay(TimeSpan.FromSeconds(4));
// var whenAll = Task
// .WhenAll
// (
// acDcTask,
// dcDcTask,
// batteryTask,
// relaysTask,
// loadOnAcIslandTask,
// gridMeterTask,
// pvOnDcTask
// );
//
//
// await Task.WhenAny(whenAll, timeout);
//
// var acDc = await acDcTask.ResultOrNull() ;
// var dcDc = await dcDcTask.ResultOrNull();
// var battery = await batteryTask.ResultOrNull();
// var relays = await relaysTask.ResultOrNull();
// var loadOnAcIsland = await loadOnAcIslandTask.ResultOrNull();
// var gridMeter = await gridMeterTask.ResultOrNull();
// var pvOnDc = await pvOnDcTask.ResultOrNull();
//
//
// var pvOnAcGrid = AcDevicePower.Null;
// var pvOnAcIsland = AcDevicePower.Null;
// var loadOnAcGrid = pvOnAcGrid.Power +
// pvOnAcIsland.Power +
// (gridMeter is null ? AcPower.Null : gridMeter.Ac.Power) +
// (loadOnAcIsland is null ? AcPower.Null : loadOnAcIsland.Ac.Power);
//
//
// var dcPowers = new[]
// {
// acDc?.Dc.Power.Value,
// pvOnDc?.Dc?.Power.Value,
// dcDc?.Dc.Link.Power.Value
// };
//
// var loadOnDc = dcPowers.Any(p => p is null)
// ? null
// : new DcDevicePower { Power = dcPowers.Sum(p => p)!} ;
//
//
// return new StatusRecord
// {
// AcDc = acDc ?? AcDcDevicesRecord.Null,
// DcDc = dcDc ?? DcDcDevicesRecord.Null,
// Battery = battery ?? Battery48TlRecords.Null,
// Relays = relays,
// GridMeter = gridMeter,
//
// PvOnAcGrid = pvOnAcGrid,
// PvOnAcIsland = pvOnAcIsland,
// PvOnDc = pvOnDc ?? AmptStatus.Null,
//
// LoadOnAcGrid = new AcDevicePower { Power = -loadOnAcGrid },
// LoadOnAcIsland = loadOnAcIsland,
// LoadOnDc = loadOnDc,
//
// Config = Config.Load() // load from disk every iteration, so config can be changed while running
// };
// }
void WriteControl(StatusRecord r) void WriteControl(StatusRecord r)
{ {
@ -130,40 +243,64 @@ internal static class Program
var t = UnixTime.FromTicks(UnixTime.Now.Ticks / 2 * 2); var t = UnixTime.FromTicks(UnixTime.Now.Ticks / 2 * 2);
t.ToUtcDateTime().WriteLine(); //t.ToUtcDateTime().WriteLine();
var record = ReadStatus(); var record = ReadStatus();
var emuMeterRegisters = record.GridMeter;
if (emuMeterRegisters is not null)
{
emuMeterRegisters.Ac.Power.Active.WriteLine("Grid Active");
emuMeterRegisters.Ac.Power.Reactive.WriteLine("Grid Reactive");
}
record.AcDc.ResetAlarms(); record.AcDc.ResetAlarms();
record.DcDc.ResetAlarms(); record.DcDc.ResetAlarms();
record.ControlConstants();
record.ControlSystemState(); record.ControlSystemState();
Console.WriteLine($"{record.StateMachine.State}: {record.StateMachine.Message}");
var essControl = record.ControlEss(); var essControl = record.ControlEss();
record.Ess = essControl; record.EssControl = essControl;
record.AcDc.SystemControl.ApplyDefaultSettings(); record.AcDc.SystemControl.ApplyDefaultSettings();
record.DcDc.SystemControl.ApplyDefaultSettings(); record.DcDc.SystemControl.ApplyDefaultSettings();
DistributePower(record, essControl); DistributePower(record, essControl);
"===========================================".WriteLine();
WriteControl(record); WriteControl(record);
await UploadCsv(record, t); await UploadCsv(record, t);
var emuMeterRegisters = record.GridMeter; record.Config.Save();
if (emuMeterRegisters is not null)
{ "===========================================".WriteLine();
emuMeterRegisters.Ac.Power.Active.WriteLine();
emuMeterRegisters.Ac.Power.Reactive.WriteLine();
}
} }
// ReSharper disable once FunctionNeverReturns // ReSharper disable once FunctionNeverReturns
} }
private static async Task<T?> ResultOrNull<T>(this Task<T> task)
{
if (task.Status == TaskStatus.RanToCompletion)
return await task;
return default;
}
private static void ControlConstants(this StatusRecord r)
{
var inverters = r.AcDc.Devices;
inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcBusVoltage);
inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage);
}
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;
@ -174,8 +311,6 @@ internal static class Program
//var powerPerInverterPhase = AcPower.Null; //var powerPerInverterPhase = AcPower.Null;
powerPerInverterPhase.WriteLine("powerPerInverterPhase");
record.AcDc.Devices.ForEach(d => record.AcDc.Devices.ForEach(d =>
{ {
d.Control.Ac.PhaseControl = PhaseControl.Asymmetric; d.Control.Ac.PhaseControl = PhaseControl.Asymmetric;
@ -194,7 +329,7 @@ internal static class Program
sc.SystemConfig = AcDcAndDcDc; sc.SystemConfig = AcDcAndDcDc;
#if DEBUG #if DEBUG
sc.CommunicationTimeout = TimeSpan.FromMinutes(10); sc.CommunicationTimeout = TimeSpan.FromMinutes(2);
#else #else
sc.CommunicationTimeout = TimeSpan.FromSeconds(10); sc.CommunicationTimeout = TimeSpan.FromSeconds(10);
#endif #endif
@ -234,13 +369,15 @@ internal static class Program
private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp) private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp)
{ {
var csv = status.ToCsv(); timeStamp.WriteLine();
var csv = status.ToCsv().WriteLine();
var s3Path = timeStamp + ".csv"; var s3Path = timeStamp + ".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));
csv.WriteLine(); //csv.WriteLine();
timeStamp.Ticks.WriteLine(); //timeStamp.Ticks.WriteLine();
if (response.StatusCode != 200) if (response.StatusCode != 200)
{ {