Add the downward calibration

This commit is contained in:
atef 2025-05-19 08:36:25 +02:00
parent 3b43090b84
commit 6fbec18a00
8 changed files with 146 additions and 54 deletions

View File

@ -4,9 +4,11 @@ namespace InnovEnergy.App.SodiStoreMax.DataTypes;
public class Configuration
{
public Double MinimumSoC { get; set; }
public Double GridSetPoint { get; set; }
public CalibrationChargeType CalibrationChargeState { get; set; }
public DateTime CalibrationChargeDate { get; set; }
public Double MinimumSoC { get; set; }
public Double GridSetPoint { get; set; }
public CalibrationChargeType CalibrationChargeState { get; set; }
public CalibrationDischargeType CalibrationDischargeState { get; set; }
public DateTime CalibrationChargeDate { get; set; }
public DateTime CalibrationDischargeDate { get; set; }
}

View File

@ -11,21 +11,23 @@ public static class Controller
private static readonly Double MaxDischargePower = -8000; // By battery TODO: move to config
private static readonly Double MaxChargePower = 6000; // By battery TODO: move to config
private static Boolean _hasRepetitiveCalibrationChargeChecked = false;
private static Boolean _hasRepetitiveCalibrationChargeChecked = false;
private static Boolean _hasRepetitiveCalibrationDischargeChecked = false;
private static DateTime _nextDayAt10Am = DateTime.Now;
public static EssMode SelectControlMode(this StatusRecord s)
{
//return EssMode.OptimizeSelfConsumption;
return s.StateMachine.State != 23 ? EssMode.Off
: s.MustReachMinSoc() ? EssMode.ReachMinSoc
: s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge
: s.GridMeter is null ? EssMode.NoGridMeter
: EssMode.OptimizeSelfConsumption;
return s.StateMachine.State != 23 ? EssMode.Off
: s.MustDoCalibrationCharge() ? EssMode.UpwardsCalibrationCharge
: s.MustDoDownwardsCalibrationCharge() ? EssMode.DownwardsCalibrationCharge
: s.MustReachMinSoc() ? EssMode.ReachMinSoc
: s.GridMeter is null ? EssMode.NoGridMeter
: EssMode.OptimizeSelfConsumption;
}
public static EssControl ControlEss(this StatusRecord s)
{
var mode = s.SelectControlMode().WriteLine();
@ -45,8 +47,9 @@ public static class Controller
return EssControl.Default;
}
// if we have no reading from the Grid meter, but we have a grid power (K1 is close),
// then we do only heat the battery to avoid discharging the battery and the oscillation between reach min soc and off mode
/* if we have no reading from the Grid meter, but we have a grid power (K1 is close), then we do only heat the battery
to avoid discharging the battery and the oscillation between reach min soc and off mode
This should be reconsidered for deligreen */
if (mode is EssMode.NoGridMeter)
return new EssControl
{
@ -117,7 +120,6 @@ public static class Controller
private static EssControl LimitChargePower(this EssControl control, StatusRecord s)
{
//var maxInverterChargePower = s.ControlInverterPower(s.Config.MaxInverterPower);
var maxBatteryChargePower = s.MaxBatteryChargePower();
maxBatteryChargePower.WriteLine(" Max Battery Charge Power");
@ -125,7 +127,6 @@ public static class Controller
return control
//.LimitChargePower(, EssLimit.ChargeLimitedByInverterPower)
.LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower);
}
private static EssControl LimitDischargePower(this EssControl control, StatusRecord s)
@ -134,7 +135,11 @@ public static class Controller
var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower());
maxBatteryDischargeDelta.WriteLine(" Max Battery Discharge Power");
if (control.Mode == EssMode.DownwardsCalibrationCharge )
{
return control
.LimitDischargePower(maxBatteryDischargeDelta, EssLimit.DischargeLimitedByBatteryPower);
}
return control
.LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower)
.LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc);
@ -142,17 +147,19 @@ public static class Controller
private static Double ComputePowerDelta(this StatusRecord s, EssMode mode)
{
var chargePower = s.AcDc.Devices.Sum(d => d.Status.Nominal.Power.Value);
var chargePower = s.AcDc.Devices.Sum(d => d.Status.Nominal.Power.Value);
var batteryDischargePower = s.Battery?.Devices.Count * MaxDischargePower ?? 0 ;
return mode switch
{
EssMode.ReachMinSoc => s.ControlInverterPower(chargePower),
EssMode.OptimizeSelfConsumption => s.ControlGridPower(s.Config.GridSetPoint),
EssMode.Off => 0,
EssMode.OffGrid => 0,
EssMode.NoGridMeter => 0,
EssMode.CalibrationCharge => s.ControlInverterPower(chargePower),
_ => throw new ArgumentException(null, nameof(mode))
EssMode.ReachMinSoc => s.ControlInverterPower(chargePower),
EssMode.OptimizeSelfConsumption => s.ControlGridPower(s.Config.GridSetPoint),
EssMode.Off => 0,
EssMode.OffGrid => 0,
EssMode.NoGridMeter => 0,
EssMode.UpwardsCalibrationCharge => s.ControlInverterPower(chargePower),
EssMode.DownwardsCalibrationCharge => s.ControlInverterPower(batteryDischargePower),
_ => throw new ArgumentException(null, nameof(mode))
};
}
@ -229,6 +236,36 @@ public static class Controller
return mustDoCalibrationCharge;
}
private static Boolean MustDoDownwardsCalibrationCharge(this StatusRecord statusRecord)
{
var calibrationDischargeForced = statusRecord.Config.ForceCalibrationDischargeState;
var additionalCalibrationRequired = DownAdditionalCalibrationDateHasBeenPassed(statusRecord.Config.DownDayAndTimeForAdditionalCalibration);
var repetitiveCalibrationRequired = DownRepetitiveCalibrationDateHasBeenPassed(statusRecord.Config.DownDayAndTimeForRepetitiveCalibration);
var mustDoCalibrationDischarge = calibrationDischargeForced == CalibrationDischargeType.DischargePermanently ||
(calibrationDischargeForced == CalibrationDischargeType.AdditionallyOnce && additionalCalibrationRequired) ||
(calibrationDischargeForced == CalibrationDischargeType.RepetitivelyEvery && repetitiveCalibrationRequired);
Console.WriteLine("Next Repetitive calibration charge date is "+ statusRecord.Config.DownDayAndTimeForRepetitiveCalibration);
Console.WriteLine("Next Additional calibration charge date is "+ statusRecord.Config.DownDayAndTimeForAdditionalCalibration);
if (statusRecord.Battery is not null)
{
if (calibrationDischargeForced == CalibrationDischargeType.AdditionallyOnce && statusRecord.Battery.Eod )
{
statusRecord.Config.ForceCalibrationDischargeState = CalibrationDischargeType.RepetitivelyEvery;
}
else if (calibrationDischargeForced == CalibrationDischargeType.RepetitivelyEvery && statusRecord.Battery.Eod && _hasRepetitiveCalibrationDischargeChecked)
{
statusRecord.Config.DownDayAndTimeForRepetitiveCalibration = statusRecord.Config.DownDayAndTimeForRepetitiveCalibration.AddDays(7);
_hasRepetitiveCalibrationDischargeChecked = false;
}
}
return mustDoCalibrationDischarge;
}
private static Boolean RepetitiveCalibrationDateHasBeenPassed(DateTime calibrationChargeDate)
{
if (DateTime.Now >= calibrationChargeDate )
@ -247,6 +284,23 @@ public static class Controller
return false;
}
private static Boolean DownRepetitiveCalibrationDateHasBeenPassed(DateTime calibrationDischargeDate)
{
if (DateTime.Now >= calibrationDischargeDate )
{
_hasRepetitiveCalibrationDischargeChecked = true;
return true;
}
return false;
}
private static Boolean DownAdditionalCalibrationDateHasBeenPassed(DateTime calibrationDischargeDate)
{
if (DateTime.Now >= calibrationDischargeDate )
{
return true;
}
return false;
}
private static Double ControlGridPower(this StatusRecord status, Double targetPower)
{
return ControlPower

View File

@ -5,7 +5,8 @@ public enum EssMode
Off,
OffGrid,
HeatBatteries,
CalibrationCharge,
UpwardsCalibrationCharge,
DownwardsCalibrationCharge,
ReachMinSoc,
NoGridMeter,
OptimizeSelfConsumption

View File

@ -10,7 +10,6 @@ using System.Security;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Flurl.Http;
using InnovEnergy.App.SodiStoreMax;
using InnovEnergy.App.SodiStoreMax.Devices;
@ -33,10 +32,11 @@ using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils;
using InnovEnergy.App.SodiStoreMax.DataTypes;
using Newtonsoft.Json;
using static System.Int32;
using static InnovEnergy.App.SodiStoreMax.AggregationService.Aggregator;
using static InnovEnergy.App.SodiStoreMax.MiddlewareClasses.MiddlewareAgent;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig;
using Controller = InnovEnergy.App.SodiStoreMax.System.Controller;
using DeviceState = InnovEnergy.App.SodiStoreMax.Devices.DeviceState;
// ReSharper disable PossibleLossOfFraction
@ -60,6 +60,7 @@ internal static class Program
private static readonly Channel RelaysChannel;
private static readonly Channel BatteriesChannel;
//
private static Boolean _curtailFlag = false;
private const String VpnServerIp = "10.2.0.11";
private static Boolean _subscribedToQueue = false;
@ -69,6 +70,7 @@ internal static class Program
private static UInt16 _fileCounter = 0;
private static SalimaxAlarmState _salimaxAlarmState = SalimaxAlarmState.Green;
private const String Port = "/dev/ttyUSB0"; // move to a config file
private static Int32 _failsCounter = 0; // move to a config file
static Program()
{
@ -240,14 +242,32 @@ internal static class Program
record.ControlConstants();
record.ControlSystemState();
record.AcDc.SystemControl.ApplyAcDcDefaultSettings();
record.DcDc.SystemControl.ApplyDcDcDefaultSettings();
/* Console.WriteLine(" Fails Counter = " + _failsCounter);
// Retries Control
if (record.StateMachine.State is not (28 or 23) )
{
if (_failsCounter > 60) // 2 min
{
Console.WriteLine(" Fails retries reached threshold");
record.EnableSafeDefaults();
return record;
}
_failsCounter++;
}
else
{
_failsCounter = 0;
}*/
//record.ControlPvPower(record.Config.CurtailP, record.Config.PvInstalledPower);
var essControl = record.ControlEss().WriteLine();
record.EssControl = essControl;
record.AcDc.SystemControl.ApplyAcDcDefaultSettings();
record.DcDc.SystemControl.ApplyDcDcDefaultSettings();
DistributePower(record, essControl);
@ -310,8 +330,6 @@ internal static class Program
var alarmList = new List<AlarmOrWarning>();
var warningList = new List<AlarmOrWarning>();
var bAlarmList = new List<String>();
var bWarningList = new List<String>();
if (record.Battery is { MonomerHighVoltageAlarm: true })
{
@ -401,7 +419,7 @@ internal static class Program
private static Int32 GetInstallationId(String s3Bucket)
{
var part = s3Bucket.Split('-').FirstOrDefault();
return TryParse(part, out var id) ? id : 0; // is 0 a default safe value? check with Marios
return int.TryParse(part, out var id) ? id : 0; // is 0 a default safe value? check with Marios
}
private static String? DetectAlarmStates(this StatusRecord r) => r.Relays switch
@ -766,7 +784,6 @@ internal static class Program
{
var csv = status.ToCsv();
Dictionary<string, object> jsonData = new Dictionary<string, object>();
//Console.WriteLine(csv);
foreach (var line in csv.Split('\n'))
{
@ -842,8 +859,8 @@ internal static class Program
private static void Heartbit(DateTime timeStamp)
{
var s3Bucket = Config.Load().S3?.Bucket;
var tryParse = TryParse(s3Bucket?.Split("-")[0], out var installationId);
var parse = TryParse(timeStamp.ToUnixTime().ToString(), out var nameOfJsonFile);
var tryParse = int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
var parse = int.TryParse(timeStamp.ToUnixTime().ToString(), out var nameOfJsonFile);
if (tryParse)
{

View File

@ -550,7 +550,6 @@ public static class Controller
s.Relays.DisconnectIslandBusFromGrid();
return false;
// => 13
}
@ -636,7 +635,7 @@ public static class Controller
//this is must be deleted
private static void Disable(this DcDcDevicesRecord dcDc)
{
// For Test purpose, The transition from island mode to grid tier and vis versa , may not need to disable Dc/Dc.
// For Test purpose, The transition from island mode to grid tier and vis versa may not need to disable Dc/Dc.
// This will keep the Dc link powered.
// dcDc.Devices
@ -687,9 +686,9 @@ public static class Controller
}
private static Boolean EnableSafeDefaults(this StatusRecord s)
public static Boolean EnableSafeDefaults(this StatusRecord s)
{
// After some tests, the safe state is switch off inverter and keep the last state of K2 , Dc/Dc and Grid type to avoid conflict.
// After some tests, the safe state is switch off inverter and keep the last state of K2, Dc/Dc and Grid type to avoid conflict.
// s.DcDc.Disable();
s.AcDc.Disable(); // Maybe comment this to avoid opening/closing K3

View File

@ -5,5 +5,5 @@ public record StateMachine
public required String Message { get; set; } // TODO: init only
public required Int32 State { get; set; } // TODO: init only
public static StateMachine Default { get; } = new StateMachine { State = 100, Message = "Unknown State" };
public static StateMachine Default { get; } = new StateMachine { State = 100, Message = "Default State" };
}

View File

@ -0,0 +1,10 @@
namespace InnovEnergy.App.SodiStoreMax.SystemConfig;
public enum CalibrationDischargeType
{
RepetitivelyEvery,
AdditionallyOnce,
DischargePermanently
}

View File

@ -18,9 +18,15 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
public required Double MinSoc { get; set; }
public required UInt16 CurtailP { get; set; }// in Kw
public required UInt16 PvInstalledPower { get; set; }// in Kw
public required CalibrationChargeType ForceCalibrationChargeState { get; set; }
public required DateTime DayAndTimeForRepetitiveCalibration { get; set; }
public required DateTime DayAndTimeForAdditionalCalibration { get; set; }
public required CalibrationDischargeType ForceCalibrationDischargeState { get; set; }
public required DateTime DownDayAndTimeForRepetitiveCalibration { get; set; }
public required DateTime DownDayAndTimeForAdditionalCalibration { get; set; }
public required Boolean DisplayIndividualBatteries { get; set; }
public required Double PConstant { get; set; }
public required Double GridSetPoint { get; set; }
@ -126,17 +132,20 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
#else
public static Config Default => new()
{
MinSoc = 20,
CurtailP = 0,
PvInstalledPower = 20,
ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery,
DayAndTimeForRepetitiveCalibration = DefaultDatetime,
DayAndTimeForAdditionalCalibration = DefaultDatetime,
DisplayIndividualBatteries = false,
PConstant = .5,
GridSetPoint = 0,
BatterySelfDischargePower = 200,
HoldSocZone = 1, // TODO: find better name,
MinSoc = 20,
CurtailP = 0,
PvInstalledPower = 20,
ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery,
DayAndTimeForRepetitiveCalibration = DefaultDatetime,
DayAndTimeForAdditionalCalibration = DefaultDatetime,
ForceCalibrationDischargeState = CalibrationDischargeType.RepetitivelyEvery,
DownDayAndTimeForAdditionalCalibration = DefaultDatetime,
DownDayAndTimeForRepetitiveCalibration = DefaultDatetime,
DisplayIndividualBatteries = false,
PConstant = .5,
GridSetPoint = 0,
BatterySelfDischargePower = 200,
HoldSocZone = 1, // TODO: find better name,
IslandMode = new()
{
AcDc = new ()