Update the main loop and reorganise the ESS mode enum
This commit is contained in:
parent
6a0cd3d5a7
commit
ce51af8510
|
|
@ -2,8 +2,8 @@ namespace InnovEnergy.App.GrowattCommunication.ESS;
|
|||
|
||||
public enum EssMode
|
||||
{
|
||||
Off,
|
||||
GridPriority,
|
||||
LoadPriority,
|
||||
BatteryPriority,
|
||||
LoadPriority
|
||||
}
|
||||
GridPriority,
|
||||
Off
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ using Newtonsoft.Json;
|
|||
using Formatting = Newtonsoft.Json.Formatting;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
using InnovEnergy.App.GrowattCommunication.DataTypes;
|
||||
using InnovEnergy.Lib.Devices.WITGrowatt4_15K.DataType;
|
||||
using static InnovEnergy.App.GrowattCommunication.MiddlewareClasses.MiddlewareAgent;
|
||||
|
|
@ -24,7 +26,7 @@ namespace InnovEnergy.App.GrowattCommunication;
|
|||
|
||||
public static class Program
|
||||
{
|
||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(5);
|
||||
private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file
|
||||
private static UInt16 _fileCounter = 0;
|
||||
//
|
||||
|
|
@ -69,7 +71,8 @@ public static class Program
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
[RequiresUnreferencedCode("Calls InnovEnergy.App.GrowattCommunication.Program.SaveModbusTcpFile(StatusRecord)")]
|
||||
private static async Task Run()
|
||||
{
|
||||
Watchdog.NotifyReady();
|
||||
|
|
@ -80,7 +83,7 @@ public static class Program
|
|||
// var growattDevices = new WITGrowattDevices(new List<WITGrowatDevice> { growattDeviceT415K });
|
||||
|
||||
|
||||
StatusRecord ReadStatus()
|
||||
StatusRecord? ReadStatus()
|
||||
{
|
||||
var config = Config.Load();
|
||||
var growattRecord = growattDeviceT415K.Read();
|
||||
|
|
@ -88,20 +91,29 @@ public static class Program
|
|||
return new StatusRecord
|
||||
{
|
||||
AcDcGrowatt = growattRecord,
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
await Observable
|
||||
.Interval(UpdateInterval)
|
||||
.Select(_ => RunIteration())
|
||||
.SelectMany(status =>
|
||||
DataLogging(status, DateTime.Now.Round(UpdateInterval))
|
||||
.ContinueWith(_ => status)) // back to StatusRecord
|
||||
.SelectMany(SaveModbusTcpFile)
|
||||
.SelectError()
|
||||
.ToTask();
|
||||
}
|
||||
|
||||
StatusRecord? RunIteration()
|
||||
{
|
||||
try
|
||||
{
|
||||
Watchdog.NotifyAlive();
|
||||
|
||||
var timestamp = DateTime.Now.Round(UpdateInterval).ToUnixTime();
|
||||
|
||||
$"{timestamp} : {DateTime.Now.Round(UpdateInterval):dd/MM/yyyy HH:mm:ss}".WriteLine();
|
||||
|
||||
var startTime = DateTime.Now;
|
||||
Console.WriteLine("***************************** Reading Battery Data *********************************************");
|
||||
Console.WriteLine(startTime.ToString("HH:mm:ss.fff"));
|
||||
|
|
@ -111,46 +123,102 @@ public static class Program
|
|||
|
||||
SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord);
|
||||
|
||||
await DataLogging(statusrecord, timestamp); // save a csv file locally
|
||||
await SaveModbusTcpFile(statusrecord); // save the json file for modbuscTCP
|
||||
//await DataLogging(statusrecord, timestamp); // save a csv file locally
|
||||
//await SaveModbusTcpFile(statusrecord); // save the json file for modbuscTCP
|
||||
|
||||
statusrecord.AcDcGrowatt.RemotePowerControl.WriteLine(" = RemotePowerControl");
|
||||
|
||||
statusrecord.AcDcGrowatt.EnableCommand.WriteLine(" = EnableCommand");
|
||||
statusrecord.AcDcGrowatt.ControlPermession.WriteLine(" ControlPermession");
|
||||
statusrecord.AcDcGrowatt.MeterPower.WriteLine(" MeterPower");
|
||||
statusrecord.AcDcGrowatt.GridMeterPower.WriteLine(" GridMeterPower");
|
||||
var x = CalculateActivePower(statusrecord);
|
||||
x.WriteLine(" Calculated Grid Power");
|
||||
statusrecord.AcDcGrowatt.InverterActivePower.WriteLine(" InverterActivePower");
|
||||
statusrecord.AcDcGrowatt.BatteryChargeCutoffVoltage.WriteLine(" BatteryChargeCutoffVoltage "); //30409 we set power here
|
||||
statusrecord.AcDcGrowatt.BatteryDischargeCutoffVoltage.WriteLine(" BatteryDishargeCutoffVoltage "); //30409 we set power here
|
||||
statusrecord.AcDcGrowatt.PowerFactor.WriteLine(" = PowerFactor");
|
||||
statusrecord.AcDcGrowatt.Batteries[0].Soc.WriteLine(" SOC");
|
||||
statusrecord.AcDcGrowatt.Batteries[0].Power.WriteLine(" Battery Power");
|
||||
statusrecord.AcDcGrowatt.Batteries[0].Current.WriteLine(" Battery Current");
|
||||
statusrecord.AcDcGrowatt.Batteries[0].Voltage.WriteLine(" Battery Voltage");
|
||||
statusrecord.AcDcGrowatt.BatteryMaxChargeCurrent.WriteLine(" BatteryMaxChargeCurrent "); //30409 we set power here
|
||||
statusrecord.AcDcGrowatt.BatteryMaxdischargeCurrent.WriteLine(" BatteryMaxDischargeCurrent "); //30409 we set power here
|
||||
|
||||
EssModeControl(statusrecord,EssMode.LoadPriority); // this should moved to config file
|
||||
statusrecord.AcDcGrowatt.SystemOperatingMode.WriteLine(" = SystemOperatingMode");
|
||||
statusrecord.AcDcGrowatt.BatteryOperatingMode.WriteLine(" = BatteryOperatingMode");
|
||||
statusrecord.AcDcGrowatt.OperatingPriority.WriteLine(" = OperatingPriority"); // 30408 this the duration
|
||||
|
||||
statusrecord.AcDcGrowatt.FaultMainCode.WriteLine(" = FaultMainCode"); // 30408 this the duration
|
||||
statusrecord.AcDcGrowatt.FaultSubCode.WriteLine(" = FaultSubCode"); // 30408 this the duration
|
||||
statusrecord.AcDcGrowatt.WarningMainCode.WriteLine(" = WarningMainCode"); // 30408 this the duration
|
||||
statusrecord.AcDcGrowatt.WarningSubCode.WriteLine(" = WarningSubCode"); // 30408 this the duration
|
||||
|
||||
|
||||
|
||||
EssModeControl(statusrecord);
|
||||
|
||||
statusrecord.ApplyDefaultSettings();
|
||||
|
||||
statusrecord.Config.Save(); // save the config file
|
||||
|
||||
Console.WriteLine( " ************************************ We are writing ************************************");
|
||||
statusrecord.Config.Save(); // save the config file
|
||||
growattDeviceT415K.Write(statusrecord.AcDcGrowatt);
|
||||
|
||||
// Wait for 2 seconds before the next reading
|
||||
// await Task.Delay(1000); // Delay in milliseconds (1000ms = 1 seconds)
|
||||
await Task.Delay(2000); // Delay in milliseconds (1000ms = 1 seconds)
|
||||
var stopTime = DateTime.Now;
|
||||
Console.WriteLine(stopTime.ToString("HH:mm:ss.fff"));
|
||||
return statusrecord;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Handle exception and print the error
|
||||
Console.WriteLine(e );
|
||||
// await Task.Delay(2000); // Delay in milliseconds (2000ms = 2 seconds)
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static double CalculateActivePower(this StatusRecord? status)
|
||||
{
|
||||
// Convert raw registers to engineering units
|
||||
double Vab = status.AcDcGrowatt.GridAbLineVoltage * 0.1;
|
||||
double Vbc = status.AcDcGrowatt.GridBcLineVoltage * 0.1;
|
||||
double Vca = status.AcDcGrowatt.GridCaLineVoltage * 0.1;
|
||||
double Ia = status.AcDcGrowatt.PhaseACurrent * 0.1;
|
||||
double Ib = status.AcDcGrowatt.PhaseBCurrent * 0.1;
|
||||
double Ic = status.AcDcGrowatt.PhaseCCurrent * 0.1;
|
||||
|
||||
private static void ApplyConfigFile(this StatusRecord status, Configuration? config)
|
||||
// Average line-to-line voltage
|
||||
double Vll = (Vab + Vbc + Vca) / 3.0;
|
||||
|
||||
// Average line current (absolute values, in case of signed currents)
|
||||
double Il = (Math.Abs(Ia) + Math.Abs(Ib) + Math.Abs(Ic)) / 3.0;
|
||||
|
||||
// Apparent power (kVA)
|
||||
double S_kVA = Math.Sqrt(3) * Vll * Il / 1000.0;
|
||||
|
||||
// Active power (kW)
|
||||
double P_kW = S_kVA * 1;
|
||||
|
||||
return P_kW;
|
||||
}
|
||||
|
||||
private static void ApplyConfigFile(this StatusRecord? status, Configuration? config)
|
||||
{
|
||||
if (config == null) return;
|
||||
if (status == null) return;
|
||||
|
||||
status.Config.MinSoc = config.MinimumSoC;
|
||||
status.Config.GridSetPoint = config.GridSetPoint * 1000; // converted from kW to W
|
||||
status.Config.MaximumChargingCurrent = config.MaximumChargingCurrent;
|
||||
status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent; // converted from kW to W
|
||||
|
||||
status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent;
|
||||
status.Config.OperatingPriority = config.OperatingPriority;
|
||||
}
|
||||
|
||||
private static String EssModeControl(StatusRecord statusrecord, EssMode mode)
|
||||
private static String EssModeControl(StatusRecord? statusrecord)
|
||||
{
|
||||
var mode = statusrecord?.Config.OperatingPriority;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case EssMode.Off:
|
||||
|
|
@ -168,7 +236,7 @@ public static class Program
|
|||
statusrecord.AcDcGrowatt.RemotePowerControl = true;
|
||||
statusrecord.AcDcGrowatt.RemotePowerControlChargeDuration = 0; // 30408 this the duration
|
||||
|
||||
statusrecord.AcDcGrowatt.RemoteChargDischargePower = 100; //30409 we set power here // for grid priority from 0 to 100
|
||||
statusrecord.AcDcGrowatt.RemoteChargDischargePower = 100; //30409 we set power here // for battery priority from 0 to 100
|
||||
statusrecord.AcDcGrowatt.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set
|
||||
return "Battery priority mode active";
|
||||
case EssMode.LoadPriority:
|
||||
|
|
@ -179,7 +247,7 @@ public static class Program
|
|||
}
|
||||
}
|
||||
|
||||
private static StatusMessage GetSodiHomeStateAlarm(StatusRecord record)
|
||||
private static StatusMessage GetSodiHomeStateAlarm(StatusRecord? record)
|
||||
{
|
||||
var s3Bucket = Config.Load().S3?.Bucket;
|
||||
|
||||
|
|
@ -240,7 +308,7 @@ public static class Program
|
|||
return int.TryParse(part, out var id) ? id : 0; // is 0 a default safe value? check with Marios
|
||||
}
|
||||
|
||||
private static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord record)
|
||||
private static void SendSalimaxStateAlarm(StatusMessage currentSalimaxState, StatusRecord? record)
|
||||
{
|
||||
var s3Bucket = Config.Load().S3?.Bucket;
|
||||
var subscribedNow = false;
|
||||
|
|
@ -276,16 +344,14 @@ public static class Program
|
|||
return;
|
||||
|
||||
st.AcDcGrowatt.BatteryMaxChargeCurrent = st.Config.MaximumChargingCurrent;
|
||||
st.AcDcGrowatt.BatteryMaxdischargeCurrent = st.Config.MaximumDischargingCurrent;
|
||||
st.AcDcGrowatt.BatteryMaxdischargeCurrent = st.Config.MaximumDischargingCurrent ;
|
||||
st.AcDcGrowatt.DischargeCutoffSoc = st.Config.MinSoc;
|
||||
st.AcDcGrowatt.EmsCommunicationFailureTime = 20; // 20 sec
|
||||
st.AcDcGrowatt.EnableEmsCommunicationFailureTime = false;
|
||||
st.AcDcGrowatt.EnableEmsCommunicationFailureTime = true;
|
||||
st.AcDcGrowatt.EnableCommand = true;
|
||||
st.AcDcGrowatt.ControlPermession = true;
|
||||
st.AcDcGrowatt.BatteryChargeCutoffVoltage = 100; //st.Config.BatteryChargeCutoffVoltage;
|
||||
st.AcDcGrowatt.BatteryChargeCutoffVoltage = 60; //st.Config.BatteryChargeCutoffVoltage;
|
||||
st.AcDcGrowatt.BatteryDischargeCutoffVoltage = 20; //st.Config.BatteryDischargeCutoffVoltage;
|
||||
st.AcDcGrowatt.BatteryMaxChargeCurrent = 200; //st.Config.BatteryChargeCutoffVoltage;
|
||||
st.AcDcGrowatt.BatteryMaxdischargeCurrent = 200; //st.Config.BatteryChargeCutoffVoltage;
|
||||
|
||||
}
|
||||
|
||||
private static Dictionary<String, UInt16> ConvertToModbusRegisters(Object value, String outputType, Int32 startingAddress)
|
||||
|
|
@ -323,18 +389,18 @@ public static class Program
|
|||
}
|
||||
|
||||
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Serialize<System.Collections.Generic.Dictionary<string, ushort>>(System.Collections.Generic.Dictionary<string, ushort>, System.Text.Json.JsonSerializerOptions?)")]
|
||||
private static async Task SaveModbusTcpFile(StatusRecord status)
|
||||
private static async Task<Boolean> SaveModbusTcpFile(StatusRecord status)
|
||||
{
|
||||
var modbusData = new Dictionary<String, UInt16>();
|
||||
const Double protcolNumber = 1.1;
|
||||
const Double protcolNumber = 1.1; // to be communicated by yinyin maybe add this to config file
|
||||
var pv1Power = status.AcDcGrowatt.Pv1Current * status.AcDcGrowatt.Pv1Voltage;
|
||||
var pv2Power = status.AcDcGrowatt.Pv2Current * status.AcDcGrowatt.Pv2Voltage;
|
||||
|
||||
// SYSTEM DATA
|
||||
var result1 = ConvertToModbusRegisters((protcolNumber * 10), "UInt16", 30000); // this to be updated to modbusTCP version
|
||||
var result2 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemDateTime.ToUnixTime(), "UInt32", 30001);
|
||||
var result3 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemOperatingMode, "UInt16", 30003);
|
||||
var result17 = ConvertToModbusRegisters(status.AcDcGrowatt.OperatingPriority, "UInt16", 30004);
|
||||
var result1 = ConvertToModbusRegisters((protcolNumber * 10), "UInt16", 30001); // this to be updated to modbusTCP version
|
||||
var result2 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemDateTime.ToUnixTime(), "UInt32", 30002);
|
||||
var result3 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemOperatingMode, "UInt16", 30004);
|
||||
var result17 = ConvertToModbusRegisters(status.AcDcGrowatt.OperatingPriority, "UInt16", 30005);
|
||||
|
||||
// BATTERY SUMMARY (assuming single battery [0]) // this to be improved
|
||||
var battery = status.AcDcGrowatt.BatteriesRecords!.Batteries[0];
|
||||
|
|
@ -348,20 +414,25 @@ public static class Program
|
|||
var result10 = ConvertToModbusRegisters((battery.MaxAllowableDischargePower.Value * 10), "UInt32", 31011);
|
||||
var result11 = ConvertToModbusRegisters((battery.MaxAllowableDischargePower.Value * 10), "UInt32", 31013);
|
||||
|
||||
var result12 = ConvertToModbusRegisters((battery.Voltage.Value * 10), "Int16", 31015);
|
||||
var result13 = ConvertToModbusRegisters((battery.Current.Value * 10), "Int32", 31016);
|
||||
var result14 = ConvertToModbusRegisters((battery.Soc.Value * 100), "UInt16", 31018);
|
||||
var result15 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteriesRecords!.AverageSoh * 100), "UInt16", 31020);
|
||||
var result16 = ConvertToModbusRegisters((battery.BatteryAmbientTemperature.Value * 100), "UInt16", 31021);
|
||||
var result12 = ConvertToModbusRegisters((battery.Voltage.Value * 10), "Int16", 31016);
|
||||
var result13 = ConvertToModbusRegisters((battery.Current.Value * 10), "Int32", 31017);
|
||||
var result14 = ConvertToModbusRegisters((battery.Soc.Value * 100), "UInt16", 31019);
|
||||
var result15 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteriesRecords!.AverageSoh * 100), "UInt16", 31022);
|
||||
var result16 = ConvertToModbusRegisters((battery.BatteryAmbientTemperature.Value * 100), "UInt16", 31023);
|
||||
var result18 = ConvertToModbusRegisters(((pv1Power + pv2Power) * 10), "UInt32", 31999);
|
||||
var result19 = ConvertToModbusRegisters((status.AcDcGrowatt.GridMeterPower.Value * 10), "Int32", 32999);
|
||||
|
||||
var result20 = ConvertToModbusRegisters((status.AcDcGrowatt.DischargeCutoffSoc * 100), "UInt16", 31020);
|
||||
var result21 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryMaxChargeCurrent * 10), "UInt16", 31024);
|
||||
var result22 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryMaxdischargeCurrent * 10), "UInt16", 31025);
|
||||
var result23 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteryChargeCutoffVoltage * 10), "UInt16", 31026);
|
||||
|
||||
|
||||
// Merge all results into one dictionary
|
||||
var allResults = new[]
|
||||
{
|
||||
result1,result2, result3, result17, result4, result5, result6, result7, result8,
|
||||
result9, result10, result11, result12, result13, result14, result15, result16, result18, result19
|
||||
result9, result10, result11, result12, result13, result14, result15, result16, result18, result19,result20,
|
||||
result21, result22, result23
|
||||
};
|
||||
|
||||
foreach (var result in allResults)
|
||||
|
|
@ -377,9 +448,10 @@ public static class Program
|
|||
|
||||
//Console.WriteLine("JSON file written successfully.");
|
||||
//Console.WriteLine(json);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static async Task<Boolean> DataLogging(StatusRecord status, Int64 timestamp)
|
||||
private static async Task<Boolean> DataLogging(StatusRecord status, DateTime timeStamp)
|
||||
{
|
||||
var csv = status.ToCsv();
|
||||
|
||||
|
|
@ -389,7 +461,7 @@ public static class Program
|
|||
// Console.WriteLine(item + "");
|
||||
//}
|
||||
|
||||
await SavingLocalCsvFile(timestamp, csv);
|
||||
await SavingLocalCsvFile(timeStamp.ToUnixTime(), csv);
|
||||
|
||||
var jsonData = new Dictionary<String, Object>();
|
||||
|
||||
|
|
@ -401,7 +473,7 @@ public static class Program
|
|||
return false;
|
||||
|
||||
//Concatenating 15 files in one file
|
||||
return await ConcatinatingAndCompressingFiles(timestamp, s3Config);
|
||||
return await ConcatinatingAndCompressingFiles(timeStamp.ToUnixTime(), s3Config);
|
||||
}
|
||||
|
||||
private static String ConvertToJson(String csv, Dictionary<String, Object> jsonData)
|
||||
|
|
@ -650,34 +722,5 @@ public static class Program
|
|||
|
||||
return compressedBytes;
|
||||
}
|
||||
|
||||
private static byte[] ComputeCrc16Appended(byte[] data)
|
||||
{
|
||||
ushort crc = 0xFFFF;
|
||||
|
||||
foreach (byte b in data)
|
||||
{
|
||||
crc ^= b;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
bool lsb = (crc & 0x0001) != 0;
|
||||
crc >>= 1;
|
||||
if (lsb)
|
||||
{
|
||||
crc ^= 0xA001;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte crcLow = (byte)(crc & 0xFF);
|
||||
byte crcHigh = (byte)((crc >> 8) & 0xFF);
|
||||
|
||||
// Create a new array with space for CRC
|
||||
byte[] result = new byte[data.Length + 2];
|
||||
Array.Copy(data, result, data.Length);
|
||||
result[result.Length - 2] = crcLow;
|
||||
result[result.Length - 1] = crcHigh;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue