Compare commits

...

12 Commits

36 changed files with 1313 additions and 1400 deletions

View File

@ -9,6 +9,8 @@
<ItemGroup>
<ProjectReference Include="../../Lib/Devices/BatteryDeligreen/BatteryDeligreen.csproj" />
<ProjectReference Include="..\..\Lib\Devices\Amax5070\Amax5070.csproj" />
<ProjectReference Include="..\..\Lib\Protocols\Modbus\Modbus.csproj" />
<ProjectReference Include="..\..\Lib\Units\Units.csproj" />
</ItemGroup>

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.App.DeligreenBatteryCommunication;
public enum DeviceState
{
Disabled,
Measured,
Computed
}

View File

@ -1,4 +1,8 @@
using InnovEnergy.Lib.Devices.BatteryDeligreen;
using System.Net;
using InnovEnergy.Lib.Devices.BatteryDeligreen;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Utils;
using InnovEnergy.Lib.Utils.Net;
namespace InnovEnergy.App.DeligreenBatteryCommunication;
@ -6,7 +10,7 @@ internal static class Program
{
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
// private static readonly Channel? BatteriesChannel;
private static Channel _relaysChannel;
private const String Port = "/dev/ttyUSB0";
@ -19,18 +23,21 @@ internal static class Program
}
public static async Task Main(string[] args)
public static async Task Main(String[] args)
{
var listOfBatteries = new List<BatteryDeligreenDevice>
var device1 = new SalimaxDevice { Host = "10.0.1.3" , Port = 502, DeviceState = DeviceState.Measured };
_relaysChannel = CreateChannel(device1);
var saliMaxRelaysDevice = new RelaysDeviceAmax(_relaysChannel);
/* var listOfBatteries = new List<BatteryDeligreenDevice>
{
new BatteryDeligreenDevice(Port, 0),
new BatteryDeligreenDevice(Port, 1),
new BatteryDeligreenDevice(Port, 2),
new BatteryDeligreenDevice(Port, 3),
new BatteryDeligreenDevice(Port, 4)
new BatteryDeligreenDevice(Port, 1)
};
var batteryDevices = new BatteryDeligreenDevices(listOfBatteries);
var batteryDevices = new BatteryDeligreenDevices(listOfBatteries);*/
Console.WriteLine("Starting Battery Communication");
@ -40,21 +47,65 @@ internal static class Program
{
var startTime = DateTime.Now;
Console.WriteLine("***************************** Reading Battery Data *********************************************");
Console.WriteLine($"Start Reading all Batteries: {startTime}");
var batteriesRecord = batteryDevices.Read();
// Console.WriteLine($"Start Reading all Batteries: {startTime}");
//var batteriesRecord = batteryDevices.Read();
var stopTime = DateTime.Now;
Console.WriteLine($"Finish Reading all Batteries: {stopTime}");
//Console.WriteLine($"Finish Reading all Batteries: {stopTime}");
var relays = saliMaxRelaysDevice.Read();
Console.WriteLine("***************************** Writing register 27 to true Amax *********************************************");
relays.K0 = true;
relays.K1 = true;
relays.R0 = true;
relays.R1 = true;
relays.R2 = true;
relays.R3 = true;
// relays.K0Input.WriteLine(" : K0input");
saliMaxRelaysDevice.Write(relays);
Console.WriteLine("***************************** Reading Amax *********************************************");
// relays.K1GridBusIsConnectedToGrid.WriteLine(" : K1");
await Task.Delay(2000);
Console.WriteLine("***************************** Writing register 27 to false Amax *********************************************");
relays.K0 = false;
relays.K1 = false;
relays.R0 = false;
relays.R1 = false;
relays.R2 = false;
relays.R3 = false;
// relays.K0Input.WriteLine(" : K0input");
saliMaxRelaysDevice.Write(relays);
Console.WriteLine("***************************** Reading Amax *********************************************");
/*
Console.WriteLine("Time used for reading all batteries:" + (stopTime - startTime));
Console.WriteLine("Average SOC " + batteriesRecord?.Soc);
Console.WriteLine("SOC Battery 0 : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.Soc);
Console.WriteLine("SOC Battery 1 : " + batteriesRecord?.Devices[1].BatteryDeligreenDataRecord.Soc);
Console.WriteLine("SOC Battery 2 : " + batteriesRecord?.Devices[2].BatteryDeligreenDataRecord.Soc);
Console.WriteLine("SOC Battery 3 : " + batteriesRecord?.Devices[3].BatteryDeligreenDataRecord.Soc);
Console.WriteLine("SOC Battery 4 : " + batteriesRecord?.Devices[4].BatteryDeligreenDataRecord.Soc);
Console.WriteLine("Min Soc " + batteriesRecord?.CurrentMinSoc);
Console.WriteLine("count " + batteriesRecord?.Devices.Count);
Console.WriteLine("Cell Alarm 1 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellAlarmList[0]);
Console.WriteLine("Cell Alarm 2 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellAlarmList[1]);
Console.WriteLine("Cell Temperature Alarm 1 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellTemperatureAlarm[0]);
Console.WriteLine("Cell Temperature Alarm 2 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellTemperatureAlarm[1]);
Console.WriteLine("Battery 1 EnviTemp Alarm: " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.EnviTempAlarm);
Console.WriteLine("Battery 1 Current Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CurrentAlarm);
Console.WriteLine("Battery 2 EnviTemp Alarm: " + batteriesRecord?.Devices[1].BatteryDeligreenAlarmRecord.EnviTempAlarm);
Console.WriteLine("Battery 2 Current Alarm : " + batteriesRecord?.Devices[1].BatteryDeligreenAlarmRecord.CurrentAlarm);
Console.WriteLine("TotalVoltage Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.TotalVoltageAlarm);
Console.WriteLine("PowerTemp Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.PowerTempAlarm);*/
// Wait for 2 seconds before the next reading
await Task.Delay(2000); // Delay in milliseconds (2000ms = 2 seconds)
@ -66,6 +117,8 @@ internal static class Program
await Task.Delay(2000); // Delay in milliseconds (2000ms = 2 seconds)
}
}
Channel CreateChannel(SalimaxDevice device) => new TcpChannel(device);
}
}

View File

@ -1,8 +1,8 @@
using InnovEnergy.Lib.Devices.Amax5070;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
namespace InnovEnergy.App.DeligreenBatteryCommunication;
public class RelaysDeviceAmax
{
@ -12,13 +12,14 @@ public class RelaysDeviceAmax
public RelaysRecordAmax? Read()
{
try
{
return AmaxDevice.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAmax)}\n{e}".LogError();
$"Failed to read from {nameof(RelaysDeviceAmax)}\n{e}".WriteLine();
return null;
}
}
@ -31,7 +32,7 @@ public class RelaysDeviceAmax
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAmax)}\n{e}".LogError();
$"Failed to write to {nameof(RelaysDeviceAmax)}\n{e}".WriteLine();
}
}
}

View File

@ -0,0 +1,98 @@
using InnovEnergy.Lib.Devices.Amax5070;
namespace InnovEnergy.App.DeligreenBatteryCommunication;
public class RelaysRecordAmax
{
private readonly Amax5070Registers _regs;
private RelaysRecordAmax(Amax5070Registers regs) => _regs = regs;
//public UInt16 K0Input
//{
// get => _regs.DigitalInput;
//}
public Boolean K0
{
get => _regs.DigitalOutput0;
set => _regs.DigitalOutput0 = value;
}
public Boolean K1
{
get => _regs.DigitalOutput1;
set => _regs.DigitalOutput1 = value;
}
public Boolean K2
{
get => _regs.DigitalOutput2;
set => _regs.DigitalOutput2 = value;
}
public Boolean K3
{
get => _regs.DigitalOutput3;
set => _regs.DigitalOutput3 = value;
}
public Boolean R0
{
get => _regs.Relay12;
set => _regs.Relay12 = value;
}
public Boolean R1
{
get => _regs.Relay22;
set => _regs.Relay22 = value;
}
public Boolean R2
{
get => _regs.Relay32;
set => _regs.Relay32 = value;
}
public Boolean R3
{
get => _regs.Relay42;
set => _regs.Relay42 = value;
}
/*
public Boolean K1GridBusIsConnectedToGrid => _regs.DigitalInput22;
public Boolean K2IslandBusIsConnectedToGridBus => !_regs.DigitalInput20;
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
private Boolean K3Inverter1IsConnectedToIslandBus => !_regs.DigitalInput16;
private Boolean K3Inverter2IsConnectedToIslandBus => !_regs.DigitalInput17;
private Boolean K3Inverter3IsConnectedToIslandBus => !_regs.DigitalInput18;
private Boolean K3Inverter4IsConnectedToIslandBus => !_regs.DigitalInput19;
public Boolean FiWarning => !_regs.DigitalInput21;
public Boolean FiError => !_regs.DigitalInput23;*/
//public Boolean K2ConnectIslandBusToGridBus
//{
// get => _regs.Relay22;
// set => _regs.Relay22 = value;
//}
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._regs;
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
}

View File

@ -0,0 +1,13 @@
using InnovEnergy.Lib.Utils.Net;
namespace InnovEnergy.App.DeligreenBatteryCommunication;
public class SalimaxDevice : Ip4Address
{
public required DeviceState DeviceState { get; init; }
public override String ToString() => $"{base.ToString()} ({DeviceState})";
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), DeviceState);
}

View File

@ -1,4 +1,4 @@
#undef Amax
#define Amax
#undef GridLimit
using System.IO.Compression;

View File

@ -0,0 +1,211 @@
using System.Reflection.Metadata;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
public class CombinedAdamRelaysRecord : IRelaysRecord
{
private const UInt16 SlowFreq = 3000;
private const UInt16 HighFreq = 1000;
public CombinedAdamRelaysRecord(RelaysRecordAdam6060? relaysRecordAdam6060, RelaysRecordAdam6360D? relaysRecordAdam6360D)
{
_recordAdam6060 = relaysRecordAdam6060;
_recordAdam6360D = relaysRecordAdam6360D;
}
private static RelaysRecordAdam6060? _recordAdam6060;
private static RelaysRecordAdam6360D? _recordAdam6360D;
public static IRelaysRecord Instance { get; } = new CombinedAdamRelaysRecord(_recordAdam6060, _recordAdam6360D);
public Boolean K1GridBusIsConnectedToGrid => _recordAdam6360D.K1GridBusIsConnectedToGrid;
public Boolean K2IslandBusIsConnectedToGridBus => _recordAdam6360D.K2IslandBusIsConnectedToGridBus;
public Boolean FiWarning => _recordAdam6360D.FiWarning;
public Boolean FiError => _recordAdam6360D.FiError;
public Boolean K2ConnectIslandBusToGridBus
{
get => _recordAdam6360D.K2ConnectIslandBusToGridBus;
set => _recordAdam6360D.K2ConnectIslandBusToGridBus = value;
}
public Boolean Inverter1WagoStatus => _recordAdam6360D.Inverter1WagoStatus;
public Boolean Inverter2WagoStatus => _recordAdam6360D.Inverter2WagoStatus;
public Boolean Inverter3WagoStatus => _recordAdam6360D.Inverter3WagoStatus;
public Boolean Inverter4WagoStatus => _recordAdam6360D.Inverter4WagoStatus;
public Boolean Dc1WagoStatus => _recordAdam6060.Dc1WagoStatus;
public Boolean Dc2WagoStatus => _recordAdam6060.Dc2WagoStatus;
public Boolean Dc3WagoStatus => _recordAdam6060.Dc3WagoStatus;
public Boolean Dc4WagoStatus => _recordAdam6060.Dc4WagoStatus;
public Boolean DcSystemControlWagoStatus => _recordAdam6060.DcSystemControlWagoStatus;
public Boolean LedGreen { get => _recordAdam6360D.LedGreen; set => _recordAdam6360D.LedGreen = value;}
public Boolean LedRed { get => _recordAdam6360D.LedRed; set => _recordAdam6360D.LedRed = value;}
public Boolean Harvester1Step => _recordAdam6360D.Harvester1Step;
public Boolean Harvester2Step => _recordAdam6360D.Harvester2Step;
public Boolean Harvester3Step => _recordAdam6360D.Harvester3Step;
public Boolean Harvester4Step => _recordAdam6360D.Harvester4Step;
public UInt16 DigitalOutput0Mode { get => _recordAdam6360D.DigitalOutput0Mode; set => _recordAdam6360D.DigitalOutput0Mode = value; }
public UInt16 DigitalOutput1Mode
{
get => _recordAdam6360D.DigitalOutput1Mode;
set => _recordAdam6360D.DigitalOutput1Mode = value;
}
public UInt16 DigitalOutput2Mode
{
get => _recordAdam6360D.DigitalOutput2Mode;
set => _recordAdam6360D.DigitalOutput2Mode = value;
}
public UInt16 DigitalOutput3Mode
{
get => _recordAdam6360D.DigitalOutput3Mode;
set => _recordAdam6360D.DigitalOutput3Mode = value;
}
public UInt16 DigitalOutput4Mode
{
get => _recordAdam6360D.DigitalOutput4Mode;
set => _recordAdam6360D.DigitalOutput4Mode = value;
}
public UInt16 DigitalOutput5Mode
{
get => _recordAdam6360D.DigitalOutput5Mode;
set => _recordAdam6360D.DigitalOutput5Mode = value;
}
public Boolean Do0StartPulse { get => _recordAdam6360D.Do0Pulse; set => _recordAdam6360D.Do0Pulse = value; }
public Boolean Do1StartPulse { get => _recordAdam6360D.Do1Pulse; set => _recordAdam6360D.Do1Pulse = value; }
public Boolean Do2StartPulse { get => _recordAdam6360D.Do2Pulse; set => _recordAdam6360D.Do2Pulse = value; }
public Boolean Do3StartPulse { get => _recordAdam6360D.Do3Pulse; set => _recordAdam6360D.Do3Pulse = value; }
public Boolean Do4StartPulse { get => _recordAdam6360D.Do4Pulse; set => _recordAdam6360D.Do4Pulse = value; }
public Boolean Do5StartPulse { get => _recordAdam6360D.Do5Pulse; set => _recordAdam6360D.Do5Pulse = value; }
public UInt16 PulseOut0LowTime { get => _recordAdam6360D.PulseOut0LowTime; set => _recordAdam6360D.PulseOut0LowTime = value; }
public UInt16 PulseOut1LowTime { get => _recordAdam6360D.PulseOut1LowTime; set => _recordAdam6360D.PulseOut1LowTime = value; }
public UInt16 PulseOut2LowTime { get => _recordAdam6360D.PulseOut2LowTime; set => _recordAdam6360D.PulseOut2LowTime = value; }
public UInt16 PulseOut3LowTime { get => _recordAdam6360D.PulseOut3LowTime; set => _recordAdam6360D.PulseOut3LowTime = value; }
public UInt16 PulseOut4LowTime { get => _recordAdam6360D.PulseOut4LowTime; set => _recordAdam6360D.PulseOut4LowTime = value; }
public UInt16 PulseOut5LowTime { get => _recordAdam6360D.PulseOut5LowTime; set => _recordAdam6360D.PulseOut5LowTime = value; }
public UInt16 PulseOut0HighTime { get => _recordAdam6360D.PulseOut0HighTime; set => _recordAdam6360D.PulseOut0HighTime = value; }
public UInt16 PulseOut1HighTime { get => _recordAdam6360D.PulseOut1HighTime; set => _recordAdam6360D.PulseOut1HighTime = value; }
public UInt16 PulseOut2HighTime { get => _recordAdam6360D.PulseOut2HighTime; set => _recordAdam6360D.PulseOut2HighTime = value; }
public UInt16 PulseOut3HighTime { get => _recordAdam6360D.PulseOut3HighTime; set => _recordAdam6360D.PulseOut3HighTime = value; }
public UInt16 PulseOut4HighTime { get => _recordAdam6360D.PulseOut4HighTime; set => _recordAdam6360D.PulseOut4HighTime = value; }
public UInt16 PulseOut5HighTime { get => _recordAdam6360D.PulseOut5HighTime; set => _recordAdam6360D.PulseOut5HighTime = value; }
/**************************** Green LED *********************************/
public void PerformSolidGreenLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = true;
LedRed = false;
}
public void PerformSlowFlashingGreenLed()
{
PulseOut0HighTime = SlowFreq;
PulseOut0LowTime = SlowFreq;
DigitalOutput0Mode = 2;
Do0StartPulse = true;
Do1StartPulse = false; // make sure the red LED is off
Console.WriteLine("Green Slow Flashing Starting");
}
public void PerformFastFlashingGreenLed()
{
PulseOut0HighTime = HighFreq;
PulseOut0LowTime = HighFreq;
DigitalOutput0Mode = 2;
Do0StartPulse = true;
Do1StartPulse = false;// make sure the red LED is off
Console.WriteLine("Green Slow Flashing Starting");
}
/**************************** Orange LED *********************************/
public void PerformSolidOrangeLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = true;
LedRed = true;
}
public void PerformSlowFlashingOrangeLed()
{
PerformSlowFlashingGreenLed();
PerformSlowFlashingRedLed();
Do0StartPulse = true;
Do1StartPulse = true;
Console.WriteLine("Orange Slow Flashing Starting");
}
public void PerformFastFlashingOrangeLed()
{
PerformFastFlashingGreenLed();
PerformFastFlashingRedLed();
Do0StartPulse = true;
Do1StartPulse = true;
Console.WriteLine("Orange Fast Flashing Starting");
}
/**************************** RED LED *********************************/
public void PerformSolidRedLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = false;
LedRed = true;
}
public void PerformSlowFlashingRedLed()
{
PulseOut1HighTime = SlowFreq;
PulseOut1LowTime = SlowFreq;
DigitalOutput1Mode = 2;
Do0StartPulse = false; // make sure the green LED is off
Do1StartPulse = true;
Console.WriteLine("Red Slow Flashing Starting");
}
public void PerformFastFlashingRedLed()
{
PulseOut1HighTime = HighFreq;
PulseOut1LowTime = HighFreq;
DigitalOutput1Mode = 2;
Do0StartPulse = false; // make sure the green LED is off
Do1StartPulse = true;
Console.WriteLine("Red Fast Flashing Starting");
}
public RelaysRecordAdam6360D? GetAdam6360DRecord()
{
return _recordAdam6360D;
}
public RelaysRecordAdam6060? GetAdam6060Record()
{
return _recordAdam6060;
}
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus => _recordAdam6360D.K3InverterIsConnectedToIslandBus;
}

View File

@ -0,0 +1,78 @@
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public interface IRelaysRecord
{
Boolean K1GridBusIsConnectedToGrid { get; }
Boolean K2IslandBusIsConnectedToGridBus { get; }
IEnumerable<Boolean> K3InverterIsConnectedToIslandBus { get; }
Boolean FiWarning { get; }
Boolean FiError { get; }
Boolean K2ConnectIslandBusToGridBus { get; set; }
// Boolean Inverter1WagoRelay { get; set; } // to add in the future
// Boolean Inverter2WagoRelay { get; set; } // to add in the future
// Boolean Inverter3WagoRelay { get; set; } // to add in the future
// Boolean Inverter4WagoRelay { get; set; } // to add in the future
/* Boolean Inverter1WagoStatus { get; }
Boolean Inverter2WagoStatus { get; }
Boolean Inverter3WagoStatus { get; }
Boolean Inverter4WagoStatus { get; }
Boolean Dc1WagoStatus { get; } // to test
Boolean Dc2WagoStatus { get; } // to test
Boolean Dc3WagoStatus { get; } // to test
Boolean Dc4WagoStatus { get; } // to test
Boolean DcSystemControlWagoStatus { get; } // to test
Boolean LedGreen { get; set; }
Boolean LedRed { get; }
Boolean Harvester1Step { get; }
Boolean Harvester2Step { get; }
Boolean Harvester3Step { get; }
Boolean Harvester4Step { get; }
Boolean Do0StartPulse { get; set; }
Boolean Do1StartPulse { get; set; }
Boolean Do2StartPulse { get; set; }
Boolean Do3StartPulse { get; set; }
Boolean Do4StartPulse { get; set; }
Boolean Do5StartPulse { get; set; }
UInt16 DigitalOutput0Mode { get; set; }
UInt16 DigitalOutput1Mode { get; set; }
UInt16 DigitalOutput2Mode { get; set; }
UInt16 DigitalOutput3Mode { get; set; }
UInt16 DigitalOutput4Mode { get; set; }
UInt16 DigitalOutput5Mode { get; set; }
UInt16 PulseOut0LowTime { get; set; }
UInt16 PulseOut1LowTime { get; set; }
UInt16 PulseOut2LowTime { get; set; }
UInt16 PulseOut3LowTime { get; set; }
UInt16 PulseOut4LowTime { get; set; }
UInt16 PulseOut5LowTime { get; set; }
UInt16 PulseOut0HighTime { get; set; }
UInt16 PulseOut1HighTime { get; set; }
UInt16 PulseOut2HighTime { get; set; }
UInt16 PulseOut3HighTime { get; set; }
UInt16 PulseOut4HighTime { get; set; }
UInt16 PulseOut5HighTime { get; set; }
void PerformSolidGreenLed();
void PerformSlowFlashingGreenLed();
void PerformFastFlashingGreenLed();
void PerformSolidOrangeLed();
void PerformSlowFlashingOrangeLed();
void PerformFastFlashingOrangeLed();
void PerformSolidRedLed();
void PerformSlowFlashingRedLed();
void PerformFastFlashingRedLed();*/
}

View File

@ -0,0 +1,38 @@
using InnovEnergy.Lib.Devices.Adam6360D;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysDeviceAdam6360
{
private Adam6360DDevice AdamDevice6360D { get; }
public RelaysDeviceAdam6360(String hostname) => AdamDevice6360D = new Adam6360DDevice(hostname, 2);
public RelaysDeviceAdam6360(Channel channel) => AdamDevice6360D = new Adam6360DDevice(channel, 2);
public RelaysRecordAdam6360D? Read()
{
try
{
return AdamDevice6360D.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAdam6360)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAdam6360D r)
{
try
{
AdamDevice6360D.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAdam6360)}\n{e}".LogError();
}
}
}

View File

@ -0,0 +1,38 @@
using InnovEnergy.Lib.Devices.Adam6060;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysDeviceAdam6060
{
private Adam6060Device AdamDevice6060 { get; }
public RelaysDeviceAdam6060(String hostname) => AdamDevice6060 = new Adam6060Device(hostname, 2);
public RelaysDeviceAdam6060(Channel channel) => AdamDevice6060 = new Adam6060Device(channel, 2);
public RelaysRecordAdam6060? Read()
{
try
{
return AdamDevice6060.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAdam6060)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAdam6060 r)
{
try
{
AdamDevice6060.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAdam6060)}\n{e}".LogError();
}
}
}

View File

@ -0,0 +1,38 @@
using InnovEnergy.Lib.Devices.Amax5070;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysDeviceAmax
{
private Amax5070Device AmaxDevice { get; }
public RelaysDeviceAmax(String hostname) => AmaxDevice = new Amax5070Device(hostname);
public RelaysDeviceAmax(Channel channel) => AmaxDevice = new Amax5070Device(channel);
public RelaysRecordAmax? Read()
{
try
{
return AmaxDevice.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAmax)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAmax r)
{
try
{
AmaxDevice.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAmax)}\n{e}".LogError();
}
}
}

View File

@ -0,0 +1,24 @@
using InnovEnergy.Lib.Devices.Adam6060;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysRecordAdam6060
{
private readonly Adam6060Registers _Regs;
private RelaysRecordAdam6060(Adam6060Registers regs) => _Regs = regs;
public Boolean Dc1WagoStatus => _Regs.DigitalInput0; // to test
public Boolean Dc2WagoStatus => _Regs.DigitalInput1; // to test
public Boolean Dc3WagoStatus => _Regs.DigitalInput4; // to test
public Boolean Dc4WagoStatus => _Regs.DigitalInput5; // to test
public Boolean DcSystemControlWagoStatus => _Regs.DigitalInput3; // to test
public static implicit operator Adam6060Registers(RelaysRecordAdam6060 d) => d._Regs;
public static implicit operator RelaysRecordAdam6060(Adam6060Registers d) => new RelaysRecordAdam6060(d);
}

View File

@ -0,0 +1,79 @@
using InnovEnergy.Lib.Devices.Adam6360D;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysRecordAdam6360D
{
private readonly Adam6360DRegisters _Regs;
private RelaysRecordAdam6360D(Adam6360DRegisters regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput6;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput4;
public Boolean Inverter1WagoStatus => _Regs.DigitalInput8;
public Boolean Inverter2WagoStatus => _Regs.DigitalInput9;
public Boolean Inverter3WagoStatus => _Regs.DigitalInput10;
public Boolean Inverter4WagoStatus => _Regs.DigitalInput11;
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
private Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput0; // change it to private should be ok
private Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput1;
private Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput2;
private Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput3;
public Boolean FiWarning => !_Regs.DigitalInput5;
public Boolean FiError => !_Regs.DigitalInput7;
public Boolean Harvester1Step =>_Regs.DigitalOutput2;
public Boolean Harvester2Step =>_Regs.DigitalOutput3;
public Boolean Harvester3Step =>_Regs.DigitalOutput4;
public Boolean Harvester4Step =>_Regs.DigitalOutput5;
public Boolean LedGreen { get =>_Regs.DigitalOutput0; set => _Regs.DigitalOutput0 = value;}
public Boolean LedRed { get =>_Regs.DigitalOutput1; set => _Regs.DigitalOutput1 = value;}
public Boolean Do0Pulse { get => _Regs.Do0Pulse; set => _Regs.Do0Pulse = value;}
public Boolean Do1Pulse { get => _Regs.Do1Pulse; set => _Regs.Do1Pulse = value;}
public Boolean Do2Pulse { get => _Regs.Do2Pulse; set => _Regs.Do2Pulse = value;}
public Boolean Do3Pulse { get => _Regs.Do3Pulse; set => _Regs.Do3Pulse = value;}
public Boolean Do4Pulse { get => _Regs.Do4Pulse; set => _Regs.Do4Pulse = value;}
public Boolean Do5Pulse { get => _Regs.Do5Pulse; set => _Regs.Do5Pulse = value;}
public UInt16 PulseOut0LowTime { get => _Regs.PulseOut0LowTime; set => _Regs.PulseOut0LowTime = value;} //in milleseconds
public UInt16 PulseOut1LowTime { get => _Regs.PulseOut1LowTime; set => _Regs.PulseOut1LowTime = value;}
public UInt16 PulseOut2LowTime { get => _Regs.PulseOut2LowTime; set => _Regs.PulseOut2LowTime = value;}
public UInt16 PulseOut3LowTime { get => _Regs.PulseOut3LowTime; set => _Regs.PulseOut3LowTime = value;}
public UInt16 PulseOut4LowTime { get => _Regs.PulseOut4LowTime; set => _Regs.PulseOut4LowTime = value;}
public UInt16 PulseOut5LowTime { get => _Regs.PulseOut5LowTime; set => _Regs.PulseOut5LowTime = value;}
public UInt16 PulseOut0HighTime { get => _Regs.PulseOut0HighTime; set => _Regs.PulseOut0HighTime = value;} // in milleseconds
public UInt16 PulseOut1HighTime { get => _Regs.PulseOut1HighTime; set => _Regs.PulseOut1HighTime = value;}
public UInt16 PulseOut2HighTime { get => _Regs.PulseOut2HighTime; set => _Regs.PulseOut2HighTime = value;}
public UInt16 PulseOut3HighTime { get => _Regs.PulseOut3HighTime; set => _Regs.PulseOut3HighTime = value;}
public UInt16 PulseOut4HighTime { get => _Regs.PulseOut4HighTime; set => _Regs.PulseOut4HighTime = value;}
public UInt16 PulseOut5HighTime { get => _Regs.PulseOut5HighTime; set => _Regs.PulseOut5HighTime = value;}
public UInt16 DigitalOutput0Mode { get => _Regs.DigitalOutput0Mode; set => _Regs.DigitalOutput0Mode = value;} // To test: 0, 1 or 2
public UInt16 DigitalOutput1Mode { get => _Regs.DigitalOutput1Mode; set => _Regs.DigitalOutput1Mode = value;}
public UInt16 DigitalOutput2Mode { get => _Regs.DigitalOutput2Mode; set => _Regs.DigitalOutput2Mode = value;}
public UInt16 DigitalOutput3Mode { get => _Regs.DigitalOutput3Mode; set => _Regs.DigitalOutput3Mode = value;}
public UInt16 DigitalOutput4Mode { get => _Regs.DigitalOutput4Mode; set => _Regs.DigitalOutput4Mode = value;}
public UInt16 DigitalOutput5Mode { get => _Regs.DigitalOutput5Mode; set => _Regs.DigitalOutput5Mode = value;}
public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;}
public static implicit operator Adam6360DRegisters(RelaysRecordAdam6360D d) => d._Regs;
public static implicit operator RelaysRecordAdam6360D(Adam6360DRegisters d) => new RelaysRecordAdam6360D(d);
}

View File

@ -0,0 +1,134 @@
using InnovEnergy.Lib.Devices.Amax5070;
namespace InnovEnergy.App.SaliMax.SaliMaxRelays;
public class RelaysRecordAmax : IRelaysRecord
{
private readonly Amax5070Registers _Regs;
private RelaysRecordAmax(Amax5070Registers regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput22;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput20;
public Boolean Inverter1WagoStatus => _Regs.DigitalInput0;
public Boolean Inverter2WagoStatus => _Regs.DigitalInput1;
public Boolean Inverter3WagoStatus => _Regs.DigitalInput2;
public Boolean Inverter4WagoStatus => _Regs.DigitalInput3;
public Boolean Dc1WagoStatus => _Regs.DigitalInput6;
public Boolean Dc2WagoStatus => _Regs.DigitalInput7;
public Boolean Dc3WagoStatus => _Regs.DigitalInput10;
public Boolean Dc4WagoStatus => _Regs.DigitalInput11;
public Boolean DcSystemControlWagoStatus => _Regs.DigitalInput9;
public Boolean LedGreen
{
get => _Regs.DigitalOutput0;
set => _Regs.DigitalOutput0 = value;
}
public Boolean LedRed => _Regs.DigitalOutput1;
public Boolean Harvester1Step => _Regs.DigitalOutput2;
public Boolean Harvester2Step => _Regs.DigitalOutput3;
public Boolean Harvester3Step => _Regs.DigitalOutput4;
public Boolean Harvester4Step => _Regs.DigitalOutput5;
/* public Boolean Do0StartPulse { get; set; }
public Boolean Do1StartPulse { get; set; }
public Boolean Do2StartPulse { get; set; }
public Boolean Do3StartPulse { get; set; }
public Boolean Do4StartPulse { get; set; }
public Boolean Do5StartPulse { get; set; }
public UInt16 DigitalOutput0Mode { get; set; }
public UInt16 DigitalOutput1Mode { get; set; }
public UInt16 DigitalOutput2Mode { get; set; }
public UInt16 DigitalOutput3Mode { get; set; }
public UInt16 DigitalOutput4Mode { get; set; }
public UInt16 DigitalOutput5Mode { get; set; }
public UInt16 PulseOut0LowTime { get; set; }
public UInt16 PulseOut1LowTime { get; set; }
public UInt16 PulseOut2LowTime { get; set; }
public UInt16 PulseOut3LowTime { get; set; }
public UInt16 PulseOut4LowTime { get; set; }
public UInt16 PulseOut5LowTime { get; set; }
public UInt16 PulseOut0HighTime { get; set; }
public UInt16 PulseOut1HighTime { get; set; }
public UInt16 PulseOut2HighTime { get; set; }
public UInt16 PulseOut3HighTime { get; set; }
public UInt16 PulseOut4HighTime { get; set; }
public UInt16 PulseOut5HighTime { get; set; }*/
/*
public void PerformSolidGreenLed()
{
Console.WriteLine("Solid Green: This is not yet implemented ");
}
public void PerformSlowFlashingGreenLed()
{
Console.WriteLine("Slow Flashing Green: This is not yet implemented ");
}
public void PerformFastFlashingGreenLed()
{
Console.WriteLine("Fast Flashing Green: This is not yet implemented ");
}
public void PerformSolidOrangeLed()
{
Console.WriteLine("Solid Orange: This is not yet implemented ");
}
public void PerformSlowFlashingOrangeLed()
{
Console.WriteLine("Slow Flashing Orange: This is not yet implemented ");
}
public void PerformFastFlashingOrangeLed()
{
Console.WriteLine("Fast Flashing Orange: This is not yet implemented ");
}
public void PerformSolidRedLed()
{
Console.WriteLine("Solid Red: This is not yet implemented ");
}
public void PerformSlowFlashingRedLed()
{
Console.WriteLine("Slow Flashing Red: This is not yet implemented ");
}
public void PerformFastFlashingRedLed()
{
Console.WriteLine("Fast Flashing Red: This is not yet implemented ");
}*/
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
public Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput16;
public Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput17;
public Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput18;
public Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput19;
public Boolean FiWarning => !_Regs.DigitalInput21;
public Boolean FiError => !_Regs.DigitalInput23;
public Boolean K2ConnectIslandBusToGridBus
{
get => _Regs.Relay23;
set => _Regs.Relay23 = value;
}
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._Regs;
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
}

View File

@ -53,7 +53,6 @@ public static class Controller
};
var essDelta = s.ComputePowerDelta(mode);
essDelta.WriteLine("Power Correction");
var unlimitedControl = new EssControl
{
@ -141,9 +140,6 @@ public static class Controller
{
var chargePower = s.AcDc.Devices.Sum(d => d.Status.Nominal.Power.Value);
s.Config.GridSetPoint.WriteLine(" GridSetPoint");
return mode switch
{
EssMode.ReachMinSoc => s.ControlInverterPower(chargePower),

View File

@ -1,4 +1,4 @@
#undef Amax
#define Amax
#undef GridLimit
using System.Diagnostics;
@ -54,7 +54,6 @@ internal static class Program
private static readonly Channel PvOnAcGrid;
private static readonly Channel PvOnAcIsland;
private static readonly Channel RelaysChannel;
private static readonly Channel RelaysTsChannel;
private static readonly Channel BatteriesChannel;
private static Boolean _curtailFlag = false;
@ -86,7 +85,6 @@ internal static class Program
PvOnAcGrid = CreateChannel(d.PvOnAcGrid);
PvOnAcIsland = CreateChannel(d.PvOnAcIsland);
RelaysChannel = CreateChannel(d.RelaysIp);
RelaysTsChannel = CreateChannel(d.TsRelaysIp);
BatteriesChannel = CreateChannel(d.BatteryIp);
BatteryNodes = config
@ -140,13 +138,11 @@ internal static class Program
var pvOnDcDevice = new AmptDevices(PvOnDc);
var pvOnAcGridDevice = new AmptDevices(PvOnAcGrid);
var pvOnAcIslandDevice = new AmptDevices(PvOnAcIsland);
var saliMaxTsRelaysDevice = new RelaysDeviceAdam6060(RelaysTsChannel);
#if Amax
var saliMaxRelaysDevice = new RelaysDeviceAmax(RelaysChannel);
#else
var saliMaxRelaysDevice = new RelaysDeviceAdam6360(RelaysChannel);
var saliMaxRelaysDevice = new RelaysDevice(RelaysChannel);
#endif
@ -157,12 +153,10 @@ internal static class Program
var acDc = acDcDevices.Read();
var dcDc = dcDcDevices.Read();
var relays = saliMaxRelaysDevice.Read();
var tsRelays = saliMaxTsRelaysDevice.Read();
var loadOnAcIsland = acIslandLoadMeter.Read();
var gridMeter = gridMeterDevice.Read();
var pvOnDc = pvOnDcDevice.Read();
var battery = batteryDevices.Read();
var pvOnAcGrid = pvOnAcGridDevice.Read();
var pvOnAcIsland = pvOnAcIslandDevice.Read();
@ -180,18 +174,13 @@ internal static class Program
Topology.CalculateAcDcToDcLink(pvOnDc, dcDc, acDc)
: new DcPowerDevice{ Power = acDc.Dc.Power};
#if Amax
var combinedRelays = relays;
#else
var combinedRelays = new CombinedAdamRelaysRecord(tsRelays, relays);
#endif
return new StatusRecord
{
AcDc = acDc,
DcDc = dcDc,
Battery = battery,
Relays = combinedRelays,
Relays = relays,
GridMeter = gridMeter,
PvOnAcGrid = pvOnAcGrid,
PvOnAcIsland = pvOnAcIsland,
@ -215,13 +204,9 @@ internal static class Program
#if Amax
saliMaxRelaysDevice.Write((RelaysRecordAmax)r.Relays);
#else
if (r.Relays is CombinedAdamRelaysRecord adamRelays)
{
saliMaxRelaysDevice.Write(adamRelays.GetAdam6360DRecord() ?? throw new InvalidOperationException());
saliMaxTsRelaysDevice.Write(adamRelays.GetAdam6060Record() ?? throw new InvalidOperationException());
}
((RelaysDevice)saliMaxRelaysDevice).Write((RelaysRecord)r.Relays);
#endif
}
acDcDevices.Write(r.AcDc);
@ -246,41 +231,13 @@ internal static class Program
Watchdog.NotifyAlive();
var record = ReadStatus();
/*
if (record.Relays != null)
{
record.Relays.Do0StartPulse = true;
record.Relays.PulseOut0HighTime = 20000;
record.Relays.PulseOut0LowTime = 20000;
record.Relays.DigitalOutput0Mode = 2;
record.Relays.LedGreen = false;
record.Relays.Do0StartPulse.WriteLine(" = start pulse 0");
record.Relays.PulseOut0HighTime.WriteLine(" = PulseOut0HighTime");
record.Relays.PulseOut0LowTime.WriteLine(" = PulseOut0LowTime");
record.Relays.DigitalOutput0Mode.WriteLine(" = DigitalOutput0Mode");
record.Relays.LedGreen.WriteLine(" = LedGreen");
record.Relays.LedRed.WriteLine(" = LedRed");
}
else
{
" Relays are null".WriteLine();
}*/
SendSalimaxStateAlarm(GetSalimaxStateAlarm(record), record); // to improve
record.ControlConstants();
record.ControlSystemState();
record.ControlPvPower(record.Config.CurtailP, record.Config.PvInstalledPower);
//record.ControlPvPower(record.Config.CurtailP, record.Config.PvInstalledPower);
var essControl = record.ControlEss().WriteLine();
@ -291,7 +248,7 @@ internal static class Program
DistributePower(record, essControl);
record.PerformLed();
//record.PerformLed();
WriteControl(record);
@ -529,8 +486,13 @@ internal static class Program
var inverters = r.AcDc.Devices;
var dcDevices = r.DcDc.Devices;
var configFile = r.Config;
//var maxBatteryDischargingCurrentLive = 0.0; //never used with deligreenBattery
var devicesConfig = r.AcDc.Devices.All(d => d.Control.Ac.GridType == GridType.GridTied400V50Hz) ? configFile.GridTie : configFile.IslandMode; // TODO if any of the grid tie mode
Double maxBatteryChargingCurrentLive ; //used with deligreenBattery for limiting charging
Double maxBatteryDischargingCurrentLive; //used with deligreenBattery for limiting discharging
//var maxBatteryDischargingCurrentLive = 0.0; //never used with deligreenBattery
/*
// This adapting the max discharging current to the current Active Strings
if (r.Battery != null)
@ -561,6 +523,27 @@ internal static class Program
*/
// TODO The discharging current is well calculated but not communicated to live. But Written in S3
// Deligreen upper current limitation dynCCL
if (r.Battery?.Voltage != null && (r.Battery?.Voltage ?? 0) > 61.0 )
{
maxBatteryChargingCurrentLive = r.Battery.Devices.Count * 10; // Max charging current is 10 A * Number of batteries
maxBatteryChargingCurrentLive.WriteLine("dynCCL Active: Max Battery Charging is "+ maxBatteryChargingCurrentLive);
}
else
{
maxBatteryChargingCurrentLive = devicesConfig.DcDc.MaxBatteryChargingCurrent;
}
// Deligreen lower current limitation dynDCL
if (r.Battery?.Soc != null && (r.Battery?.Soc ?? 100) < r.Config.MinSoc )
{
maxBatteryDischargingCurrentLive = 0; // Max charging current is 10 A * Number of batteries
maxBatteryDischargingCurrentLive.WriteLine("dynDCL Active: Max Battery disCharging is "+ maxBatteryDischargingCurrentLive);
}
else
{
maxBatteryDischargingCurrentLive = devicesConfig.DcDc.MaxBatteryDischargingCurrent;
}
inverters.ForEach(d => d.Control.Dc.MaxVoltage = devicesConfig.AcDc.MaxDcLinkVoltage);
inverters.ForEach(d => d.Control.Dc.MinVoltage = devicesConfig.AcDc.MinDcLinkVoltage);
@ -572,8 +555,8 @@ internal static class Program
dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = devicesConfig.DcDc.LowerDcLinkVoltage);
dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = devicesConfig.DcDc.ReferenceDcLinkVoltage);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = devicesConfig.DcDc.MaxBatteryChargingCurrent);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = devicesConfig.DcDc.MaxBatteryDischargingCurrent);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = maxBatteryChargingCurrentLive);
dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = maxBatteryDischargingCurrentLive);
dcDevices.ForEach(d => d.Control.MaxDcPower = devicesConfig.DcDc.MaxDcPower);
dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = devicesConfig.DcDc.MaxChargeBatteryVoltage);
@ -699,7 +682,7 @@ internal static class Program
}
// To test, most probably the curtailing flag will not work
private static void PerformLed(this StatusRecord record)
/*private static void PerformLed(this StatusRecord record)
{
if (record.StateMachine.State == 23)
{
@ -751,7 +734,7 @@ internal static class Program
{
record.Relays?.PerformFastFlashingRedLed();
}
}
}*/
private static Double IncreaseInverterUpperLimit(Double upperLimit, Double stepSize)
{

View File

@ -1,211 +0,0 @@
using System.Reflection.Metadata;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
public class CombinedAdamRelaysRecord : IRelaysRecord
{
private const UInt16 SlowFreq = 3000;
private const UInt16 HighFreq = 1000;
public CombinedAdamRelaysRecord(RelaysRecordAdam6060? relaysRecordAdam6060, RelaysRecordAdam6360D? relaysRecordAdam6360D)
{
_recordAdam6060 = relaysRecordAdam6060;
_recordAdam6360D = relaysRecordAdam6360D;
}
private static RelaysRecordAdam6060? _recordAdam6060;
private static RelaysRecordAdam6360D? _recordAdam6360D;
public static IRelaysRecord Instance { get; } = new CombinedAdamRelaysRecord(_recordAdam6060, _recordAdam6360D);
public Boolean K1GridBusIsConnectedToGrid => _recordAdam6360D.K1GridBusIsConnectedToGrid;
public Boolean K2IslandBusIsConnectedToGridBus => _recordAdam6360D.K2IslandBusIsConnectedToGridBus;
public Boolean FiWarning => _recordAdam6360D.FiWarning;
public Boolean FiError => _recordAdam6360D.FiError;
public Boolean K2ConnectIslandBusToGridBus
{
get => _recordAdam6360D.K2ConnectIslandBusToGridBus;
set => _recordAdam6360D.K2ConnectIslandBusToGridBus = value;
}
public Boolean Inverter1WagoStatus => _recordAdam6360D.Inverter1WagoStatus;
public Boolean Inverter2WagoStatus => _recordAdam6360D.Inverter2WagoStatus;
public Boolean Inverter3WagoStatus => _recordAdam6360D.Inverter3WagoStatus;
public Boolean Inverter4WagoStatus => _recordAdam6360D.Inverter4WagoStatus;
public Boolean Dc1WagoStatus => _recordAdam6060.Dc1WagoStatus;
public Boolean Dc2WagoStatus => _recordAdam6060.Dc2WagoStatus;
public Boolean Dc3WagoStatus => _recordAdam6060.Dc3WagoStatus;
public Boolean Dc4WagoStatus => _recordAdam6060.Dc4WagoStatus;
public Boolean DcSystemControlWagoStatus => _recordAdam6060.DcSystemControlWagoStatus;
public Boolean LedGreen { get => _recordAdam6360D.LedGreen; set => _recordAdam6360D.LedGreen = value;}
public Boolean LedRed { get => _recordAdam6360D.LedRed; set => _recordAdam6360D.LedRed = value;}
public Boolean Harvester1Step => _recordAdam6360D.Harvester1Step;
public Boolean Harvester2Step => _recordAdam6360D.Harvester2Step;
public Boolean Harvester3Step => _recordAdam6360D.Harvester3Step;
public Boolean Harvester4Step => _recordAdam6360D.Harvester4Step;
public UInt16 DigitalOutput0Mode { get => _recordAdam6360D.DigitalOutput0Mode; set => _recordAdam6360D.DigitalOutput0Mode = value; }
public UInt16 DigitalOutput1Mode
{
get => _recordAdam6360D.DigitalOutput1Mode;
set => _recordAdam6360D.DigitalOutput1Mode = value;
}
public UInt16 DigitalOutput2Mode
{
get => _recordAdam6360D.DigitalOutput2Mode;
set => _recordAdam6360D.DigitalOutput2Mode = value;
}
public UInt16 DigitalOutput3Mode
{
get => _recordAdam6360D.DigitalOutput3Mode;
set => _recordAdam6360D.DigitalOutput3Mode = value;
}
public UInt16 DigitalOutput4Mode
{
get => _recordAdam6360D.DigitalOutput4Mode;
set => _recordAdam6360D.DigitalOutput4Mode = value;
}
public UInt16 DigitalOutput5Mode
{
get => _recordAdam6360D.DigitalOutput5Mode;
set => _recordAdam6360D.DigitalOutput5Mode = value;
}
public Boolean Do0StartPulse { get => _recordAdam6360D.Do0Pulse; set => _recordAdam6360D.Do0Pulse = value; }
public Boolean Do1StartPulse { get => _recordAdam6360D.Do1Pulse; set => _recordAdam6360D.Do1Pulse = value; }
public Boolean Do2StartPulse { get => _recordAdam6360D.Do2Pulse; set => _recordAdam6360D.Do2Pulse = value; }
public Boolean Do3StartPulse { get => _recordAdam6360D.Do3Pulse; set => _recordAdam6360D.Do3Pulse = value; }
public Boolean Do4StartPulse { get => _recordAdam6360D.Do4Pulse; set => _recordAdam6360D.Do4Pulse = value; }
public Boolean Do5StartPulse { get => _recordAdam6360D.Do5Pulse; set => _recordAdam6360D.Do5Pulse = value; }
public UInt16 PulseOut0LowTime { get => _recordAdam6360D.PulseOut0LowTime; set => _recordAdam6360D.PulseOut0LowTime = value; }
public UInt16 PulseOut1LowTime { get => _recordAdam6360D.PulseOut1LowTime; set => _recordAdam6360D.PulseOut1LowTime = value; }
public UInt16 PulseOut2LowTime { get => _recordAdam6360D.PulseOut2LowTime; set => _recordAdam6360D.PulseOut2LowTime = value; }
public UInt16 PulseOut3LowTime { get => _recordAdam6360D.PulseOut3LowTime; set => _recordAdam6360D.PulseOut3LowTime = value; }
public UInt16 PulseOut4LowTime { get => _recordAdam6360D.PulseOut4LowTime; set => _recordAdam6360D.PulseOut4LowTime = value; }
public UInt16 PulseOut5LowTime { get => _recordAdam6360D.PulseOut5LowTime; set => _recordAdam6360D.PulseOut5LowTime = value; }
public UInt16 PulseOut0HighTime { get => _recordAdam6360D.PulseOut0HighTime; set => _recordAdam6360D.PulseOut0HighTime = value; }
public UInt16 PulseOut1HighTime { get => _recordAdam6360D.PulseOut1HighTime; set => _recordAdam6360D.PulseOut1HighTime = value; }
public UInt16 PulseOut2HighTime { get => _recordAdam6360D.PulseOut2HighTime; set => _recordAdam6360D.PulseOut2HighTime = value; }
public UInt16 PulseOut3HighTime { get => _recordAdam6360D.PulseOut3HighTime; set => _recordAdam6360D.PulseOut3HighTime = value; }
public UInt16 PulseOut4HighTime { get => _recordAdam6360D.PulseOut4HighTime; set => _recordAdam6360D.PulseOut4HighTime = value; }
public UInt16 PulseOut5HighTime { get => _recordAdam6360D.PulseOut5HighTime; set => _recordAdam6360D.PulseOut5HighTime = value; }
/**************************** Green LED *********************************/
public void PerformSolidGreenLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = true;
LedRed = false;
}
public void PerformSlowFlashingGreenLed()
{
PulseOut0HighTime = SlowFreq;
PulseOut0LowTime = SlowFreq;
DigitalOutput0Mode = 2;
Do0StartPulse = true;
Do1StartPulse = false; // make sure the red LED is off
Console.WriteLine("Green Slow Flashing Starting");
}
public void PerformFastFlashingGreenLed()
{
PulseOut0HighTime = HighFreq;
PulseOut0LowTime = HighFreq;
DigitalOutput0Mode = 2;
Do0StartPulse = true;
Do1StartPulse = false;// make sure the red LED is off
Console.WriteLine("Green Slow Flashing Starting");
}
/**************************** Orange LED *********************************/
public void PerformSolidOrangeLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = true;
LedRed = true;
}
public void PerformSlowFlashingOrangeLed()
{
PerformSlowFlashingGreenLed();
PerformSlowFlashingRedLed();
Do0StartPulse = true;
Do1StartPulse = true;
Console.WriteLine("Orange Slow Flashing Starting");
}
public void PerformFastFlashingOrangeLed()
{
PerformFastFlashingGreenLed();
PerformFastFlashingRedLed();
Do0StartPulse = true;
Do1StartPulse = true;
Console.WriteLine("Orange Fast Flashing Starting");
}
/**************************** RED LED *********************************/
public void PerformSolidRedLed()
{
DigitalOutput0Mode = 0;
DigitalOutput1Mode = 0;
LedGreen = false;
LedRed = true;
}
public void PerformSlowFlashingRedLed()
{
PulseOut1HighTime = SlowFreq;
PulseOut1LowTime = SlowFreq;
DigitalOutput1Mode = 2;
Do0StartPulse = false; // make sure the green LED is off
Do1StartPulse = true;
Console.WriteLine("Red Slow Flashing Starting");
}
public void PerformFastFlashingRedLed()
{
PulseOut1HighTime = HighFreq;
PulseOut1LowTime = HighFreq;
DigitalOutput1Mode = 2;
Do0StartPulse = false; // make sure the green LED is off
Do1StartPulse = true;
Console.WriteLine("Red Fast Flashing Starting");
}
public RelaysRecordAdam6360D? GetAdam6360DRecord()
{
return _recordAdam6360D;
}
public RelaysRecordAdam6060? GetAdam6060Record()
{
return _recordAdam6060;
}
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus => _recordAdam6360D.K3InverterIsConnectedToIslandBus;
}

View File

