parent
8ba880df05
commit
38f32b8bf7
|
|
@ -9,7 +9,5 @@ public class Configuration
|
||||||
public Double GridSetPoint { get; set; }
|
public Double GridSetPoint { get; set; }
|
||||||
public Double MaximumDischargingCurrent { get; set; }
|
public Double MaximumDischargingCurrent { get; set; }
|
||||||
public Double MaximumChargingCurrent { get; set; }
|
public Double MaximumChargingCurrent { get; set; }
|
||||||
public DateTime CalibrationChargeDate { get; set; }
|
|
||||||
public DateTime CalibrationDischargeDate { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace InnovEnergy.App.GrowattCommunication.ESS;
|
||||||
|
|
||||||
|
public enum EssMode
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
GridPriority,
|
||||||
|
BatteryPriority,
|
||||||
|
LoadPriority
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,6 @@ namespace InnovEnergy.App.GrowattCommunication.ESS;
|
||||||
|
|
||||||
public record StatusRecord
|
public record StatusRecord
|
||||||
{
|
{
|
||||||
public required WITGrowatRecord AcDcGrowatt { get; set; }
|
public required WitGrowattDevicesRecord AcDcGrowatt { get; set; }
|
||||||
public required Config Config { get; set; }
|
public required Config Config { get; set; }
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ namespace InnovEnergy.App.GrowattCommunication;
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
||||||
private const UInt16 NbrOfFileToConcatenate = 30; // add this to config file
|
private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file
|
||||||
private static UInt16 _fileCounter = 0;
|
private static UInt16 _fileCounter = 0;
|
||||||
//
|
//
|
||||||
private static Channel _growattChannel;
|
private static Channel _growattChannel;
|
||||||
|
|
@ -76,12 +76,14 @@ public static class Program
|
||||||
|
|
||||||
Console.WriteLine("Starting Growatt Communication");
|
Console.WriteLine("Starting Growatt Communication");
|
||||||
|
|
||||||
var growatrrDevicet415K = new WITGrowatDevice(_growattChannel, SlaveId);
|
var growattDeviceT415K = new WITGrowatDevice(_growattChannel, SlaveId);
|
||||||
|
var growattDevices = new WITGrowattDevices(new List<WITGrowatDevice> { growattDeviceT415K });
|
||||||
|
|
||||||
|
|
||||||
StatusRecord ReadStatus()
|
StatusRecord ReadStatus()
|
||||||
{
|
{
|
||||||
var config = Config.Load();
|
var config = Config.Load();
|
||||||
var growattRecord = growatrrDevicet415K.Read();
|
var growattRecord = growattDevices.Read();
|
||||||
|
|
||||||
return new StatusRecord
|
return new StatusRecord
|
||||||
{
|
{
|
||||||
|
|
@ -107,12 +109,12 @@ public static class Program
|
||||||
// the order matter of the next three lines
|
// the order matter of the next three lines
|
||||||
var statusrecord = ReadStatus();
|
var statusrecord = ReadStatus();
|
||||||
|
|
||||||
SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord);
|
// SendSalimaxStateAlarm(GetSodiHomeStateAlarm(statusrecord),statusrecord);
|
||||||
|
|
||||||
await DataLogging(statusrecord, timestamp); // save a csv file locally
|
await DataLogging(statusrecord, timestamp); // save a csv file locally
|
||||||
await SaveModbusTcpFile(statusrecord); // save the json file for modbuscTCP
|
await SaveModbusTcpFile(statusrecord); // save the json file for modbuscTCP
|
||||||
|
|
||||||
|
/*
|
||||||
statusrecord.AcDcGrowatt.EnableCommand.WriteLine(" = EnableCommand");
|
statusrecord.AcDcGrowatt.EnableCommand.WriteLine(" = EnableCommand");
|
||||||
statusrecord.AcDcGrowatt.ControlPermession.WriteLine(" ControlPermession");
|
statusrecord.AcDcGrowatt.ControlPermession.WriteLine(" ControlPermession");
|
||||||
statusrecord.AcDcGrowatt.GridMeterPower.WriteLine(" GridMeterPower");
|
statusrecord.AcDcGrowatt.GridMeterPower.WriteLine(" GridMeterPower");
|
||||||
|
|
@ -134,16 +136,10 @@ public static class Program
|
||||||
|
|
||||||
var stopTime = DateTime.Now;
|
var stopTime = DateTime.Now;
|
||||||
Console.WriteLine(stopTime.ToString("HH:mm:ss.fff"));
|
Console.WriteLine(stopTime.ToString("HH:mm:ss.fff"));
|
||||||
Console.WriteLine("***************************** Finish Battery Data *********************************************");
|
Console.WriteLine("***************************** Finish Battery Data *********************************************");*/
|
||||||
statusrecord.AcDcGrowatt.EnableCommand = true;
|
statusrecord.AcDcGrowatt.Devices[0].EnableCommand = true;
|
||||||
statusrecord.AcDcGrowatt.ControlPermession = true;
|
statusrecord.AcDcGrowatt.Devices[0].ControlPermession = true;
|
||||||
statusrecord.AcDcGrowatt.RemotePowerControl = true;
|
|
||||||
statusrecord.AcDcGrowatt.RemotePowerControlChargeDuration = 0; // 30408 this the duration
|
|
||||||
statusrecord.AcDcGrowatt.ActivePowerPercent = 50; // 30408 this the duration
|
|
||||||
statusrecord.AcDcGrowatt.ActivePowerPercentDerating = 50; // 30408 this the duration
|
|
||||||
|
|
||||||
statusrecord.AcDcGrowatt.RemoteChargDischargePower = 50; //30409 we set power here
|
|
||||||
statusrecord.AcDcGrowatt.ActualChargeDischargePowerControlValue.WriteLine(" register 30474");
|
|
||||||
|
|
||||||
|
|
||||||
statusrecord.ApplyDefaultSettings();
|
statusrecord.ApplyDefaultSettings();
|
||||||
|
|
@ -151,7 +147,7 @@ public static class Program
|
||||||
statusrecord.Config.Save(); // save the config file
|
statusrecord.Config.Save(); // save the config file
|
||||||
|
|
||||||
Console.WriteLine( " ************************************ We are writing ************************************");
|
Console.WriteLine( " ************************************ We are writing ************************************");
|
||||||
growatrrDevicet415K.Write(statusrecord.AcDcGrowatt);
|
growattDevices.Write(statusrecord.AcDcGrowatt);
|
||||||
|
|
||||||
// Wait for 2 seconds before the next reading
|
// Wait for 2 seconds before the next reading
|
||||||
// await Task.Delay(1000); // Delay in milliseconds (1000ms = 1 seconds)
|
// await Task.Delay(1000); // Delay in milliseconds (1000ms = 1 seconds)
|
||||||
|
|
@ -178,6 +174,36 @@ public static class Program
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String GetSodiHomeStateAlarm(StatusRecord statusrecord, EssMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case EssMode.Off:
|
||||||
|
return "no mode";
|
||||||
|
case EssMode.GridPriority:
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemotePowerControl = true;
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemotePowerControlChargeDuration = 0; // 30408 this the duration
|
||||||
|
//statusrecord.AcDcGrowatt.ActivePowerPercent = 50; // 30408 this the duration
|
||||||
|
//statusrecord.AcDcGrowatt.ActivePowerPercentDerating = 50; // 30408 this the duration
|
||||||
|
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemoteChargDischargePower = - 100; //30409 we set power here // for grid priority from 0 to -100
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set
|
||||||
|
return "Grid priority mode active";
|
||||||
|
case EssMode.BatteryPriority:
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemotePowerControl = true;
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemotePowerControlChargeDuration = 0; // 30408 this the duration
|
||||||
|
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemoteChargDischargePower = 100; //30409 we set power here // for grid priority from 0 to 100
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].ActualChargeDischargePowerControlValue.WriteLine(" register 30474");; // this to check what was set
|
||||||
|
return "Battery priority mode active";
|
||||||
|
case EssMode.LoadPriority:
|
||||||
|
statusrecord.AcDcGrowatt.Devices[0].RemotePowerControl = false;
|
||||||
|
return "Load priority mode active";
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static StatusMessage GetSodiHomeStateAlarm(StatusRecord record)
|
private static StatusMessage GetSodiHomeStateAlarm(StatusRecord record)
|
||||||
{
|
{
|
||||||
var s3Bucket = Config.Load().S3?.Bucket;
|
var s3Bucket = Config.Load().S3?.Bucket;
|
||||||
|
|
@ -185,27 +211,27 @@ public static class Program
|
||||||
var alarmList = new List<AlarmOrWarning>();
|
var alarmList = new List<AlarmOrWarning>();
|
||||||
var warningList = new List<AlarmOrWarning>();
|
var warningList = new List<AlarmOrWarning>();
|
||||||
|
|
||||||
if (record.AcDcGrowatt.SystemOperatingMode == GrowattSystemStatus.Fault)
|
if (record.AcDcGrowatt.Devices[0].SystemOperatingMode == GrowattSystemStatus.Fault)
|
||||||
{
|
{
|
||||||
if (record.AcDcGrowatt.FaultMainCode != 0)
|
if (record.AcDcGrowatt.Devices[0].FaultMainCode != 0)
|
||||||
{
|
{
|
||||||
alarmList.Add(new AlarmOrWarning
|
alarmList.Add(new AlarmOrWarning
|
||||||
{
|
{
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
CreatedBy = "Growatt Inverter",
|
CreatedBy = "Growatt Inverter",
|
||||||
Description = record.AcDcGrowatt.WarningMainCode.ToString(), // to add the sub code
|
Description = record.AcDcGrowatt.Devices[0].WarningMainCode.ToString(), // to add the sub code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.AcDcGrowatt.FaultSubCode != 0)
|
if (record.AcDcGrowatt.Devices[0].FaultSubCode != 0)
|
||||||
{
|
{
|
||||||
warningList.Add(new AlarmOrWarning
|
warningList.Add(new AlarmOrWarning
|
||||||
{
|
{
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
CreatedBy = "Growatt inverter",
|
CreatedBy = "Growatt inverter",
|
||||||
Description = record.AcDcGrowatt.FaultMainCode.ToString(), //to add the sub code
|
Description = record.AcDcGrowatt.Devices[0].FaultMainCode.ToString(), //to add the sub code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,14 +300,14 @@ public static class Program
|
||||||
if (st is null)
|
if (st is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
st.AcDcGrowatt.EmsCommunicationFailureTime = 20; // 20 sec
|
st.AcDcGrowatt.Devices[0].EmsCommunicationFailureTime = 20; // 20 sec
|
||||||
st.AcDcGrowatt.EnableEmsCommunicationFailureTime = true;
|
st.AcDcGrowatt.Devices[0].EnableEmsCommunicationFailureTime = true;
|
||||||
st.AcDcGrowatt.EnableCommand = true;
|
st.AcDcGrowatt.Devices[0].EnableCommand = true;
|
||||||
st.AcDcGrowatt.ControlPermession = true;
|
st.AcDcGrowatt.Devices[0].ControlPermession = true;
|
||||||
st.AcDcGrowatt.BatteryChargeCutoffVoltage = 100; //st.Config.BatteryChargeCutoffVoltage;
|
st.AcDcGrowatt.Devices[0].BatteryChargeCutoffVoltage = 100; //st.Config.BatteryChargeCutoffVoltage;
|
||||||
st.AcDcGrowatt.BatteryDischargeCutoffVoltage = 20; //st.Config.BatteryDischargeCutoffVoltage;
|
st.AcDcGrowatt.Devices[0].BatteryDischargeCutoffVoltage = 20; //st.Config.BatteryDischargeCutoffVoltage;
|
||||||
st.AcDcGrowatt.BatteryMaxChargeCurrent = 150; //st.Config.BatteryChargeCutoffVoltage;
|
st.AcDcGrowatt.Devices[0].BatteryMaxChargeCurrent = 150; //st.Config.BatteryChargeCutoffVoltage;
|
||||||
st.AcDcGrowatt.BatteryMaxdischargeCurrent = 150; //st.Config.BatteryChargeCutoffVoltage;
|
st.AcDcGrowatt.Devices[0].BatteryMaxdischargeCurrent = 150; //st.Config.BatteryChargeCutoffVoltage;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,16 +350,16 @@ public static class Program
|
||||||
{
|
{
|
||||||
var modbusData = new Dictionary<String, UInt16>();
|
var modbusData = new Dictionary<String, UInt16>();
|
||||||
|
|
||||||
Console.WriteLine(new DateTimeOffset(status.AcDcGrowatt.SystemDateTime).ToUnixTimeSeconds() + " This Growatt time");
|
Console.WriteLine(new DateTimeOffset(status.AcDcGrowatt.Devices[0].SystemDateTime).ToUnixTimeSeconds() + " This Growatt time");
|
||||||
// SYSTEM DATA
|
// SYSTEM DATA
|
||||||
var result1 = ConvertToModbusRegisters((status.AcDcGrowatt.VppProtocolVerNumber * 10), "UInt16", 30001);
|
var result1 = ConvertToModbusRegisters((status.AcDcGrowatt.Devices[0].VppProtocolVerNumber * 10), "UInt16", 30001);
|
||||||
var result2 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemDateTime.ToUnixTime(), "UInt32", 30002);
|
var result2 = ConvertToModbusRegisters(status.AcDcGrowatt.Devices[0].SystemDateTime.ToUnixTime(), "UInt32", 30002);
|
||||||
var result3 = ConvertToModbusRegisters(status.AcDcGrowatt.SystemOperatingMode, "Int16", 30004);
|
var result3 = ConvertToModbusRegisters(status.AcDcGrowatt.Devices[0].SystemOperatingMode, "Int16", 30004);
|
||||||
|
|
||||||
// BATTERY SUMMARY (assuming single battery [0])
|
// BATTERY SUMMARY (assuming single battery [0])
|
||||||
var battery = status.AcDcGrowatt.BatteriesRecords!.Batteries[0];
|
var battery = status.AcDcGrowatt.Devices[0].BatteriesRecords!.Batteries[0];
|
||||||
|
|
||||||
var result4 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteriesRecords!.Batteries.Count ), "UInt16", 31000);
|
var result4 = ConvertToModbusRegisters((status.AcDcGrowatt.Devices[0].BatteriesRecords!.Batteries.Count ), "UInt16", 31000);
|
||||||
var result5 = ConvertToModbusRegisters((battery.Power.Value * 10), "Int32", 31001);
|
var result5 = ConvertToModbusRegisters((battery.Power.Value * 10), "Int32", 31001);
|
||||||
var result6 = ConvertToModbusRegisters((battery.DailyChargeEnergy.Value * 10), "UInt32", 31003);
|
var result6 = ConvertToModbusRegisters((battery.DailyChargeEnergy.Value * 10), "UInt32", 31003);
|
||||||
var result7 = ConvertToModbusRegisters((battery.AccumulatedChargeEnergy.Value * 10), "UInt32", 31005);
|
var result7 = ConvertToModbusRegisters((battery.AccumulatedChargeEnergy.Value * 10), "UInt32", 31005);
|
||||||
|
|
@ -345,7 +371,7 @@ public static class Program
|
||||||
var result12 = ConvertToModbusRegisters((battery.Voltage.Value * 10), "Int16", 31015);
|
var result12 = ConvertToModbusRegisters((battery.Voltage.Value * 10), "Int16", 31015);
|
||||||
var result13 = ConvertToModbusRegisters((battery.Current.Value * 10), "Int32", 31016);
|
var result13 = ConvertToModbusRegisters((battery.Current.Value * 10), "Int32", 31016);
|
||||||
var result14 = ConvertToModbusRegisters((battery.Soc.Value * 100), "UInt16", 31018);
|
var result14 = ConvertToModbusRegisters((battery.Soc.Value * 100), "UInt16", 31018);
|
||||||
var result15 = ConvertToModbusRegisters((status.AcDcGrowatt.BatteriesRecords!.AverageSoh * 100), "UInt16", 31019);
|
var result15 = ConvertToModbusRegisters((status.AcDcGrowatt.Devices[0].BatteriesRecords!.AverageSoh * 100), "UInt16", 31019);
|
||||||
var result16 = ConvertToModbusRegisters((battery.BatteryAmbientTemperature.Value * 100), "UInt16", 31021);
|
var result16 = ConvertToModbusRegisters((battery.BatteryAmbientTemperature.Value * 100), "UInt16", 31021);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ public class BatteryRecord
|
||||||
{
|
{
|
||||||
public required Percent Soc { get; init; }
|
public required Percent Soc { get; init; }
|
||||||
public required Double Soh { get; init; }
|
public required Double Soh { get; init; }
|
||||||
// public required UInt16 ClusterTotalNumber { get; init; }
|
|
||||||
public required Current Current { get; init; }
|
public required Current Current { get; init; }
|
||||||
public required Voltage Voltage { get; init; }
|
public required Voltage Voltage { get; init; }
|
||||||
public required DcPower Power { get; init; }
|
public required DcPower Power { get; init; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Slaves;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K;
|
||||||
|
|
||||||
|
public class WITGrowattDevices
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<ModbusDevice<WITGrowatRecord>> _AcDcs;
|
||||||
|
private readonly IReadOnlyList<WITGrowatDevice> _Devices;
|
||||||
|
|
||||||
|
public WITGrowattDevices(IReadOnlyList<WITGrowatDevice> devices) => _Devices = devices;
|
||||||
|
|
||||||
|
public WitGrowattDevicesRecord? Read()
|
||||||
|
{
|
||||||
|
var records = _Devices
|
||||||
|
.Select(TryRead)
|
||||||
|
.NotNull()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return new WitGrowattDevicesRecord(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WITGrowatRecord? TryRead(WITGrowatDevice d)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return d.Read();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to read Battery node {d.SlaveId}\n{e.Message}");
|
||||||
|
// TODO: log
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(WitGrowattDevicesRecord r)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var (ctrl, device) in r.Devices.Zip(_AcDcs))
|
||||||
|
{
|
||||||
|
device.Write(ctrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
// TODO: log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K;
|
||||||
|
|
||||||
|
public class WitGrowattDevicesRecord
|
||||||
|
{
|
||||||
|
public static WitGrowattDevicesRecord Null => new WitGrowattDevicesRecord(Array.Empty<WITGrowatRecord>());
|
||||||
|
|
||||||
|
public WitGrowattDevicesRecord(IReadOnlyList<WITGrowatRecord> devices)
|
||||||
|
{
|
||||||
|
Devices = devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<WITGrowatRecord> Devices { get; init; }
|
||||||
|
|
||||||
|
//public IEnumerable<AlarmMessage> Alarms => new []{AlarmMessage.}; //Devices.SelectMany(d => d.Status.Alarms).Distinct();
|
||||||
|
//public IEnumerable<WarningMessage> Warnings => new []{WarningMessage.DcOffset}; //Devices.SelectMany(d => d.Status.Warnings).Distinct();
|
||||||
|
|
||||||
|
//public IEnumerable<AlarmMessage> Alarms => Devices.SelectMany(d => d.Status.Alarms).Distinct();
|
||||||
|
//public IEnumerable<WarningMessage> Warnings => Devices.SelectMany(d => d.Status.Warnings).Distinct();
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue