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

@ -7,6 +7,8 @@ public class Configuration
public Double MinimumSoC { get; set; } public Double MinimumSoC { get; set; }
public Double GridSetPoint { get; set; } public Double GridSetPoint { get; set; }
public CalibrationChargeType CalibrationChargeState { get; set; } public CalibrationChargeType CalibrationChargeState { get; set; }
public CalibrationDischargeType CalibrationDischargeState { get; set; }
public DateTime CalibrationChargeDate { get; set; } public DateTime CalibrationChargeDate { get; set; }
public DateTime CalibrationDischargeDate { get; set; }
} }

View File

@ -12,6 +12,8 @@ public static class Controller
private static readonly Double MaxChargePower = 6000; // 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; private static DateTime _nextDayAt10Am = DateTime.Now;
public static EssMode SelectControlMode(this StatusRecord s) public static EssMode SelectControlMode(this StatusRecord s)
@ -19,13 +21,13 @@ public static class Controller
//return EssMode.OptimizeSelfConsumption; //return EssMode.OptimizeSelfConsumption;
return s.StateMachine.State != 23 ? EssMode.Off return s.StateMachine.State != 23 ? EssMode.Off
: s.MustDoCalibrationCharge() ? EssMode.UpwardsCalibrationCharge
: s.MustDoDownwardsCalibrationCharge() ? EssMode.DownwardsCalibrationCharge
: s.MustReachMinSoc() ? EssMode.ReachMinSoc : s.MustReachMinSoc() ? EssMode.ReachMinSoc
: s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge
: 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 mode = s.SelectControlMode().WriteLine(); var mode = s.SelectControlMode().WriteLine();
@ -45,8 +47,9 @@ public static class Controller
return EssControl.Default; return EssControl.Default;
} }
// if we have no reading from the Grid meter, but we have a grid power (K1 is close), /* 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
// then we do only heat the battery to avoid discharging the battery and the oscillation between reach min soc and off mode 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) if (mode is EssMode.NoGridMeter)
return new EssControl return new EssControl
{ {
@ -117,7 +120,6 @@ public static class Controller
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();
maxBatteryChargePower.WriteLine(" Max Battery Charge Power"); maxBatteryChargePower.WriteLine(" Max Battery Charge Power");
@ -125,7 +127,6 @@ public static class Controller
return control return control
//.LimitChargePower(, EssLimit.ChargeLimitedByInverterPower) //.LimitChargePower(, EssLimit.ChargeLimitedByInverterPower)
.LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower); .LimitChargePower(maxBatteryChargePower, EssLimit.ChargeLimitedByBatteryPower);
} }
private static EssControl LimitDischargePower(this EssControl control, StatusRecord s) private static EssControl LimitDischargePower(this EssControl control, StatusRecord s)
@ -134,7 +135,11 @@ public static class Controller
var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower()); var keepMinSocLimitDelta = s.ControlBatteryPower(s.HoldMinSocPower());
maxBatteryDischargeDelta.WriteLine(" Max Battery Discharge Power"); maxBatteryDischargeDelta.WriteLine(" Max Battery Discharge Power");
if (control.Mode == EssMode.DownwardsCalibrationCharge )
{
return control
.LimitDischargePower(maxBatteryDischargeDelta, EssLimit.DischargeLimitedByBatteryPower);
}
return control return control
.LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower) .LimitDischargePower(maxBatteryDischargeDelta , EssLimit.DischargeLimitedByBatteryPower)
.LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc); .LimitDischargePower(keepMinSocLimitDelta , EssLimit.DischargeLimitedByMinSoc);
@ -143,6 +148,7 @@ public static class Controller
private static Double ComputePowerDelta(this StatusRecord s, EssMode mode) 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 return mode switch
{ {
@ -151,7 +157,8 @@ public static class Controller
EssMode.Off => 0, EssMode.Off => 0,
EssMode.OffGrid => 0, EssMode.OffGrid => 0,
EssMode.NoGridMeter => 0, EssMode.NoGridMeter => 0,
EssMode.CalibrationCharge => s.ControlInverterPower(chargePower), EssMode.UpwardsCalibrationCharge => s.ControlInverterPower(chargePower),
EssMode.DownwardsCalibrationCharge => s.ControlInverterPower(batteryDischargePower),
_ => throw new ArgumentException(null, nameof(mode)) _ => throw new ArgumentException(null, nameof(mode))
}; };
} }
@ -229,6 +236,36 @@ public static class Controller
return mustDoCalibrationCharge; 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) private static Boolean RepetitiveCalibrationDateHasBeenPassed(DateTime calibrationChargeDate)
{ {
if (DateTime.Now >= calibrationChargeDate ) if (DateTime.Now >= calibrationChargeDate )
@ -247,6 +284,23 @@ public static class Controller
return false; 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) private static Double ControlGridPower(this StatusRecord status, Double targetPower)
{ {
return ControlPower return ControlPower

View File

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

View File

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

View File

@ -550,7 +550,6 @@ public static class Controller
s.Relays.DisconnectIslandBusFromGrid(); s.Relays.DisconnectIslandBusFromGrid();
return false; return false;
// => 13 // => 13
} }
@ -636,7 +635,7 @@ public static class Controller
//this is must be deleted //this is must be deleted
private static void Disable(this DcDcDevicesRecord dcDc) 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. // This will keep the Dc link powered.
// dcDc.Devices // dcDc.Devices
@ -687,7 +686,7 @@ 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.

View File

@ -5,5 +5,5 @@ public record StateMachine
public required String Message { get; set; } // TODO: init only public required String Message { get; set; } // TODO: init only
public required Int32 State { 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 Double MinSoc { get; set; }
public required UInt16 CurtailP { get; set; }// in Kw public required UInt16 CurtailP { get; set; }// in Kw
public required UInt16 PvInstalledPower { get; set; }// in Kw public required UInt16 PvInstalledPower { get; set; }// in Kw
public required CalibrationChargeType ForceCalibrationChargeState { get; set; } public required CalibrationChargeType ForceCalibrationChargeState { get; set; }
public required DateTime DayAndTimeForRepetitiveCalibration { get; set; } public required DateTime DayAndTimeForRepetitiveCalibration { get; set; }
public required DateTime DayAndTimeForAdditionalCalibration { 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 Boolean DisplayIndividualBatteries { get; set; }
public required Double PConstant { get; set; } public required Double PConstant { get; set; }
public required Double GridSetPoint { get; set; } public required Double GridSetPoint { get; set; }
@ -132,6 +138,9 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery, ForceCalibrationChargeState = CalibrationChargeType.RepetitivelyEvery,
DayAndTimeForRepetitiveCalibration = DefaultDatetime, DayAndTimeForRepetitiveCalibration = DefaultDatetime,
DayAndTimeForAdditionalCalibration = DefaultDatetime, DayAndTimeForAdditionalCalibration = DefaultDatetime,
ForceCalibrationDischargeState = CalibrationDischargeType.RepetitivelyEvery,
DownDayAndTimeForAdditionalCalibration = DefaultDatetime,
DownDayAndTimeForRepetitiveCalibration = DefaultDatetime,
DisplayIndividualBatteries = false, DisplayIndividualBatteries = false,
PConstant = .5, PConstant = .5,
GridSetPoint = 0, GridSetPoint = 0,