@ -1,78 +0,0 @@
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public interface IRelaysRecord
{
Boolean K1GridBusIsConnectedToGrid { get; }
Boolean K2IslandBusIsConnectedToGridBus { get; }
IEnumerable<Boolean> K3InverterIsConnectedToIslandBus { get; }
Boolean FiWarning { get; }
Boolean FiError { get; }
Boolean K2ConnectIslandBusToGridBus { get; set; }
// Boolean Inverter1WagoRelay { get; set; } // to add in the future
// Boolean Inverter2WagoRelay { get; set; } // to add in the future
// Boolean Inverter3WagoRelay { get; set; } // to add in the future
// Boolean Inverter4WagoRelay { get; set; } // to add in the future
Boolean Inverter1WagoStatus { get; }
Boolean Inverter2WagoStatus { get; }
Boolean Inverter3WagoStatus { get; }
Boolean Inverter4WagoStatus { get; }
Boolean Dc1WagoStatus { get; } // to test
Boolean Dc2WagoStatus { get; } // to test
Boolean Dc3WagoStatus { get; } // to test
Boolean Dc4WagoStatus { get; } // to test
Boolean DcSystemControlWagoStatus { get; } // to test
Boolean LedGreen { get; set; }
Boolean LedRed { get; }
Boolean Harvester1Step { get; }
Boolean Harvester2Step { get; }
Boolean Harvester3Step { get; }
Boolean Harvester4Step { get; }
Boolean Do0StartPulse { get; set; }
Boolean Do1StartPulse { get; set; }
Boolean Do2StartPulse { get; set; }
Boolean Do3StartPulse { get; set; }
Boolean Do4StartPulse { get; set; }
Boolean Do5StartPulse { get; set; }
UInt16 DigitalOutput0Mode { get; set; }
UInt16 DigitalOutput1Mode { get; set; }
UInt16 DigitalOutput2Mode { get; set; }
UInt16 DigitalOutput3Mode { get; set; }
UInt16 DigitalOutput4Mode { get; set; }
UInt16 DigitalOutput5Mode { get; set; }
UInt16 PulseOut0LowTime { get; set; }
UInt16 PulseOut1LowTime { get; set; }
UInt16 PulseOut2LowTime { get; set; }
UInt16 PulseOut3LowTime { get; set; }
UInt16 PulseOut4LowTime { get; set; }
UInt16 PulseOut5LowTime { get; set; }
UInt16 PulseOut0HighTime { get; set; }
UInt16 PulseOut1HighTime { get; set; }
UInt16 PulseOut2HighTime { get; set; }
UInt16 PulseOut3HighTime { get; set; }
UInt16 PulseOut4HighTime { get; set; }
UInt16 PulseOut5HighTime { get; set; }
void PerformSolidGreenLed();
void PerformSlowFlashingGreenLed();
void PerformFastFlashingGreenLed();
void PerformSolidOrangeLed();
void PerformSlowFlashingOrangeLed();
void PerformFastFlashingOrangeLed();
void PerformSolidRedLed();
void PerformSlowFlashingRedLed();
void PerformFastFlashingRedLed();
}

View File

@ -0,0 +1,72 @@
using InnovEnergy.Lib.Devices.Adam6360D;
using InnovEnergy.Lib.Devices.Amax5070;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysDevice
{
private Adam6360DDevice AdamDevice { get; }
public RelaysDevice(String hostname) => AdamDevice = new Adam6360DDevice(hostname, 2);
public RelaysDevice(Channel channel) => AdamDevice = new Adam6360DDevice(channel, 2);
public RelaysRecord? Read()
{
try
{
return AdamDevice.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDevice)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecord r)
{
try
{
AdamDevice.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDevice)}\n{e}".LogError();
}
}
}
public class RelaysDeviceAmax
{
private Amax5070Device AmaxDevice { get; }
public RelaysDeviceAmax(Channel channel) => AmaxDevice = new Amax5070Device(channel);
public RelaysRecordAmax? Read()
{
try
{
return AmaxDevice.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAmax)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAmax r)
{
try
{
AmaxDevice.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAmax)}\n{e}".LogError();
}
}
}

View File

@ -1,40 +0,0 @@
using InnovEnergy.Lib.Devices.Adam6360D;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysDeviceAdam6360
{
private Adam6360DDevice AdamDevice6360D { get; }
public RelaysDeviceAdam6360(String hostname) => AdamDevice6360D = new Adam6360DDevice(hostname, 2);
public RelaysDeviceAdam6360(Channel channel) => AdamDevice6360D = new Adam6360DDevice(channel, 2);
public RelaysRecordAdam6360D? Read()
{
try
{
return AdamDevice6360D.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAdam6360)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAdam6360D r)
{
try
{
AdamDevice6360D.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAdam6360)}\n{e}".LogError();
}
}
}

View File

@ -1,38 +0,0 @@
using InnovEnergy.Lib.Devices.Adam6060;
using InnovEnergy.Lib.Protocols.Modbus.Channels;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysDeviceAdam6060
{
private Adam6060Device AdamDevice6060 { get; }
public RelaysDeviceAdam6060(String hostname) => AdamDevice6060 = new Adam6060Device(hostname, 2);
public RelaysDeviceAdam6060(Channel channel) => AdamDevice6060 = new Adam6060Device(channel, 2);
public RelaysRecordAdam6060? Read()
{
try
{
return AdamDevice6060.Read();
}
catch (Exception e)
{
$"Failed to read from {nameof(RelaysDeviceAdam6060)}\n{e}".LogError();
return null;
}
}
public void Write(RelaysRecordAdam6060 r)
{
try
{
AdamDevice6060.Write(r);
}
catch (Exception e)
{
$"Failed to write to {nameof(RelaysDeviceAdam6060)}\n{e}".LogError();
}
}
}

View File

@ -0,0 +1,83 @@
using InnovEnergy.Lib.Devices.Adam6360D;
using InnovEnergy.Lib.Devices.Amax5070;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public interface IRelaysRecord
{
Boolean K1GridBusIsConnectedToGrid { get; }
Boolean K2IslandBusIsConnectedToGridBus { get; }
IEnumerable<Boolean> K3InverterIsConnectedToIslandBus { get; }
Boolean FiWarning { get; }
Boolean FiError { get; }
Boolean K2ConnectIslandBusToGridBus { get; set; }
}
public class RelaysRecord : IRelaysRecord
{
private readonly Adam6360DRegisters _Regs;
private RelaysRecord(Adam6360DRegisters regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput6;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput4;
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
public Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput0;
public Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput1;
public Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput2;
public Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput3;
public Boolean FiWarning => !_Regs.DigitalInput5;
public Boolean FiError => !_Regs.DigitalInput7;
public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;}
public static implicit operator Adam6360DRegisters(RelaysRecord d) => d._Regs;
public static implicit operator RelaysRecord(Adam6360DRegisters d) => new RelaysRecord(d);
}
public class RelaysRecordAmax : IRelaysRecord
{
private readonly Amax5070Registers _Regs;
private RelaysRecordAmax(Amax5070Registers regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput22;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput20;
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
public Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput16;
public Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput17;
public Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput18;
public Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput19;
public Boolean FiWarning => !_Regs.DigitalInput21;
public Boolean FiError => !_Regs.DigitalInput23;
public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay23; set => _Regs.Relay23 = value;}
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._Regs;
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
}

View File

@ -1,24 +0,0 @@
using InnovEnergy.Lib.Devices.Adam6060;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysRecordAdam6060
{
private readonly Adam6060Registers _Regs;
private RelaysRecordAdam6060(Adam6060Registers regs) => _Regs = regs;
public Boolean Dc1WagoStatus => _Regs.DigitalInput0; // to test
public Boolean Dc2WagoStatus => _Regs.DigitalInput1; // to test
public Boolean Dc3WagoStatus => _Regs.DigitalInput4; // to test
public Boolean Dc4WagoStatus => _Regs.DigitalInput5; // to test
public Boolean DcSystemControlWagoStatus => _Regs.DigitalInput3; // to test
public static implicit operator Adam6060Registers(RelaysRecordAdam6060 d) => d._Regs;
public static implicit operator RelaysRecordAdam6060(Adam6060Registers d) => new RelaysRecordAdam6060(d);
}

View File

@ -1,81 +0,0 @@
using InnovEnergy.Lib.Devices.Adam6360D;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysRecordAdam6360D
{
private readonly Adam6360DRegisters _Regs;
private RelaysRecordAdam6360D(Adam6360DRegisters regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput6;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput4;
public Boolean Inverter1WagoStatus => _Regs.DigitalInput8;
public Boolean Inverter2WagoStatus => _Regs.DigitalInput9;
public Boolean Inverter3WagoStatus => _Regs.DigitalInput10;
public Boolean Inverter4WagoStatus => _Regs.DigitalInput11;
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
private Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput0; // change it to private should be ok
private Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput1;
private Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput2;
private Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput3;
public Boolean FiWarning => !_Regs.DigitalInput5;
public Boolean FiError => !_Regs.DigitalInput7;
public Boolean Harvester1Step =>_Regs.DigitalOutput2;
public Boolean Harvester2Step =>_Regs.DigitalOutput3;
public Boolean Harvester3Step =>_Regs.DigitalOutput4;
public Boolean Harvester4Step =>_Regs.DigitalOutput5;
public Boolean LedGreen { get =>_Regs.DigitalOutput0; set => _Regs.DigitalOutput0 = value;}
public Boolean LedRed { get =>_Regs.DigitalOutput1; set => _Regs.DigitalOutput1 = value;}
public Boolean Do0Pulse { get => _Regs.Do0Pulse; set => _Regs.Do0Pulse = value;}
public Boolean Do1Pulse { get => _Regs.Do1Pulse; set => _Regs.Do1Pulse = value;}
public Boolean Do2Pulse { get => _Regs.Do2Pulse; set => _Regs.Do2Pulse = value;}
public Boolean Do3Pulse { get => _Regs.Do3Pulse; set => _Regs.Do3Pulse = value;}
public Boolean Do4Pulse { get => _Regs.Do4Pulse; set => _Regs.Do4Pulse = value;}
public Boolean Do5Pulse { get => _Regs.Do5Pulse; set => _Regs.Do5Pulse = value;}
public UInt16 PulseOut0LowTime { get => _Regs.PulseOut0LowTime; set => _Regs.PulseOut0LowTime = value;} //in milleseconds
public UInt16 PulseOut1LowTime { get => _Regs.PulseOut1LowTime; set => _Regs.PulseOut1LowTime = value;}
public UInt16 PulseOut2LowTime { get => _Regs.PulseOut2LowTime; set => _Regs.PulseOut2LowTime = value;}
public UInt16 PulseOut3LowTime { get => _Regs.PulseOut3LowTime; set => _Regs.PulseOut3LowTime = value;}
public UInt16 PulseOut4LowTime { get => _Regs.PulseOut4LowTime; set => _Regs.PulseOut4LowTime = value;}
public UInt16 PulseOut5LowTime { get => _Regs.PulseOut5LowTime; set => _Regs.PulseOut5LowTime = value;}
public UInt16 PulseOut0HighTime { get => _Regs.PulseOut0HighTime; set => _Regs.PulseOut0HighTime = value;} // in milleseconds
public UInt16 PulseOut1HighTime { get => _Regs.PulseOut1HighTime; set => _Regs.PulseOut1HighTime = value;}
public UInt16 PulseOut2HighTime { get => _Regs.PulseOut2HighTime; set => _Regs.PulseOut2HighTime = value;}
public UInt16 PulseOut3HighTime { get => _Regs.PulseOut3HighTime; set => _Regs.PulseOut3HighTime = value;}
public UInt16 PulseOut4HighTime { get => _Regs.PulseOut4HighTime; set => _Regs.PulseOut4HighTime = value;}
public UInt16 PulseOut5HighTime { get => _Regs.PulseOut5HighTime; set => _Regs.PulseOut5HighTime = value;}
public UInt16 DigitalOutput0Mode { get => _Regs.DigitalOutput0Mode; set => _Regs.DigitalOutput0Mode = value;} // To test: 0, 1 or 2
public UInt16 DigitalOutput1Mode { get => _Regs.DigitalOutput1Mode; set => _Regs.DigitalOutput1Mode = value;}
public UInt16 DigitalOutput2Mode { get => _Regs.DigitalOutput2Mode; set => _Regs.DigitalOutput2Mode = value;}
public UInt16 DigitalOutput3Mode { get => _Regs.DigitalOutput3Mode; set => _Regs.DigitalOutput3Mode = value;}
public UInt16 DigitalOutput4Mode { get => _Regs.DigitalOutput4Mode; set => _Regs.DigitalOutput4Mode = value;}
public UInt16 DigitalOutput5Mode { get => _Regs.DigitalOutput5Mode; set => _Regs.DigitalOutput5Mode = value;}
public Boolean K2ConnectIslandBusToGridBus { get => _Regs.Relay0; set => _Regs.Relay0 = value;}
public static implicit operator Adam6360DRegisters(RelaysRecordAdam6360D d) => d._Regs;
public static implicit operator RelaysRecordAdam6360D(Adam6360DRegisters d) => new RelaysRecordAdam6360D(d);
}

View File

@ -1,134 +0,0 @@
using InnovEnergy.Lib.Devices.Amax5070;
namespace InnovEnergy.App.SodiStoreMax.SaliMaxRelays;
public class RelaysRecordAmax : IRelaysRecord
{
private readonly Amax5070Registers _Regs;
private RelaysRecordAmax(Amax5070Registers regs) => _Regs = regs;
public Boolean K1GridBusIsConnectedToGrid => _Regs.DigitalInput22;
public Boolean K2IslandBusIsConnectedToGridBus => !_Regs.DigitalInput20;
public Boolean Inverter1WagoStatus => _Regs.DigitalInput0;
public Boolean Inverter2WagoStatus => _Regs.DigitalInput1;
public Boolean Inverter3WagoStatus => _Regs.DigitalInput2;
public Boolean Inverter4WagoStatus => _Regs.DigitalInput3;
public Boolean Dc1WagoStatus => _Regs.DigitalInput6;
public Boolean Dc2WagoStatus => _Regs.DigitalInput7;
public Boolean Dc3WagoStatus => _Regs.DigitalInput10;
public Boolean Dc4WagoStatus => _Regs.DigitalInput11;
public Boolean DcSystemControlWagoStatus => _Regs.DigitalInput9;
public Boolean LedGreen
{
get => _Regs.DigitalOutput0;
set => _Regs.DigitalOutput0 = value;
}
public Boolean LedRed => _Regs.DigitalOutput1;
public Boolean Harvester1Step => _Regs.DigitalOutput2;
public Boolean Harvester2Step => _Regs.DigitalOutput3;
public Boolean Harvester3Step => _Regs.DigitalOutput4;
public Boolean Harvester4Step => _Regs.DigitalOutput5;
public Boolean Do0StartPulse { get; set; }
public Boolean Do1StartPulse { get; set; }
public Boolean Do2StartPulse { get; set; }
public Boolean Do3StartPulse { get; set; }
public Boolean Do4StartPulse { get; set; }
public Boolean Do5StartPulse { get; set; }
public UInt16 DigitalOutput0Mode { get; set; }
public UInt16 DigitalOutput1Mode { get; set; }
public UInt16 DigitalOutput2Mode { get; set; }
public UInt16 DigitalOutput3Mode { get; set; }
public UInt16 DigitalOutput4Mode { get; set; }
public UInt16 DigitalOutput5Mode { get; set; }
public UInt16 PulseOut0LowTime { get; set; }
public UInt16 PulseOut1LowTime { get; set; }
public UInt16 PulseOut2LowTime { get; set; }
public UInt16 PulseOut3LowTime { get; set; }
public UInt16 PulseOut4LowTime { get; set; }
public UInt16 PulseOut5LowTime { get; set; }
public UInt16 PulseOut0HighTime { get; set; }
public UInt16 PulseOut1HighTime { get; set; }
public UInt16 PulseOut2HighTime { get; set; }
public UInt16 PulseOut3HighTime { get; set; }
public UInt16 PulseOut4HighTime { get; set; }
public UInt16 PulseOut5HighTime { get; set; }
public void PerformSolidGreenLed()
{
Console.WriteLine("Solid Green: This is not yet implemented ");
}
public void PerformSlowFlashingGreenLed()
{
Console.WriteLine("Slow Flashing Green: This is not yet implemented ");
}
public void PerformFastFlashingGreenLed()
{
Console.WriteLine("Fast Flashing Green: This is not yet implemented ");
}
public void PerformSolidOrangeLed()
{
Console.WriteLine("Solid Orange: This is not yet implemented ");
}
public void PerformSlowFlashingOrangeLed()
{
Console.WriteLine("Slow Flashing Orange: This is not yet implemented ");
}
public void PerformFastFlashingOrangeLed()
{
Console.WriteLine("Fast Flashing Orange: This is not yet implemented ");
}
public void PerformSolidRedLed()
{
Console.WriteLine("Solid Red: This is not yet implemented ");
}
public void PerformSlowFlashingRedLed()
{
Console.WriteLine("Slow Flashing Red: This is not yet implemented ");
}
public void PerformFastFlashingRedLed()
{
Console.WriteLine("Fast Flashing Red: This is not yet implemented ");
}
public IEnumerable<Boolean> K3InverterIsConnectedToIslandBus
{
get
{
yield return K3Inverter1IsConnectedToIslandBus;
yield return K3Inverter2IsConnectedToIslandBus;
yield return K3Inverter3IsConnectedToIslandBus;
yield return K3Inverter4IsConnectedToIslandBus;
}
}
private Boolean K3Inverter1IsConnectedToIslandBus => !_Regs.DigitalInput16;
private Boolean K3Inverter2IsConnectedToIslandBus => !_Regs.DigitalInput17;
private Boolean K3Inverter3IsConnectedToIslandBus => !_Regs.DigitalInput18;
private Boolean K3Inverter4IsConnectedToIslandBus => !_Regs.DigitalInput19;
public Boolean FiWarning => !_Regs.DigitalInput21;
public Boolean FiError => !_Regs.DigitalInput23;
public Boolean K2ConnectIslandBusToGridBus
{
get => _Regs.Relay23;
set => _Regs.Relay23 = value;
}
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._Regs;
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
}

View File

@ -569,7 +569,8 @@ public static class Controller
private static Boolean State103(StatusRecord s)
{
s.StateMachine.Message = "Panic: ACDCs have unequal grid types or PowerStage";
return s.EnableSafeDefaults();
s.AcDc.Enable();
return false;
}
// private static Boolean State104(StatusRecord s)

View File

@ -8,7 +8,7 @@ namespace InnovEnergy.Lib.Devices.Amax5070
[AddressOffset(-1)]
public class Amax5070Registers
{
[Coil(1)] public Boolean DigitalOutput0 { get; set; }
[Coil(1)] public Boolean DigitalOutput0 { get; private set; }
[Coil(2)] public Boolean DigitalOutput1 { get; private set; }
[Coil(3)] public Boolean DigitalOutput2 { get; private set; }
[Coil(4)] public Boolean DigitalOutput3 { get; private set; }

View File

@ -27,4 +27,161 @@ public class AlarmMessage
LowVoltageAlarmForTotalVoltage,
UnderVoltageProtectionForTotalVoltage
}
// Enum for Alarm Event 3
public enum AlarmEvent3
{
ChargeHighTemperatureAlarm,
ChargeOverTemperatureProtection,
ChargeLowTemperatureAlarm,
ChargeUnderTemperatureProtection,
DischargeHighTemperatureAlarm,
DischargeOverTemperatureProtection,
DischargeLowTemperatureAlarm,
DischargeUnderTemperatureProtection
}
// Enum for Alarm Event 4
public enum AlarmEvent4
{
EnvironmentHighTemperatureAlarm,
EnvironmentOverTemperatureProtection,
EnvironmentLowTemperatureAlarm,
EnvironmentUnderTemperatureProtection,
PowerOverTemperatureProtection,
PowerHighTemperatureAlarm,
CellLowTemperatureHeating,
ReservationBit
}
// Enum for Alarm Event 5
public enum AlarmEvent5
{
ChargeOverCurrentAlarm,
ChargeOverCurrentProtection,
DischargeOverCurrentAlarm,
DischargeOverCurrentProtection,
TransientOverCurrentProtection,
OutputShortCircuitProtection,
TransientOverCurrentLockout,
OutputShortCircuitLockout
}
// Enum for Alarm Event 6
public enum AlarmEvent6
{
ChargeHighVoltageProtection,
IntermittentRechargeWaiting,
ResidualCapacityAlarm,
ResidualCapacityProtection,
CellLowVoltageChargingProhibition,
OutputReversePolarityProtection,
OutputConnectionFault,
InsideBit
}
// Enum for On-Off State
public enum OnOffState
{
DischargeSwitchState,
ChargeSwitchState,
CurrentLimitSwitchState,
HeatingSwitchState,
ReservationBit1,
ReservationBit2,
ReservationBit3,
ReservationBit4
}
// Enum for Equilibrium State 1
public enum EquilibriumState1
{
Cell01Equilibrium,
Cell02Equilibrium,
Cell03Equilibrium,
Cell04Equilibrium,
Cell05Equilibrium,
Cell06Equilibrium,
Cell07Equilibrium,
Cell08Equilibrium
}
// Enum for Equilibrium State 2
public enum EquilibriumState2
{
Cell09Equilibrium,
Cell10Equilibrium,
Cell11Equilibrium,
Cell12Equilibrium,
Cell13Equilibrium,
Cell14Equilibrium,
Cell15Equilibrium,
Cell16Equilibrium
}
// Enum for System State
public enum SystemState
{
Discharge,
Charge,
FloatingCharge,
ReservationBit1,
Standby,
Shutdown,
ReservationBit2,
ReservationBit3
}
// Enum for Disconnection State 1
public enum DisconnectionState1
{
Cell01Disconnection,
Cell02Disconnection,
Cell03Disconnection,
Cell04Disconnection,
Cell05Disconnection,
Cell06Disconnection,
Cell07Disconnection,
Cell08Disconnection
}
// Enum for Disconnection State 2
public enum DisconnectionState2
{
Cell09Disconnection,
Cell10Disconnection,
Cell11Disconnection,
Cell12Disconnection,
Cell13Disconnection,
Cell14Disconnection,
Cell15Disconnection,
Cell16Disconnection
}
// Enum for Alarm Event 7
public enum AlarmEvent7
{
InsideBit1,
InsideBit2,
InsideBit3,
InsideBit4,
AutomaticChargingWaiting,
ManualChargingWaiting,
InsideBit5,
InsideBit6
}
// Enum for Alarm Event 8
public enum AlarmEvent8
{
EepStorageFault,
RtcError,
VoltageCalibrationNotPerformed,
CurrentCalibrationNotPerformed,
ZeroCalibrationNotPerformed,
InsideBit1,
InsideBit2,
InsideBit3
}
}

View File

@ -1,26 +1,23 @@
namespace InnovEnergy.Lib.Devices.BatteryDeligreen;
public class BatteryDeligreenAlarmRecord
{/*
public String FwVersion { get; set; }
public TemperaturesList TemperaturesList { get; set; }
// public Dc_ Dc { get; set; }
public BatteryDeligreenAlarmRecord(Voltage busVoltage, Current busCurrent ,String fwVersion, Percent soc, UInt16 numberOfCycles, Double batteryCapacity, Double ratedCapacity, Voltage totalBatteryVoltage, Percent soh, Double residualCapacity, List<Double> cellVoltage, TemperaturesList temperaturesList)
{
BusVoltage = busVoltage;
BusCurrent = busCurrent;
FwVersion = fwVersion;
TotalBatteryVoltage = totalBatteryVoltage;
ResidualCapacity = residualCapacity;
BatteryCapacity = batteryCapacity;
Soc = soc;
RatedCapacity = ratedCapacity;
NumberOfCycles = numberOfCycles;
Soh = soh;
CellVoltage = cellVoltage;
TemperaturesList = temperaturesList;
Power = busVoltage * busCurrent;
}*/
public List<String> CellAlarmList { get; set; }
public List<String> CellTemperatureAlarm { get; set; }
public String EnviTempAlarm { get; set; }
public String PowerTempAlarm { get; set; }
public String CurrentAlarm { get; set; }
public String TotalVoltageAlarm { get; set; }
public BatteryDeligreenAlarmRecord( List<String> cellAlarmList, List<String> cellTemperatureAlarm, String enviTempAlarm1, String powerTempAlarm1, String currentAlarm1, String totalVoltageAlarm1)
{
CellAlarmList = cellAlarmList;
CellTemperatureAlarm = cellTemperatureAlarm;
EnviTempAlarm = enviTempAlarm1;
PowerTempAlarm = powerTempAlarm1;
CurrentAlarm = currentAlarm1;
TotalVoltageAlarm = totalVoltageAlarm1;
}
}

View File

@ -4,14 +4,10 @@ using static InnovEnergy.Lib.Devices.BatteryDeligreen.Temperatures;
namespace InnovEnergy.Lib.Devices.BatteryDeligreen;
using InnovEnergy.Lib.Units;
using Strings = IReadOnlyList<String>;
public class BatteryDeligreenDataRecord
{
// public Strings Warnings => ParseWarnings().OrderBy(w => w).ToList();
// public Strings Alarms => ParseAlarms() .OrderBy(w => w).ToList();
public String FwVersion { get; set; }
public Voltage BusVoltage { get; set; }
public Current BusCurrent { get; set; }
@ -25,7 +21,6 @@ public class BatteryDeligreenDataRecord
public Percent Soh { get; set; }
public List<Double> CellVoltage { get; set; }
public TemperaturesList TemperaturesList { get; set; }
// public Dc_ Dc { get; set; }
public BatteryDeligreenDataRecord(Voltage busVoltage, Current busCurrent ,String fwVersion, Percent soc, UInt16 numberOfCycles, Double batteryCapacity, Double ratedCapacity, Voltage totalBatteryVoltage, Percent soh, Double residualCapacity, List<Double> cellVoltage, TemperaturesList temperaturesList)
{
@ -43,12 +38,4 @@ public class BatteryDeligreenDataRecord
TemperaturesList = temperaturesList;
Power = busVoltage * busCurrent;
}
// public struct Dc_
// {
// public Voltage Voltage => BusVoltage;
// public Current Current => BusCurrent;
// public ActivePower Power => BusVoltage * BusCurrent;
// }
}

View File

@ -144,7 +144,7 @@ public class BatteryDeligreenDevice
// Read telemetry data from the connected device
private async Task<BatteryDeligreenDataRecord?> ReadTelemetryData(UInt16 batteryId)
{
String frameToSend = batteryId switch
var frameToSend = batteryId switch
{
0 => "7E3230303034363432453030323030464433370D",
1 => "7E3230303134363432453030323031464433350D",
@ -199,7 +199,7 @@ public class BatteryDeligreenDevice
return Task.FromResult(responseBuffer.ToArray());
}
private async Task<BatteryDeligreenAlarmRecord> ReadTelecomandData(UInt16 batteryId)
private async Task<BatteryDeligreenAlarmRecord?> ReadTelecomandData(UInt16 batteryId)
{
var frameToSend = batteryId switch
{
@ -219,15 +219,14 @@ public class BatteryDeligreenDevice
{
// Write the frame to the channel (send it to the device)
Write(frameToSend);
// await Task.Delay(delayFrame2);
// Read the response from the channel (assuming max response size)
var responseBytes = await ReadFullResponse(116, 64); // Assuming Read can be executed asynchronously
// Convert the byte array to a hexadecimal string
var responseHex = BytesToHexString(responseBytes);
var response = new TelecommandFrameParser().ParsingTelecommandFrame(responseHex);
return new BatteryDeligreenAlarmRecord();
return new TelecommandFrameParser().ParsingTelecommandFrame(responseHex);
}
catch (Exception ex)
{
@ -242,7 +241,7 @@ public class BatteryDeligreenDevice
var alarmRecord = ReadTelecomandData(SlaveId).Result;
await Task.Delay(5); // looks like this is need. A time delay needed between each frame to send to each battery
return dataRecord != null ? new BatteryDeligreenRecord(dataRecord, alarmRecord) : null;
return (dataRecord != null && alarmRecord != null ) ? new BatteryDeligreenRecord(dataRecord, alarmRecord) : null; // to check how this work if one of the record is null
}
private static String ConstructFrameToSend(UInt16 batteryId, String functionCode)
@ -252,8 +251,7 @@ public class BatteryDeligreenDevice
var batteryIdHex = string.Concat(batteryIdAscii.Select(c => ((Int32)c).ToString("X2")));
Console.WriteLine("Battery ID " + batteryIdHex);
var frameToSend =
FrameStart + Version + batteryIdHex + DeviceCode + functionCode +
var frameToSend = FrameStart + Version + batteryIdHex + DeviceCode + functionCode +
"453030323030464433370D"; // Example custom frame with dynamic batteryId
Console.WriteLine(frameToSend);
return frameToSend;

View File

@ -1,132 +0,0 @@
import serial
def parse_start_code(frame):
soi = frame[0:2]
if soi == "~":
return "ok!"
else:
raise ValueError(f"Invalid start identifier! ({soi})")
def parse_version_code(frame):
ver = frame[2:6]
return f"Protocol Version V{ver[0]}.{ver[1]}"
def parse_address_code(frame):
adr = frame[6:10]
if 0 <= int(adr) <= 15:
return adr
else:
raise ValueError(f"Invalid address: {adr} (out of range 0-15)")
def parse_device_code(frame):
cid1 = frame[10:14]
return bms.CID1_DEVICE_CODES.get(cid1, "Unknown!")
def parse_function_code(frame):
cid2 = frame[14:18]
if cid2 in bms.CID2_COMMAND_CODES:
return f"Command -> {bms.CID2_COMMAND_CODES.get(cid2)}"
elif cid2 in bms.CID2_RETURN_CODES:
return f"Return -> {bms.CID2_RETURN_CODES.get(cid2)}"
else:
return f"Unknown CID2: {cid2}"
def parse_lchksum(length_code):
# implements chapter 3.2.2 of the Protocol Specification
lchksum = int(length_code[0], 16)
# Compute lchksum
d11d10d09d08 = int(length_code[1])
d07d06d05d04 = int(length_code[2])
d03d0ld01d00 = int(length_code[3])
sum = d11d10d09d08 + d07d06d05d04 + d03d0ld01d00
remainder = sum % 16
inverted = ~remainder & 0xF
computed_lchksum = (inverted + 1) & 0xF
if computed_lchksum == lchksum:
return "ok!"
else:
raise ValueError(f"Invalid LCHKSUM: {lchksum} (computed: {computed_lchksum})")
def parse_lenid(length_code):
# implements chapter 3.2.1 of the Protocol Specification
d11d10d09d08 = int(length_code[1])
d07d06d05d04 = int(length_code[2])
d03d0ld01d00 = int(length_code[3])
lenid = d11d10d09d08 << 8 | d07d06d05d04 << 4 | d03d0ld01d00
return lenid>>1
def parse_length_code(frame):
# implements chapter 3.2 of the Protocol Specification
length_code = frame[18:26]
lchksum = parse_lchksum(length_code)
lenid = parse_lenid(length_code)
return { "LCHKSUM": lchksum, "LENID": lenid }
def parse_info(frame):
cid2 = frame[14:18]
lenid = parse_lenid(frame[18:26])
info = frame[26:26+lenid*2]
if cid2 == '00' and lenid == 49:
return parse_telecommand_return(info)
elif cid2 == '00' and lenid == 75:
return parse_telemetry_return(info)
else:
return info
def parse_telecommand_return(info_raw, info={}, index=0):
info["DATA FLAG"] = info_raw[index:index+4]
index += 4
info["COMMAND GROUP"] = info_raw[index:index+4]
index += 4
def parse_modbus_ascii_frame(frame, parsed_data = {}):
frame = bytes.fromhex(frame).decode('ascii')
parsed_data["SOI"] = parse_start_code(frame)
parsed_data["VER"] = parse_version_code(frame)
parsed_data["ADR"] = parse_address_code(frame)
parsed_data["CID1"] = parse_device_code(frame)
parsed_data["CID2"] = parse_function_code(frame)
parsed_data["LENGTH"] = parse_length_code(frame)
parsed_data["INFO"] = parse_info(frame)
parsed_data["CHKSUM"] = parse_checksum(frame)
parsed_data["EOI"] = parse_end_code(frame)
return parsed_data
def send_command():
# Define the serial port and baud rate
port = 'COM9' # Replace with your actual port
baudrate = 19200 # Replace with the correct baud rate for your BMS
# Create the serial connection
try:
with serial.Serial(port, baudrate, timeout=1) as ser:
# Convert the hex string to bytes
command = bytes.fromhex("7E3230303034363434453030323030464433350D")
# Send the command
ser.write(command)
print("Command sent successfully.")
# Wait for and read the response
response = ser.read(200) # Adjust the number of bytes to read as needed
if response:
hex_response = response.hex()
print("Response received:", hex_response)
# Process the response to check details
check_starting_byte_and_extract_details(hex_response)
else:
print("No response received.")
except serial.SerialException as e:
print(f"Error opening serial port: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
send_command()

View File

@ -1,480 +0,0 @@
import serial
import csv
TELECOMMAND_FILE_PATH = "Telecommand_Return_Record.csv"
# Table 3
CID1_DEVICE_CODES = {
"46": "Lithium iron phosphate battery BMS",
}
# Table 4
CID2_COMMAND_CODES = {
"42": "Acquisition of telemetering information",
"44": "Acquisition of telecommand information",
"45": "Telecontrol command",
"47": "Acquisition of teleregulation information",
"49": "Setting of teleregulation information",
"4F": "Acquisition of the communication protocol version number",
"51": "Acquisition of device vendor information",
"4B": "Acquisition of historical data",
"4D": "Acquisition time",
"4E": "Synchronization time",
"A0": "Production calibration",
"A1": "Production setting",
"A2": "Regular recording"
}
# Table 5
CID2_RETURN_CODES = {
"00": "Normal",
"01": "VER error",
"02": "CHKSUM error",
"03": "LCHKSUM error",
"04": "CID2 invalid",
"05": "Command format error",
"06": "Data invalid (parameter setting)",
"07": "No data (history)",
"E1": "CID1 invalid",
"E2": "Command execution failure",
"E3": "Device fault",
"E4": "Invalid permissions"
}
# Table 12
BYTE_ALARM_CODES = {
"00": "Normal, no alarm",
"01": "Alarm that analog quantity reaches the lower limit",
"02": "Alarm that analog quantity reaches the upper limit",
"F0": "Other alarms"
}
# Table 13
BIT_ALARM_CODES = {
"Alarm event 1": (
"Voltage sensor fault",
"Temperature sensor fault",
"Current sensor fault",
"Key switch fault",
"Cell voltage dropout fault",
"Charge switch fault",
"Discharge switch fault",
"Current limit switch fault"
),
"Alarm event 2": (
"Monomer high voltage alarm",
"Monomer overvoltage protection",
"Monomer low voltage alarm",
"Monomer under voltage protection",
"High voltage alarm for total voltage",
"Overvoltage protection for total voltage",
"Low voltage alarm for total voltage",
"Under voltage protection for total voltage"
),
"Alarm event 3": (
"Charge high temperature alarm",
"Charge over temperature protection",
"Charge low temperature alarm",
"Charge under temperature protection",
"Discharge high temperature alarm",
"Discharge over temperature protection",
"Discharge low temperature alarm",
"Discharge under temperature protection"
),
"Alarm event 4": (
"Environment high temperature alarm",
"Environment over temperature protection",
"Environment low temperature alarm",
"Environment under temperature protection",
"Power over temperature protection",
"Power high temperature alarm",
"Cell low temperature heating",
"Reservation bit"
),
"Alarm event 5": (
"Charge over current alarm",
"Charge over current protection",
"Discharge over current alarm",
"Discharge over current protection",
"Transient over current protection",
"Output short circuit protection",
"Transient over current lockout",
"Output short circuit lockout"
),
"Alarm event 6": (
"Charge high voltage protection",
"Intermittent recharge waiting",
"Residual capacity alarm",
"Residual capacity protection",
"Cell low voltage charging prohibition",
"Output reverse polarity protection",
"Output connection fault",
"Inside bit"
),
"On-off state": (
"Discharge switch state",
"Charge switch state",
"Current limit switch state",
"Heating switch state",
"Reservation bit",
"Reservation bit",
"Reservation bit",
"Reservation bit"
),
"Equilibrium state 1": (
"Cell 01 equilibrium",
"Cell 02 equilibrium",
"Cell 03 equilibrium",
"Cell 04 equilibrium",
"Cell 05 equilibrium",
"Cell 06 equilibrium",
"Cell 07 equilibrium",
"Cell 08 equilibrium"
),
"Equilibrium state 2": (
"Cell 09 equilibrium",
"Cell 10 equilibrium",
"Cell 11 equilibrium",
"Cell 12 equilibrium",
"Cell 13 equilibrium",
"Cell 14 equilibrium",
"Cell 15 equilibrium",
"Cell 16 equilibrium"
),
"System state": (
"Discharge",
"Charge",
"Floating charge",
"Reservation bit",
"Standby",
"Shutdown",
"Reservation bit",
"Reservation bit"
),
"Disconnection state 1": (
"Cell 01 disconnection",
"Cell 02 disconnection",
"Cell 03 disconnection",
"Cell 04 disconnection",
"Cell 05 disconnection",
"Cell 06 disconnection",
"Cell 07 disconnection",
"Cell 08 disconnection"
),
"Disconnection state 2": (
"Cell 09 disconnection",
"Cell 10 disconnection",
"Cell 11 disconnection",
"Cell 12 disconnection",
"Cell 13 disconnection",
"Cell 14 disconnection",
"Cell 15 disconnection",
"Cell 16 disconnection"
),
"Alarm event 7": (
"Inside bit",
"Inside bit",
"Inside bit",
"Inside bit",
"Automatic charging waiting",
"Manual charging waiting",
"Inside bit",
"Inside bit"
),
"Alarm event 3": (
"EEP storage fault",
"RTC error",
"Voltage calibration not performed",
"Current calibration not performed",
"Zero calibration not performed",
"Inside bit",
"Inside bit",
"Inside bit"
),
}
def parse_start_code(frame):
soi = frame[0:1]
if soi == "~":
return "ok!"
else:
raise ValueError(f"Invalid start identifier! ({soi})")
def parse_version_code(frame):
ver = frame[1:3]
return f"Protocol Version V{ver[0]}.{ver[1]}"
def parse_address_code(frame):
adr = frame[3:5]
if 0 <= int(adr) <= 15:
return adr
else:
raise ValueError(f"Invalid address: {adr} (out of range 0-15)")
def parse_device_code(frame):
cid1 = frame[5:7]
return CID1_DEVICE_CODES.get(cid1, "Unknown!")
def parse_function_code(frame):
cid2 = frame[7:9]
if cid2 in CID2_COMMAND_CODES:
return f"Command -> {CID2_COMMAND_CODES.get(cid2)}"
elif cid2 in CID2_RETURN_CODES:
return f"Return -> {CID2_RETURN_CODES.get(cid2)}"
else:
return f"Unknown CID2: {cid2}"
def parse_lchksum(length_code):
# implements chapter 3.2.2 of the Protocol Specification
lchksum = int(length_code[0], 16)
# Compute lchksum
d11d10d09d08 = int(length_code[1])
d07d06d05d04 = int(length_code[2])
d03d0ld01d00 = int(length_code[3])
sum = d11d10d09d08 + d07d06d05d04 + d03d0ld01d00
remainder = sum % 16
inverted = ~remainder & 0xF
computed_lchksum = (inverted + 1) & 0xF
if computed_lchksum == lchksum:
return "ok!"
else:
raise ValueError(f"Invalid LCHKSUM: {lchksum} (computed: {computed_lchksum})")
def parse_lenid(length_code):
# implements chapter 3.2.1 of the Protocol Specification
d11d10d09d08 = int(length_code[1])
d07d06d05d04 = int(length_code[2])
d03d0ld01d00 = int(length_code[3])
lenid = d11d10d09d08 << 8 | d07d06d05d04 << 4 | d03d0ld01d00
return lenid >> 1
def parse_length_code(frame):
# implements chapter 3.2 of the Protocol Specification
length_code = frame[9:13]
lchksum = parse_lchksum(length_code)
lenid = parse_lenid(length_code)
return { "LCHKSUM": lchksum, "LENID": lenid }
def parse_info(frame):
cid2 = frame[7:9]
lenid = parse_lenid(frame[9:13])
info = frame[13:13+lenid*2]
if cid2 == '00' and lenid == 49:
return parse_telecommand_return(info)
elif cid2 == '00' and lenid == 75:
return parse_telemetry_return(info)
else:
return info
def parse_telecommand_return(info_raw, info={}, index=0):
info["DATA FLAG"] = info_raw[index:index+2]
index += 2
info["COMMAND GROUP"] = info_raw[index:index+2]
index += 2
num_of_cells = int(info_raw[index:index+2], 16)
info["Number of cells"] = num_of_cells
index += 2
for cell in range(info["Number of cells"]):
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info[f"Cell {cell +1} alarm"] = alarm
index += 2
num_of_temperatures = int(info_raw[index:index+2], 16)
info["Number of temperatures"] = num_of_temperatures
index += 2
for sensor in range(4):
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info[f"Cell temperature alarm {sensor}"] = alarm
index += 2
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info["Environment temperature alarm"] = alarm
index += 2
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info["Power temperature alarm 1"] = alarm
index += 2
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info["Charge/discharge current alarm"] = alarm
index += 2
alarm = BYTE_ALARM_CODES.get(info_raw[index:index+2])
info["Total battery voltage alarm"] = alarm
index += 2
num_custom = int(info_raw[index:index+2], 16)
info["Number of custom alarms"] = num_custom
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 1"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 2"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 3"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 4"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 5"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 6"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["On-off state"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Equilibrium state 1"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Equilibrium state 2"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["System state"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Disconnection state 1"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Disconnection state 2"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 7"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Alarm event 8"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 1"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 2"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 3"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 4"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 5"] = alarm
index += 2
alarm = info_raw[index:index+2]
info["Reservation extention 6"] = alarm
index += 2
save_dict_to_csv(TELECOMMAND_FILE_PATH, info)
return f"Telecommand Return Data saved in ./{TELECOMMAND_FILE_PATH}"
def save_dict_to_csv(file_path, data):
with open(file_path, mode='a+', newline='') as csvfile:
csvfile.seek(0)
has_header = csvfile.read(1) != ""
csvfile.seek(0, 2)
writer = csv.DictWriter(csvfile, fieldnames=data.keys())
if not has_header:
writer.writeheader()
writer.writerow(data)
def parse_checksum(frame):
"""implements section 3.3 of the Protocol Specification"""
chksum = int(frame[-6:-1], 16)
data = frame[1:-5]
# Compute chksum
ascii_sum = sum(ord(char) for char in data)
remainder = ascii_sum % 65536
inverted = ~remainder & 0xFFFF
computed_chksum = (inverted + 1) & 0xFFFF
# Compare with CHKSUM in frame
if computed_chksum == chksum:
return "ok!"
else:
raise ValueError(f"Invalid CHKSUM: {chksum} (computed: {computed_chksum})")
def parse_end_code(frame):
eoi = frame[-1]
if eoi == "\r":
return "ok!"
else:
raise ValueError(f"Invalid end identifier! ({eoi})")
def parse_modbus_ascii_frame(frame, parsed_data = {}):
frame = bytes.fromhex(frame).decode('ascii')
parsed_data["SOI"] = parse_start_code(frame)
parsed_data["VER"] = parse_version_code(frame)
parsed_data["ADR"] = parse_address_code(frame)
parsed_data["CID1"] = parse_device_code(frame)
parsed_data["CID2"] = parse_function_code(frame)
parsed_data["LENGTH"] = parse_length_code(frame)
parsed_data["INFO"] = parse_info(frame)
parsed_data["CHKSUM"] = parse_checksum(frame)
parsed_data["EOI"] = parse_end_code(frame)
return parsed_data
def send_command():
# Define the serial port and baud rate
port = 'COM9' # Replace with your actual port
baudrate = 19200 # Replace with the correct baud rate for your BMS
# Create the serial connection
try:
with serial.Serial(port, baudrate, timeout=1) as ser:
# Convert the hex string to bytes
command = bytes.fromhex("7E3230303034363434453030323030464433350D")
# Send the command
ser.write(command)
print("Command sent successfully.")
# Wait for and read the response
response = ser.read(200) # Adjust the number of bytes to read as needed
if response:
hex_response = response.hex()
print("Response received:", hex_response)
# Process the response to check details
parsed_result = parse_modbus_ascii_frame(hex_response)
for key, value in parsed_result.items():
print(f"{key}: {value}")
else:
print("No response received.")
except serial.SerialException as e:
print(f"Error opening serial port: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
send_command()

View File

@ -10,7 +10,15 @@ public class TelecommandFrameParser
private static Int32 _currentIndex;
private const Int32 FrameLength = 232;
public Boolean ParsingTelecommandFrame(String response)
private static readonly Dictionary<String, String> ByteAlarmCodes = new()
{
{ "00", "Normal, no alarm" },
{ "01", "Alarm that analog quantity reaches the lower limit" },
{ "02", "Alarm that analog quantity reaches the upper limit" },
{ "F0", "Other alarms" }
};
public BatteryDeligreenAlarmRecord? ParsingTelecommandFrame(String response)
{
_currentIndex = 0; // Reset currentIndex to the start
@ -19,7 +27,7 @@ public class TelecommandFrameParser
Console.WriteLine("Response is too short to contain valid data.");
Console.WriteLine(" Fixed Length" + FrameLength);
Console.WriteLine(" response Length" + response.Length);
return false;
return null;
}
// Check starting byte
@ -31,7 +39,7 @@ public class TelecommandFrameParser
else
{
Console.WriteLine($"Incorrect starting byte: {startingByte}");
return false;
return null;
}
_currentIndex += 2;
@ -46,35 +54,65 @@ public class TelecommandFrameParser
catch (Exception)
{
Console.WriteLine($"Failed to decode firmware version from bytes: {versionBytes}");
return false;
return null;
}
_currentIndex += 4;
// Extract and parse other fields
ParseAndPrintHexField(response, "Device Address", 4);
ParseAndPrintHexField(response, "Device Code (CID1)", 4);
ParseAndPrintHexField(response, "Function Code", 4);
ParseAndPrintHexField(response, "Length Code", 8);
ParseAndPrintHexField(response, "Data Flag", 4);
ParseAndPrintHexField(response, "Command Group", 4);
ParseAndPrintHexField(response, "Number of Cells", 4);
ExtractCellAlarm(response);
return true;
ParseAndPrintHexField(response, "Device Address", 4); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Device Code (CID1)", 4); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Function Code", 4); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Length Code", 8); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Data Flag", 4); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Command Group", 4); // this is not added to the Alarm record
ParseAndPrintHexField(response, "Number of Cells", 4); // this is not added to the Alarm record
// Parse Cell Temperature Alarm fields
var cellAlarmList = ExtractCellAlarm(response);
// Parse Number of Temperature Alarm
ParseAndPrintHexField(response, "Number of Temperature ", 4); // this is not added to the Alarm record
// Parse Cell Temperature Alarm fields
var cellTemperatureAlarm = ExtractCellTempAlarm(response);
var enviTempAlarm = ParseAndPrintField(response, "Environment Temperature Alarm" );
var powerTempAlarm = ParseAndPrintField(response, "Power Temperature Alarm" );
var currentAlarm = ParseAndPrintField(response, "Charge/Discharge Current Alarm" );
var totalVoltageAlarm = ParseAndPrintField(response, "Total Battery Voltage Alarm" );
var batteryAlarmRecord = new BatteryDeligreenAlarmRecord(cellAlarmList, cellTemperatureAlarm, enviTempAlarm, powerTempAlarm,currentAlarm, totalVoltageAlarm);
return batteryAlarmRecord;
}
private static List<String> ExtractCellTempAlarm(String response)
{
var cellTempAlarmList = new List<String>();
var tempCellAlarm = response.Substring(_currentIndex, 4);
for (var i = 0; i < 4; i++)
{
try
{
var tempAscii = HexToAscii(tempCellAlarm);
var alarmMessage = ByteAlarmCodes.ContainsKey(tempAscii) ? ByteAlarmCodes[tempAscii] : "Unknown alarm code";
cellTempAlarmList.Add(alarmMessage);
}
catch (Exception)
{
Console.WriteLine($"Failed to decode Cell Temp Alarm {i + 1} from bytes: {tempCellAlarm}");
}
_currentIndex += 4;
}
return cellTempAlarmList;
}
private static void ExtractCellAlarm(String response)
private static List<String> ExtractCellAlarm(String response)
{
Dictionary<string, string> byteAlarmCodes = new Dictionary<string, string>
{
{ "00", "Normal, no alarm" },
{ "01", "Alarm that analog quantity reaches the lower limit" },
{ "02", "Alarm that analog quantity reaches the upper limit" },
{ "F0", "Other alarms" }
};
var cellAlarmList = new List<String>();
// Process Alarms for all 16 cells
for (var i = 0; i < 16; i++)
@ -83,21 +121,21 @@ public class TelecommandFrameParser
try
{
var alarmAscii = HexToAscii(cellAlarm);
var cellVoltageDecimal = HexToDecimal(alarmAscii);
string alarmMessage = byteAlarmCodes.ContainsKey(alarmAscii) ? byteAlarmCodes[alarmAscii] : "Unknown alarm code";
var alarmMessage = ByteAlarmCodes.ContainsKey(alarmAscii) ? ByteAlarmCodes[alarmAscii] : "Unknown alarm code";
cellAlarmList.Add(alarmMessage);
// Console.WriteLine($"Cell {i + 1}: Alarm Code {cellAlarm}, Status: {alarmMessage}");
}
catch (Exception)
{
Console.WriteLine($"Failed to decode Voltage of Cell {i + 1} from bytes: {cellAlarm}");
Console.WriteLine($"Failed to decode Cell Alarm {i + 1} from bytes: {cellAlarm}");
}
_currentIndex += 4;
}
return cellAlarmList;
}
private static void ParseAndPrintHexField(String response, String fieldName, int length)
private static void ParseAndPrintHexField(String response, String fieldName, Int32 length)
{
var hexBytes = response.Substring(_currentIndex, length);
try
@ -112,20 +150,24 @@ public class TelecommandFrameParser
}
_currentIndex += length;
}
private static void ParseAndPrintField(String response, String fieldName, Int32 length, Func<Double, Double> conversion, String unit)
private static String ParseAndPrintField(String response, String fieldName)
{
var fieldBytes = response.Substring(_currentIndex, length);
var fieldBytes = response.Substring(_currentIndex, 4);
var alarmMessage = "Failed to read alarm";
try
{
var fieldAscii = HexToAscii(fieldBytes);
var fieldDecimal = conversion(HexToDecimal(fieldAscii));
Console.WriteLine($"{fieldName}: {fieldBytes} (Hex), ASCII: {fieldAscii}, {fieldName}: {fieldDecimal:F3} {unit}");
var tempAscii = HexToAscii(fieldBytes);
alarmMessage = ByteAlarmCodes.ContainsKey(tempAscii) ? ByteAlarmCodes[tempAscii] : "Unknown alarm code";
}
catch (Exception)
{
Console.WriteLine($"Failed to decode {fieldName} from bytes: {fieldBytes}");
Console.WriteLine($"Failed to decode : {fieldName}" + " Alarm");
}
_currentIndex += length;
_currentIndex += 4;
return alarmMessage;
}
private static String HexToAscii(String hex)

View File

@ -52,18 +52,18 @@ public class TelemetryFrameParser
_currentIndex += 4;
// Extract and parse other fields
ParseAndPrintHexField(response, "Device Address", 4);
ParseAndPrintHexField(response, "Device Code (CID1)", 4);
ParseAndPrintHexField(response, "Function Code", 4);
ParseAndPrintHexField(response, "Length Code", 8);
ParseAndPrintHexField(response, "Data Flag", 4);
ParseAndPrintHexField(response, "Command Group", 4);
ParseAndPrintHexField(response, "Number of Cells", 4);
ParseAndPrintHexField(response, "Device Address", 4); // this is not added to the Data record
ParseAndPrintHexField(response, "Device Code (CID1)", 4); // this is not added to the Data record
ParseAndPrintHexField(response, "Function Code", 4); // this is not added to the Data record
ParseAndPrintHexField(response, "Length Code", 8); // this is not added to the Data record
ParseAndPrintHexField(response, "Data Flag", 4); // this is not added to the Data record
ParseAndPrintHexField(response, "Command Group", 4); // this is not added to the Data record
ParseAndPrintHexField(response, "Number of Cells", 4); // this is not added to the Data record
var cellVoltages = ExtractCellVoltage(response);
// Parse other fields
ParseAndPrintHexField(response, "Number of Temperature Sensors", 4);
ParseAndPrintHexField(response, "Number of Temperature Sensors", 4); // this is not added to the Data record
var cellTemperature = new List<Double>();
// Parse cell temperatures