Merge branch 'main' into integrated_AI
This commit is contained in:
commit
9a723c0a6f
|
|
@ -55,7 +55,7 @@ internal static class Program
|
||||||
Console.WriteLine("Time used for reading all batteries:" + (stopTime - startTime));
|
Console.WriteLine("Time used for reading all batteries:" + (stopTime - startTime));
|
||||||
|
|
||||||
Console.WriteLine("Average SOC " + batteriesRecord?.Soc);
|
Console.WriteLine("Average SOC " + batteriesRecord?.Soc);
|
||||||
/* Console.WriteLine("Cell Alarm 1 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellAlarmList[0]);
|
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 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 1 : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.CellTemperatureAlarm[0]);
|
||||||
|
|
@ -68,7 +68,7 @@ internal static class Program
|
||||||
Console.WriteLine("Battery 2 Current Alarm : " + batteriesRecord?.Devices[1].BatteryDeligreenAlarmRecord.CurrentAlarm);
|
Console.WriteLine("Battery 2 Current Alarm : " + batteriesRecord?.Devices[1].BatteryDeligreenAlarmRecord.CurrentAlarm);
|
||||||
|
|
||||||
Console.WriteLine("TotalVoltage Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.TotalVoltageAlarm);
|
Console.WriteLine("TotalVoltage Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.TotalVoltageAlarm);
|
||||||
Console.WriteLine("PowerTemp Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.PowerTempAlarm);*/
|
Console.WriteLine("PowerTemp Alarm : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.PowerTempAlarm);
|
||||||
|
|
||||||
Console.WriteLine("CellVoltageDropoutFault : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.AlarmEvent1.CellVoltageDropoutFault);
|
Console.WriteLine("CellVoltageDropoutFault : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.AlarmEvent1.CellVoltageDropoutFault);
|
||||||
Console.WriteLine("HighVoltageAlarmForTotalVoltage : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.AlarmEvent2.HighVoltageAlarmForTotalVoltage);
|
Console.WriteLine("HighVoltageAlarmForTotalVoltage : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.AlarmEvent2.HighVoltageAlarmForTotalVoltage);
|
||||||
|
|
@ -85,6 +85,18 @@ internal static class Program
|
||||||
Console.WriteLine("ManualChargingWaiting : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.OnOffState.ChargeSwitchState);
|
Console.WriteLine("ManualChargingWaiting : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.OnOffState.ChargeSwitchState);
|
||||||
Console.WriteLine("CurrentCalibrationNotPerformed : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.SystemState.Charge);
|
Console.WriteLine("CurrentCalibrationNotPerformed : " + batteriesRecord?.Devices[0].BatteryDeligreenAlarmRecord.SystemState.Charge);
|
||||||
|
|
||||||
|
Console.WriteLine("Power : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.Power);
|
||||||
|
Console.WriteLine("BusCurrent : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.BusCurrent);
|
||||||
|
Console.WriteLine("BusVoltage : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.BusVoltage);
|
||||||
|
Console.WriteLine("BatteryCapacity : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.BatteryCapacity);
|
||||||
|
Console.WriteLine("NumberOfCycles : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.NumberOfCycles);
|
||||||
|
Console.WriteLine("FwVersion : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.FwVersion);
|
||||||
|
Console.WriteLine("Soc : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.Soc);
|
||||||
|
Console.WriteLine("Soh : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.Soh);
|
||||||
|
Console.WriteLine("RatedCapacity : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.RatedCapacity);
|
||||||
|
Console.WriteLine("ResidualCapacity : " + batteriesRecord?.Devices[0].BatteryDeligreenDataRecord.ResidualCapacity);
|
||||||
|
|
||||||
|
|
||||||
// Wait for 2 seconds before the next reading
|
// Wait for 2 seconds before the next reading
|
||||||
await Task.Delay(2000); // Delay in milliseconds (2000ms = 2 seconds)
|
await Task.Delay(2000); // Delay in milliseconds (2000ms = 2 seconds)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,6 @@ public class Configuration
|
||||||
public Double MaximumChargingCurrent { get; set; }
|
public Double MaximumChargingCurrent { get; set; }
|
||||||
public EssMode OperatingPriority { get; set; }
|
public EssMode OperatingPriority { get; set; }
|
||||||
public Int16 BatteriesCount { get; set; }
|
public Int16 BatteriesCount { get; set; }
|
||||||
|
public Boolean ControlPermission { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,8 @@ public static class MiddlewareAgent
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Received a configuration message: " +
|
Console.WriteLine($"Received a configuration message: " +
|
||||||
"MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount + config.MaximumChargingCurrent + config.MaximumDischargingCurrent);
|
"MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount
|
||||||
|
+ "MaximumChargingCurrent is " + config.MaximumChargingCurrent + "MaximumDischargingCurrent " + config.MaximumDischargingCurrent + " Control permission is" + config.ControlPermission );
|
||||||
|
|
||||||
// Send the reply to the sender's endpoint
|
// Send the reply to the sender's endpoint
|
||||||
_udpListener.Send(replyData, replyData.Length, serverEndpoint);
|
_udpListener.Send(replyData, replyData.Length, serverEndpoint);
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,4 @@ public static class RabbitMqManager
|
||||||
|
|
||||||
Console.WriteLine($"Producer sent message: {message}");
|
Console.WriteLine($"Producer sent message: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -32,13 +32,13 @@ namespace InnovEnergy.App.GrowattCommunication;
|
||||||
|
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(5);
|
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(6);
|
||||||
private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file
|
private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file
|
||||||
private static UInt16 _fileCounter = 0;
|
private static UInt16 _fileCounter = 0;
|
||||||
//
|
//
|
||||||
private static Channel _growattChannel;
|
private static Channel _growattChannel;
|
||||||
|
|
||||||
private const String SwVersionNumber =" V1.00.310725 beta";
|
private static readonly String SwVersionNumber = " V1.00." + DateTime.Today;
|
||||||
private const String VpnServerIp = "10.2.0.11";
|
private const String VpnServerIp = "10.2.0.11";
|
||||||
private static Boolean _subscribedToQueue = false;
|
private static Boolean _subscribedToQueue = false;
|
||||||
private static Boolean _subscribeToQueueForTheFirstTime = false;
|
private static Boolean _subscribeToQueueForTheFirstTime = false;
|
||||||
|
|
@ -142,14 +142,14 @@ public static class Program
|
||||||
statusrecord.InverterRecord.EnableEmsCommunicationFailureTime.WriteLine(" = EnableEmsCommunicationFailureTime");
|
statusrecord.InverterRecord.EnableEmsCommunicationFailureTime.WriteLine(" = EnableEmsCommunicationFailureTime");
|
||||||
|
|
||||||
statusrecord.InverterRecord.EnableCommand.WriteLine(" = EnableCommand");
|
statusrecord.InverterRecord.EnableCommand.WriteLine(" = EnableCommand");
|
||||||
statusrecord.InverterRecord.ControlPermession.WriteLine(" = ControlPermession");
|
statusrecord.InverterRecord.ControlPermission.WriteLine(" = ControlPermession");
|
||||||
statusrecord.InverterRecord.GridPower.WriteLine(" MeterPower");
|
statusrecord.InverterRecord.GridPower.WriteLine(" MeterPower");
|
||||||
statusrecord.InverterRecord.ConsumptionPower.WriteLine(" ConsumptionPower");
|
statusrecord.InverterRecord.ConsumptionPower.WriteLine(" ConsumptionPower");
|
||||||
statusrecord.InverterRecord.ExportedPowerToGridMeter.WriteLine(" ExportedPowerToGrid");
|
statusrecord.InverterRecord.ExportedPowerToGridMeter.WriteLine(" ExportedPowerToGrid");
|
||||||
statusrecord.InverterRecord.ImportedPowerFromGrid.WriteLine(" ImportedPowerFromGrid");
|
statusrecord.InverterRecord.ImportedPowerFromGrid.WriteLine(" ImportedPowerFromGrid");
|
||||||
statusrecord.InverterRecord.InverterActivePower.WriteLine(" InverterActivePower");
|
statusrecord.InverterRecord.InverterActivePower.WriteLine(" InverterActivePower");
|
||||||
statusrecord.InverterRecord.MaxSoc.WriteLine(" BatteryChargeCutoffVoltage "); //30409 we set power here
|
statusrecord.InverterRecord.MaxSoc.WriteLine(" MaxSoc "); //30409 we set power here
|
||||||
statusrecord.InverterRecord.MinSoc.WriteLine(" BatteryDishargeCutoffVoltage "); //30409 we set power here
|
statusrecord.InverterRecord.MinSoc.WriteLine(" MinSoc "); //30409 we set power here
|
||||||
statusrecord.InverterRecord.PowerFactor.WriteLine(" = PowerFactor");
|
statusrecord.InverterRecord.PowerFactor.WriteLine(" = PowerFactor");
|
||||||
statusrecord.InverterRecord.Battery1Soc.WriteLine(" SOC");
|
statusrecord.InverterRecord.Battery1Soc.WriteLine(" SOC");
|
||||||
statusrecord.InverterRecord.Battery1Power.WriteLine(" Battery Power");
|
statusrecord.InverterRecord.Battery1Power.WriteLine(" Battery Power");
|
||||||
|
|
@ -157,7 +157,6 @@ public static class Program
|
||||||
statusrecord.InverterRecord.Battery1Voltage.WriteLine(" Battery Voltage");
|
statusrecord.InverterRecord.Battery1Voltage.WriteLine(" Battery Voltage");
|
||||||
statusrecord.InverterRecord.BatteryMaxChargingCurrent.WriteLine(" BatteryMaxChargeCurrent "); //30409 we set power here
|
statusrecord.InverterRecord.BatteryMaxChargingCurrent.WriteLine(" BatteryMaxChargeCurrent "); //30409 we set power here
|
||||||
statusrecord.InverterRecord.BatteryMaxDischargingCurrent.WriteLine(" BatteryMaxDischargeCurrent "); //30409 we set power here
|
statusrecord.InverterRecord.BatteryMaxDischargingCurrent.WriteLine(" BatteryMaxDischargeCurrent "); //30409 we set power here
|
||||||
statusrecord.InverterRecord.MinSoc.WriteLine(" MinSoc "); //30409 we set power here
|
|
||||||
statusrecord.InverterRecord.ChargeCutoffSocVoltage.WriteLine(" ChargeCutoffSoc "); //30409 we set power here
|
statusrecord.InverterRecord.ChargeCutoffSocVoltage.WriteLine(" ChargeCutoffSoc "); //30409 we set power here
|
||||||
|
|
||||||
statusrecord.InverterRecord.PvPower.WriteLine(" Pv Power "); //30409 we set power here
|
statusrecord.InverterRecord.PvPower.WriteLine(" Pv Power "); //30409 we set power here
|
||||||
|
|
@ -206,6 +205,7 @@ public static class Program
|
||||||
status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent;
|
status.Config.MaximumDischargingCurrent = config.MaximumDischargingCurrent;
|
||||||
status.Config.OperatingPriority = config.OperatingPriority;
|
status.Config.OperatingPriority = config.OperatingPriority;
|
||||||
status.Config.BatteriesCount = config.BatteriesCount;
|
status.Config.BatteriesCount = config.BatteriesCount;
|
||||||
|
status.Config.ControlPermission = config.ControlPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String EssModeControl(StatusRecord? statusrecord)
|
private static String EssModeControl(StatusRecord? statusrecord)
|
||||||
|
|
@ -247,11 +247,10 @@ public static class Program
|
||||||
var alarmList = new List<AlarmOrWarning>();
|
var alarmList = new List<AlarmOrWarning>();
|
||||||
var warningList = new List<AlarmOrWarning>();
|
var warningList = new List<AlarmOrWarning>();
|
||||||
|
|
||||||
if (record.InverterRecord.SystemOperatingMode == GrowattSystemStatus.Fault)
|
|
||||||
|
if (record.InverterRecord.WarningMainCode != 0)
|
||||||
{
|
{
|
||||||
if (record.InverterRecord.FaultMainCode != 0)
|
warningList.Add(new AlarmOrWarning
|
||||||
{
|
|
||||||
alarmList.Add(new AlarmOrWarning
|
|
||||||
{
|
{
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
|
|
@ -260,9 +259,9 @@ public static class Program
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.InverterRecord.WarningMainCode != 0)
|
if (record.InverterRecord.FaultMainCode != 0)
|
||||||
{
|
{
|
||||||
warningList.Add(new AlarmOrWarning
|
alarmList.Add(new AlarmOrWarning
|
||||||
{
|
{
|
||||||
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
Date = DateTime.Now.ToString("yyyy-MM-dd"),
|
||||||
Time = DateTime.Now.ToString("HH:mm:ss"),
|
Time = DateTime.Now.ToString("HH:mm:ss"),
|
||||||
|
|
@ -270,7 +269,7 @@ public static class Program
|
||||||
Description = record.InverterRecord.FaultMainCode.ToString(), //to add the sub code
|
Description = record.InverterRecord.FaultMainCode.ToString(), //to add the sub code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
_sodiohomeAlarmState = warningList.Any()
|
_sodiohomeAlarmState = warningList.Any()
|
||||||
? SodistoreAlarmState.Orange
|
? SodistoreAlarmState.Orange
|
||||||
|
|
@ -376,7 +375,7 @@ public static class Program
|
||||||
_lastHeartbeatTime = DateTime.Now;
|
_lastHeartbeatTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s3Bucket != null)
|
if (s3Bucket != null) // why this is based on s3 bucket?
|
||||||
{
|
{
|
||||||
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
RabbitMqManager.InformMiddleware(currentSalimaxState);
|
||||||
}
|
}
|
||||||
|
|
@ -401,9 +400,8 @@ public static class Program
|
||||||
st.InverterRecord.EmsCommunicationFailureTime = 20; // 20 sec
|
st.InverterRecord.EmsCommunicationFailureTime = 20; // 20 sec
|
||||||
st.InverterRecord.EnableEmsCommunicationFailureTime = false;
|
st.InverterRecord.EnableEmsCommunicationFailureTime = false;
|
||||||
st.InverterRecord.EnableCommand = true;
|
st.InverterRecord.EnableCommand = true;
|
||||||
st.InverterRecord.ControlPermession = true;
|
st.InverterRecord.ControlPermission = st.Config.ControlPermission;;
|
||||||
st.InverterRecord.MaxSoc = 60; //st.Config.BatteryChargeCutoffVoltage;
|
st.InverterRecord.MaxSoc = 100; //st.Config.MaxSoc;
|
||||||
st.InverterRecord.MinSoc = 25; //st.Config.BatteryDischargeCutoffVoltage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<String, UInt16> ConvertToModbusRegisters(Object value, String outputType,
|
private static Dictionary<String, UInt16> ConvertToModbusRegisters(Object value, String outputType,
|
||||||
|
|
@ -645,7 +643,7 @@ public static class Program
|
||||||
Console.WriteLine(error);
|
Console.WriteLine(error);
|
||||||
|
|
||||||
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
||||||
Heartbit();
|
// Heartbit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,7 +652,7 @@ public static class Program
|
||||||
|
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"---------------------------------------- Resending FailedUploadedFiles----------------------------------------");
|
"---------------------------------------- Resending FailedUploadedFiles----------------------------------------");
|
||||||
Heartbit();
|
//Heartbit();
|
||||||
|
|
||||||
await ResendLocalFailedFilesAsync(s3Config); // retry any pending failed files
|
await ResendLocalFailedFilesAsync(s3Config); // retry any pending failed files
|
||||||
}
|
}
|
||||||
|
|
@ -667,7 +665,7 @@ public static class Program
|
||||||
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
Heartbit();
|
//Heartbit();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -676,7 +674,7 @@ public static class Program
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Heartbit()
|
/* private static void Heartbit()
|
||||||
{
|
{
|
||||||
var s3Bucket = Config.Load().S3?.Bucket;
|
var s3Bucket = Config.Load().S3?.Bucket;
|
||||||
var tryParse = int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
|
var tryParse = int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
|
||||||
|
|
@ -693,7 +691,7 @@ public static class Program
|
||||||
if (s3Bucket != null)
|
if (s3Bucket != null)
|
||||||
RabbitMqManager.InformMiddleware(returnedStatus);
|
RabbitMqManager.InformMiddleware(returnedStatus);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private static async Task SaveToLocalCompressedFallback(Byte[] compressedData, String fileNameWithoutExtension)
|
private static async Task SaveToLocalCompressedFallback(Byte[] compressedData, String fileNameWithoutExtension)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
public required EssMode OperatingPriority { get; set; }
|
public required EssMode OperatingPriority { get; set; }
|
||||||
public required Int16 BatteriesCount { get; set; }
|
public required Int16 BatteriesCount { get; set; }
|
||||||
public required Double ModbusProtcolNumber { get; set; }
|
public required Double ModbusProtcolNumber { get; set; }
|
||||||
|
public required Boolean ControlPermission { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public required S3Config? S3 { get; set; }
|
public required S3Config? S3 { get; set; }
|
||||||
|
|
||||||
|
|
@ -36,6 +38,7 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
|
||||||
OperatingPriority = EssMode.LoadPriority,
|
OperatingPriority = EssMode.LoadPriority,
|
||||||
BatteriesCount = 0,
|
BatteriesCount = 0,
|
||||||
ModbusProtcolNumber = 1.2,
|
ModbusProtcolNumber = 1.2,
|
||||||
|
ControlPermission = false,
|
||||||
S3 = new()
|
S3 = new()
|
||||||
{
|
{
|
||||||
Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataLogging;
|
||||||
|
|
||||||
|
public class LogFileConcatenator
|
||||||
|
{
|
||||||
|
private readonly String _LogDirectory;
|
||||||
|
|
||||||
|
public LogFileConcatenator(String logDirectory = "JsonLogDirectory/")
|
||||||
|
{
|
||||||
|
_LogDirectory = logDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String ConcatenateFiles(int numberOfFiles)
|
||||||
|
{
|
||||||
|
var logFiles = Directory
|
||||||
|
.GetFiles(_LogDirectory, "log_*.json")
|
||||||
|
.OrderByDescending(file => file)
|
||||||
|
.Take(numberOfFiles)
|
||||||
|
.OrderBy(file => file)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var concatenatedContent = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var fileContent in logFiles.Select(File.ReadAllText))
|
||||||
|
{
|
||||||
|
concatenatedContent.AppendLine(fileContent);
|
||||||
|
//concatenatedContent.AppendLine(); // Append an empty line to separate the files // maybe we don't need this
|
||||||
|
}
|
||||||
|
|
||||||
|
return concatenatedContent.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataLogging;
|
||||||
|
|
||||||
|
public class CustomLogger : ILogger
|
||||||
|
{
|
||||||
|
private readonly String _LogFilePath;
|
||||||
|
//private readonly Int64 _maxFileSizeBytes;
|
||||||
|
private readonly Int32 _MaxLogFileCount;
|
||||||
|
private Int64 _CurrentFileSizeBytes;
|
||||||
|
|
||||||
|
public CustomLogger(String logFilePath, Int32 maxLogFileCount)
|
||||||
|
{
|
||||||
|
_LogFilePath = logFilePath;
|
||||||
|
_MaxLogFileCount = maxLogFileCount;
|
||||||
|
_CurrentFileSizeBytes = File.Exists(logFilePath) ? new FileInfo(logFilePath).Length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public Boolean IsEnabled(LogLevel logLevel) => true; // Enable logging for all levels
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception, String> formatter)
|
||||||
|
{
|
||||||
|
var logMessage = formatter(state, exception!);
|
||||||
|
|
||||||
|
// Check the log file count and delete the oldest file if necessary
|
||||||
|
var logFileDir = Path.GetDirectoryName(_LogFilePath)!;
|
||||||
|
var logFileExt = Path.GetExtension(_LogFilePath);
|
||||||
|
var logFileBaseName = Path.GetFileNameWithoutExtension(_LogFilePath);
|
||||||
|
|
||||||
|
var logFiles = Directory
|
||||||
|
.GetFiles(logFileDir, $"{logFileBaseName}_*{logFileExt}")
|
||||||
|
.OrderBy(file => file)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (logFiles.Count >= _MaxLogFileCount)
|
||||||
|
{
|
||||||
|
File.Delete(logFiles.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
var roundedUnixTimestamp = DateTime.Now.ToUnixTime() % 2 == 0 ? DateTime.Now.ToUnixTime() : DateTime.Now.ToUnixTime() + 1;
|
||||||
|
var timestamp = "Timestamp;" + roundedUnixTimestamp + Environment.NewLine;
|
||||||
|
|
||||||
|
var logFileBackupPath = Path.Combine(logFileDir, $"{logFileBaseName}_{DateTime.Now.ToUnixTime()}{logFileExt}");
|
||||||
|
File.AppendAllText(logFileBackupPath, timestamp + logMessage + Environment.NewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataLogging;
|
||||||
|
|
||||||
|
public static class Logger
|
||||||
|
{
|
||||||
|
// Specify the maximum log file size in bytes (e.g., 1 MB)
|
||||||
|
|
||||||
|
//private const Int32 MaxFileSizeBytes = 2024 * 30; // TODO: move to settings
|
||||||
|
private const Int32 MaxLogFileCount = 5000; // TODO: move to settings
|
||||||
|
private const String LogFilePath = "JsonLogDirectory/log.json"; // TODO: move to settings
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
private static readonly ILogger _logger = new CustomLogger(LogFilePath, MaxLogFileCount);
|
||||||
|
|
||||||
|
public static T LogInfo<T>(this T t) where T : notnull
|
||||||
|
{
|
||||||
|
_logger.LogInformation(t.ToString()); // TODO: check warning
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T LogDebug<T>(this T t) where T : notnull
|
||||||
|
{
|
||||||
|
// _logger.LogDebug(t.ToString()); // TODO: check warning
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T LogError<T>(this T t) where T : notnull
|
||||||
|
{
|
||||||
|
// _logger.LogError(t.ToString()); // TODO: check warning
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T LogWarning<T>(this T t) where T : notnull
|
||||||
|
{
|
||||||
|
// _logger.LogWarning(t.ToString()); // TODO: check warning
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
|
||||||
|
public class AlarmOrWarning
|
||||||
|
{
|
||||||
|
public String? Date { get; set; }
|
||||||
|
public String? Time { get; set; }
|
||||||
|
public String? Description { get; set; }
|
||||||
|
public String? CreatedBy { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
|
||||||
|
public class Configuration
|
||||||
|
{
|
||||||
|
public Double MinimumSoC { get; set; }
|
||||||
|
public Double MaximumDischargingCurrent { get; set; }
|
||||||
|
public Double MaximumChargingCurrent { get; set; }
|
||||||
|
//public WorkingMode OperatingPriority { get; set; }
|
||||||
|
public Int16 BatteriesCount { get; set; }
|
||||||
|
public Int16 ClusterNumber { get; set; }
|
||||||
|
public Int16 PvNumber { get; set; }
|
||||||
|
public DateTime StartTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
public DateTime StopTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
public Single TimeChargeandDischargePower { get; set; }
|
||||||
|
public Boolean ControlPermission { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
|
||||||
|
public enum SodistoreAlarmState
|
||||||
|
{
|
||||||
|
Green,
|
||||||
|
Orange,
|
||||||
|
Red
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
|
||||||
|
public class StatusMessage
|
||||||
|
{
|
||||||
|
public required Int32 InstallationId { get; set; }
|
||||||
|
public required Int32 Product { get; set; }
|
||||||
|
public required SodistoreAlarmState Status { get; set; }
|
||||||
|
public required MessageType Type { get; set; }
|
||||||
|
public List<AlarmOrWarning>? Warnings { get; set; }
|
||||||
|
public List<AlarmOrWarning>? Alarms { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
AlarmOrWarning,
|
||||||
|
Heartbit
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
|
||||||
|
public enum DeviceState
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Measured,
|
||||||
|
Computed
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using InnovEnergy.Lib.Utils.Net;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
|
||||||
|
public class SalimaxDevice : Ip4Address
|
||||||
|
{
|
||||||
|
public required DeviceState DeviceState { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using InnovEnergy.App.KacoCommunication.SystemConfig;
|
||||||
|
using InnovEnergy.Lib.Devices.BatteryDeligreen;
|
||||||
|
using InnovEnergy.Lib.Devices.Kaco92L3;
|
||||||
|
using InnovEnergy.Lib.Devices.PLVario2Meter;
|
||||||
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.ESS;
|
||||||
|
|
||||||
|
public class StatusRecord
|
||||||
|
{
|
||||||
|
public required KacoRecord? InverterRecord { get; set; }
|
||||||
|
public required PlVarioMeterRecord? GridMeterRecord { get; set; }
|
||||||
|
public required DcDcDevicesRecord? DcDc { get; init; }
|
||||||
|
|
||||||
|
public required BatteryDeligreenRecords? BatteryKabinet1 { get; set; }
|
||||||
|
public required BatteryDeligreenRecords? BatteryKabinet2 { get; set; }
|
||||||
|
public required BatteryDeligreenRecords? BatteryKabinet3 { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public required Config Config { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication;
|
||||||
|
|
||||||
|
public static class Flow
|
||||||
|
{
|
||||||
|
private static readonly String RightArrowChar = ">";
|
||||||
|
private static readonly String LeftArrowChar = "<";
|
||||||
|
private static readonly String DownArrowChar = "V";
|
||||||
|
private static readonly String UpArrowChar = "^";
|
||||||
|
private static readonly String UnknownArrowChar = "?";
|
||||||
|
|
||||||
|
public static TextBlock Horizontal(Unit? amount) => Horizontal(amount, 10);
|
||||||
|
|
||||||
|
public static TextBlock Horizontal(Unit? amount, Int32 width)
|
||||||
|
{
|
||||||
|
var label = amount?.ToDisplayString() ?? "";
|
||||||
|
|
||||||
|
var arrowChar = amount switch
|
||||||
|
{
|
||||||
|
{ Value: < 0 } => LeftArrowChar,
|
||||||
|
{ Value: >= 0 } => RightArrowChar,
|
||||||
|
_ => UnknownArrowChar,
|
||||||
|
};
|
||||||
|
|
||||||
|
//var arrowChar = amount.Value < 0 ? LeftArrowChar : RightArrowChar;
|
||||||
|
var arrow = Enumerable.Repeat(arrowChar, width).Join();
|
||||||
|
|
||||||
|
// note : appending "fake label" below to make it vertically symmetric
|
||||||
|
return TextBlock.AlignCenterHorizontal(label, arrow, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextBlock Vertical(Unit? amount) => Vertical(amount, 4);
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
|
[SuppressMessage("ReSharper", "CoVariantArrayConversion")]
|
||||||
|
public static TextBlock Vertical(Unit? amount, Int32 height)
|
||||||
|
{
|
||||||
|
var label = amount?.ToDisplayString() ?? UnknownArrowChar;
|
||||||
|
var arrowChar = amount switch
|
||||||
|
{
|
||||||
|
{ Value: < 0 } => UpArrowChar,
|
||||||
|
{ Value: >= 0 } => DownArrowChar,
|
||||||
|
_ => UnknownArrowChar,
|
||||||
|
};
|
||||||
|
|
||||||
|
// var arrowChar = amount is null ? UnknownArrowChar
|
||||||
|
// : amount.Value < 0 ? UpArrowChar
|
||||||
|
// : DownArrowChar;
|
||||||
|
|
||||||
|
return TextBlock.AlignCenterHorizontal(arrowChar, arrowChar, label, arrowChar, arrowChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="../InnovEnergy.App.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<RootNamespace>InnovEnergy.App.KacoCommunication</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Lib\Devices\BatteryDeligreen\BatteryDeligreen.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Lib\Devices\Kaco92L3\Kaco92L3.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Lib\Devices\PLVario2Meter\PLVario2Meter.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Lib\Devices\Trumpf\TruConvertDc\TruConvertDc.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Lib\Units\Units.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Lib\Utils\Utils.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Flurl.Http" Version="4.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.MiddlewareClasses;
|
||||||
|
|
||||||
|
public static class MiddlewareAgent
|
||||||
|
{
|
||||||
|
private static UdpClient _udpListener = null!;
|
||||||
|
private static IPAddress? _controllerIpAddress;
|
||||||
|
private static EndPoint? _endPoint;
|
||||||
|
|
||||||
|
public static void InitializeCommunicationToMiddleware()
|
||||||
|
{
|
||||||
|
_controllerIpAddress = FindVpnIp();
|
||||||
|
if (Equals(IPAddress.None, _controllerIpAddress))
|
||||||
|
{
|
||||||
|
Console.WriteLine("There is no VPN interface, exiting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Int32 udpPort = 9000;
|
||||||
|
_endPoint = new IPEndPoint(_controllerIpAddress, udpPort);
|
||||||
|
|
||||||
|
_udpListener = new UdpClient();
|
||||||
|
_udpListener.Client.Blocking = false;
|
||||||
|
_udpListener.Client.Bind(_endPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IPAddress FindVpnIp()
|
||||||
|
{
|
||||||
|
const String interfaceName = "innovenergy";
|
||||||
|
|
||||||
|
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||||
|
|
||||||
|
foreach (var networkInterface in networkInterfaces)
|
||||||
|
{
|
||||||
|
if (networkInterface.Name == interfaceName)
|
||||||
|
{
|
||||||
|
var ipProps = networkInterface.GetIPProperties();
|
||||||
|
var uniCastIPs = ipProps.UnicastAddresses;
|
||||||
|
var controllerIpAddress = uniCastIPs[0].Address;
|
||||||
|
|
||||||
|
Console.WriteLine("VPN IP is: "+ uniCastIPs[0].Address);
|
||||||
|
return controllerIpAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPAddress.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Configuration? SetConfigurationFile()
|
||||||
|
{
|
||||||
|
if (_udpListener.Available > 0)
|
||||||
|
{
|
||||||
|
IPEndPoint? serverEndpoint = null;
|
||||||
|
|
||||||
|
var replyMessage = "ACK";
|
||||||
|
var replyData = Encoding.UTF8.GetBytes(replyMessage);
|
||||||
|
|
||||||
|
var udpMessage = _udpListener.Receive(ref serverEndpoint);
|
||||||
|
var message = Encoding.UTF8.GetString(udpMessage);
|
||||||
|
|
||||||
|
var config = JsonSerializer.Deserialize<Configuration>(message);
|
||||||
|
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Received a configuration message: " +
|
||||||
|
"MinimumSoC is " + config.MinimumSoC + "Number of batteries is " + config.BatteriesCount
|
||||||
|
+ "Maximum Charging current is "+ config.MaximumChargingCurrent + "/n" + "Maximum Discharging current is " + config.MaximumDischargingCurrent
|
||||||
|
+ "StartTimeChargeandDischargeDayandTime is" + config.StartTimeChargeandDischargeDayandTime + "StopTimeChargeandDischargeDayandTime is" + config.StopTimeChargeandDischargeDayandTime
|
||||||
|
+ "TimeChargeandDischargePowert is " + config.TimeChargeandDischargePower + " Control permission is" + config.ControlPermission);
|
||||||
|
|
||||||
|
// Send the reply to the sender's endpoint
|
||||||
|
_udpListener.Send(replyData, replyData.Length, serverEndpoint);
|
||||||
|
Console.WriteLine($"Replied to {serverEndpoint}: {replyMessage}");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_endPoint != null && !_endPoint.Equals(_udpListener.Client.LocalEndPoint as IPEndPoint))
|
||||||
|
{
|
||||||
|
Console.WriteLine("UDP address has changed, rebinding...");
|
||||||
|
InitializeCommunicationToMiddleware();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.MiddlewareClasses;
|
||||||
|
|
||||||
|
|
||||||
|
[SuppressMessage("Trimming", "IL2026:Members annotated with \'RequiresUnreferencedCodeAttribute\' require dynamic access otherwise can break functionality when trimming application code")]
|
||||||
|
public static class RabbitMqManager
|
||||||
|
{
|
||||||
|
public static ConnectionFactory? Factory ;
|
||||||
|
public static IConnection ? Connection;
|
||||||
|
public static IModel? Channel;
|
||||||
|
|
||||||
|
public static Boolean SubscribeToQueue(StatusMessage currentSalimaxState, String? s3Bucket,String VpnServerIp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//_factory = new ConnectionFactory { HostName = VpnServerIp };
|
||||||
|
|
||||||
|
Factory = new ConnectionFactory
|
||||||
|
{
|
||||||
|
HostName = VpnServerIp,
|
||||||
|
Port = 5672,
|
||||||
|
VirtualHost = "/",
|
||||||
|
UserName = "producer",
|
||||||
|
Password = "b187ceaddb54d5485063ddc1d41af66f",
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Connection = Factory.CreateConnection();
|
||||||
|
Channel = Connection.CreateModel();
|
||||||
|
Channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
|
Console.WriteLine("The controller sends its status to the middleware for the first time");
|
||||||
|
if (s3Bucket != null) InformMiddleware(currentSalimaxState);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("An error occurred while connecting to the RabbitMQ queue: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InformMiddleware(StatusMessage status)
|
||||||
|
{
|
||||||
|
var message = JsonSerializer.Serialize(status);
|
||||||
|
var body = Encoding.UTF8.GetBytes(message);
|
||||||
|
|
||||||
|
Channel.BasicPublish(exchange: string.Empty,
|
||||||
|
routingKey: "statusQueue",
|
||||||
|
basicProperties: null,
|
||||||
|
body: body);
|
||||||
|
|
||||||
|
Console.WriteLine($"Producer sent message: {message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,637 @@
|
||||||
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Reactive.Threading.Tasks;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Flurl.Http;
|
||||||
|
using InnovEnergy.App.KacoCommunication.DataLogging;
|
||||||
|
using InnovEnergy.App.KacoCommunication.DataTypes;
|
||||||
|
using InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
using InnovEnergy.App.KacoCommunication.ESS;
|
||||||
|
using InnovEnergy.App.KacoCommunication.MiddlewareClasses;
|
||||||
|
using InnovEnergy.App.KacoCommunication.SystemConfig;
|
||||||
|
using InnovEnergy.Lib.Devices.BatteryDeligreen;
|
||||||
|
using InnovEnergy.Lib.Devices.Kaco92L3;
|
||||||
|
using InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
using InnovEnergy.Lib.Devices.PLVario2Meter;
|
||||||
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Formatting = Newtonsoft.Json.Formatting;
|
||||||
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication;
|
||||||
|
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
private static readonly TimeSpan UpdateInterval = TimeSpan.FromSeconds(5);
|
||||||
|
private const UInt16 NbrOfFileToConcatenate = 15; // add this to config file
|
||||||
|
private static UInt16 _fileCounter = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static SodistoreAlarmState _prevSodiohomeAlarmState = SodistoreAlarmState.Green;
|
||||||
|
private static SodistoreAlarmState _sodiAlarmState = SodistoreAlarmState.Green;
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly IReadOnlyList<Byte> BatteryNodes;
|
||||||
|
private static readonly Channel KacoChannel;
|
||||||
|
private static readonly Channel GridMeterChannel;
|
||||||
|
private static readonly Channel DcDcChannel;
|
||||||
|
|
||||||
|
private const String Port1Cabinet = "/dev/ttyUSB0"; // move to a config file
|
||||||
|
private const String Port2Cabinet = "/dev/ttyUSB1"; // move to a config file
|
||||||
|
private const String Port3Cabinet = "/dev/ttyUSB2"; // move to a config file
|
||||||
|
|
||||||
|
private static readonly String SwVersionNumber = " V1.00." + DateTime.Today;
|
||||||
|
private const String VpnServerIp = "10.2.0.11";
|
||||||
|
public static Boolean _subscribedToQueue = false;
|
||||||
|
public static Boolean _subscribeToQueueForTheFirstTime = false;
|
||||||
|
|
||||||
|
private static Int32 _failsCounter = 0; // move to a config file
|
||||||
|
// private static SodistoreAlarmState _prevSodiohomeAlarmState = SodistoreAlarmState.Green;
|
||||||
|
// private static SodistoreAlarmState _sodiohomeAlarmState = SodistoreAlarmState.Green;
|
||||||
|
|
||||||
|
static Program()
|
||||||
|
{
|
||||||
|
var config = Config.Load();
|
||||||
|
var d = config.Devices;
|
||||||
|
|
||||||
|
Channel CreateChannel(SalimaxDevice device) => device.DeviceState == DeviceState.Disabled
|
||||||
|
? new NullChannel()
|
||||||
|
: new TcpChannel(device);
|
||||||
|
|
||||||
|
BatteryNodes = config
|
||||||
|
.Devices
|
||||||
|
.BatteryNodes
|
||||||
|
.Select(n => n.ConvertTo<Byte>())
|
||||||
|
.ToArray(config.Devices.BatteryNodes.Length);
|
||||||
|
|
||||||
|
KacoChannel = CreateChannel(d.KacoIp);
|
||||||
|
GridMeterChannel = CreateChannel(d.GridMeterIp);
|
||||||
|
DcDcChannel = CreateChannel(d.DcDcIp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task Main(String[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// e.LogError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ReSharper disable once FunctionNeverReturns
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task Run()
|
||||||
|
{
|
||||||
|
Watchdog.NotifyReady();
|
||||||
|
|
||||||
|
Console.WriteLine("Starting Kaco Communication");
|
||||||
|
|
||||||
|
var kacoDevice = new KacoDevice(KacoChannel);
|
||||||
|
var gridMeterDevice = new PlVarioMeterDevice(GridMeterChannel);
|
||||||
|
var dcDcDevices = new TruConvertDcDcDevices(DcDcChannel);
|
||||||
|
|
||||||
|
var firstCabinetBatteriesDevice = BatteryNodes.Select(n => new BatteryDeligreenDevice(Port1Cabinet, n)).ToList();
|
||||||
|
var secondCabinetBatteriesDevice = BatteryNodes.Select(n => new BatteryDeligreenDevice(Port2Cabinet, n)).ToList();
|
||||||
|
var thirdCabinetBatteriesDevice = BatteryNodes.Select(n => new BatteryDeligreenDevice(Port3Cabinet, n)).ToList();
|
||||||
|
|
||||||
|
var batteryDevices1 = new BatteryDeligreenDevices(firstCabinetBatteriesDevice);
|
||||||
|
var batteryDevices2 = new BatteryDeligreenDevices(secondCabinetBatteriesDevice);
|
||||||
|
var batteryDevices3 = new BatteryDeligreenDevices(thirdCabinetBatteriesDevice);
|
||||||
|
|
||||||
|
StatusRecord? ReadStatus()
|
||||||
|
{
|
||||||
|
PlVarioMeterRecord? gridRecord = null;
|
||||||
|
var config = Config.Load();
|
||||||
|
var kacoRecord = kacoDevice.Read();
|
||||||
|
var gridrawRecord = gridMeterDevice.Read();
|
||||||
|
var dcDcRecord = dcDcDevices.Read();
|
||||||
|
if (gridrawRecord != null)
|
||||||
|
{
|
||||||
|
gridRecord = new PlVarioMeterRecord(gridrawRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
var batteryKabinet1 = batteryDevices1.Read();
|
||||||
|
var batteryKabinet2 = batteryDevices2.Read();
|
||||||
|
var batteryKabinet3 = batteryDevices3.Read();
|
||||||
|
|
||||||
|
return new StatusRecord
|
||||||
|
{
|
||||||
|
InverterRecord = kacoRecord,
|
||||||
|
GridMeterRecord = gridRecord,
|
||||||
|
DcDc = dcDcRecord,
|
||||||
|
BatteryKabinet1 = batteryKabinet1,
|
||||||
|
BatteryKabinet2 = batteryKabinet2,
|
||||||
|
BatteryKabinet3 = batteryKabinet3,
|
||||||
|
Config = config // load from disk every iteration, so config can be changed while running
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
await Observable
|
||||||
|
.Interval(UpdateInterval)
|
||||||
|
.Select(_ => RunIteration())
|
||||||
|
.SelectMany(status =>
|
||||||
|
DataLogging(status, DateTime.Now.Round(UpdateInterval))
|
||||||
|
.ContinueWith(_ => status)) // back to StatusRecord
|
||||||
|
.SelectMany(SaveModbusTcpFile)
|
||||||
|
.SelectError()
|
||||||
|
.ToTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StatusRecord? RunIteration()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Watchdog.NotifyAlive();
|
||||||
|
|
||||||
|
var startTime = DateTime.Now;
|
||||||
|
Console.WriteLine(
|
||||||
|
"***************************** Reading Kaco Data *********************************************");
|
||||||
|
Console.WriteLine(startTime.ToString("HH:mm:ss.fff"));
|
||||||
|
// the order matter of the next three lines
|
||||||
|
var statusrecord = ReadStatus();
|
||||||
|
statusrecord?.CreateSimpleTopologyTextBlock().WriteLine();
|
||||||
|
|
||||||
|
|
||||||
|
// statusrecord?.DcDc?.Dc.Battery.Power .WriteLine(" Power");
|
||||||
|
// statusrecord?.DcDc?.Dc.Battery.Voltage .WriteLine(" Voltage");
|
||||||
|
// statusrecord?.DcDc?.Dc.Battery.Current .WriteLine(" Current");
|
||||||
|
// statusrecord?.DcDc?.Dc.Link.Voltage .WriteLine(" Dc link Voltage");
|
||||||
|
|
||||||
|
|
||||||
|
statusrecord?.GridMeterRecord?.Frequency .WriteLine(" Frequency");
|
||||||
|
statusrecord?.GridMeterRecord?.VoltageU1 .WriteLine(" VoltageU1");
|
||||||
|
statusrecord?.GridMeterRecord?.VoltageU2 .WriteLine(" VoltageU2");
|
||||||
|
statusrecord?.GridMeterRecord?.VoltageU3 .WriteLine(" VoltageU3");
|
||||||
|
|
||||||
|
statusrecord?.GridMeterRecord?.CurrentI1 .WriteLine(" CurrentI1");
|
||||||
|
statusrecord?.GridMeterRecord?.CurrentI2 .WriteLine(" CurrentI2");
|
||||||
|
statusrecord?.GridMeterRecord?.CurrentI3 .WriteLine(" CurrentI3");
|
||||||
|
|
||||||
|
statusrecord?.GridMeterRecord?.ActivePowerL1 .WriteLine(" ActivePowerL1");
|
||||||
|
statusrecord?.GridMeterRecord?.ActivePowerL2 .WriteLine(" ActivePowerL2");
|
||||||
|
statusrecord?.GridMeterRecord?.ActivePowerL3 .WriteLine(" ActivePowerL3");
|
||||||
|
statusrecord?.GridMeterRecord?.ActivePowerTotal .WriteLine(" ActivePowerTotal");
|
||||||
|
|
||||||
|
statusrecord?.InverterRecord?.CurrentState.WriteLine(" CurrentState");
|
||||||
|
statusrecord?.InverterRecord?.RequestedState.WriteLine(" RequestedState");
|
||||||
|
statusrecord?.InverterRecord?.PcuError.WriteLine(" PcuError");
|
||||||
|
statusrecord?.InverterRecord?.PcuState.WriteLine(" PcuState");
|
||||||
|
|
||||||
|
statusrecord?.InverterRecord?.BattCharId.WriteLine(" _battCharId");
|
||||||
|
statusrecord?.InverterRecord?.BattCharLength.WriteLine(" _battCharLength");
|
||||||
|
|
||||||
|
statusrecord?.InverterRecord?.MinDischargeVoltage.WriteLine(" MinDischargeVoltage");
|
||||||
|
statusrecord?.InverterRecord?.MaxDischargeCurrent.WriteLine(" MaxDischargeCurrent");
|
||||||
|
statusrecord?.InverterRecord?.DischargeCutoffCurrent.WriteLine(" DischargeCutoffCurrent");
|
||||||
|
|
||||||
|
statusrecord?.InverterRecord?.MaxChargeVoltage.WriteLine(" MaxChargeVoltage");
|
||||||
|
statusrecord?.InverterRecord?.MaxChargeCurrent.WriteLine(" MaxChargeCurrent");
|
||||||
|
statusrecord?.InverterRecord?.ChargeCutoffCurrent.WriteLine(" ChargeCutoffCurrent");
|
||||||
|
|
||||||
|
statusrecord?.InverterRecord?.ActivePowerSetPercent.WriteLine(" ActivePowerSetPercent");
|
||||||
|
statusrecord?.InverterRecord?.ReactivePowerSetPercent.WriteLine(" ReactivePowerSetPercent");
|
||||||
|
statusrecord?.InverterRecord?.WatchdogSeconds.WriteLine(" WatchdogSeconds");
|
||||||
|
InitializeKacoStartup(statusrecord);
|
||||||
|
|
||||||
|
Console.WriteLine( " ************************************ We are writing ************************************");
|
||||||
|
|
||||||
|
statusrecord?.Config.Save(); // save the config file
|
||||||
|
if (statusrecord?.InverterRecord != null) kacoDevice.Write(statusrecord.InverterRecord);
|
||||||
|
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff"));
|
||||||
|
|
||||||
|
return statusrecord;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Handle exception and print the error
|
||||||
|
Console.WriteLine(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SavingLocalCsvFile(Int64 timestamp, String csv)
|
||||||
|
{
|
||||||
|
const String directoryPath = "/home/inesco/salimax/csvFile";
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
if (!Directory.Exists(directoryPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all .csv files ordered by creation time (oldest first)
|
||||||
|
var csvFiles = new DirectoryInfo(directoryPath)
|
||||||
|
.GetFiles("*.csv")
|
||||||
|
.OrderBy(f => f.CreationTimeUtc)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// If more than 5000 files, delete the oldest
|
||||||
|
if (csvFiles.Count >= 5000)
|
||||||
|
{
|
||||||
|
var oldestFile = csvFiles.First();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oldestFile.Delete();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to delete file: {oldestFile.FullName}, Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the filtered CSV content
|
||||||
|
var filteredCsv = csv
|
||||||
|
.SplitLines()
|
||||||
|
.Where(l => !l.Contains("Secret"))
|
||||||
|
.JoinLines();
|
||||||
|
|
||||||
|
// Save the new CSV file
|
||||||
|
var filePath = Path.Combine(directoryPath, timestamp + ".csv");
|
||||||
|
await File.WriteAllTextAsync(filePath, filteredCsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Boolean> DataLogging(StatusRecord status, DateTime timeStamp)
|
||||||
|
{
|
||||||
|
var csv = status.ToCsv();
|
||||||
|
|
||||||
|
// for debug, only to be deleted.
|
||||||
|
//foreach (var item in csv.SplitLines())
|
||||||
|
//{
|
||||||
|
// Console.WriteLine(item + "");
|
||||||
|
//}
|
||||||
|
|
||||||
|
await SavingLocalCsvFile(timeStamp.ToUnixTime(), csv);
|
||||||
|
|
||||||
|
var jsonData = new Dictionary<String, Object>();
|
||||||
|
|
||||||
|
ConvertToJson(csv, jsonData).LogInfo();
|
||||||
|
|
||||||
|
var s3Config = status.Config.S3;
|
||||||
|
|
||||||
|
if (s3Config is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Concatenating 15 files in one file
|
||||||
|
return await ConcatinatingAndCompressingFiles(timeStamp.ToUnixTime(), s3Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String ConvertToJson(String csv, Dictionary<String, Object> jsonData)
|
||||||
|
{
|
||||||
|
foreach (var line in csv.Split('\n'))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||||
|
|
||||||
|
var parts = line.Split(';');
|
||||||
|
var keyPath = parts[0];
|
||||||
|
var value = parts[1];
|
||||||
|
var unit = parts.Length > 2 ? parts[2].Trim() : "";
|
||||||
|
InsertIntoJson(jsonData, keyPath.Split('/'), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonOutput = JsonConvert.SerializeObject(jsonData, Formatting.None);
|
||||||
|
return jsonOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Boolean> ConcatinatingAndCompressingFiles(Int64 timeStamp, S3Config s3Config)
|
||||||
|
{
|
||||||
|
if (_fileCounter >= NbrOfFileToConcatenate)
|
||||||
|
{
|
||||||
|
_fileCounter = 0;
|
||||||
|
|
||||||
|
var logFileConcatenator = new LogFileConcatenator();
|
||||||
|
var jsontoSend = logFileConcatenator.ConcatenateFiles(NbrOfFileToConcatenate);
|
||||||
|
|
||||||
|
var fileNameWithoutExtension = timeStamp.ToString(); // used for both S3 and local
|
||||||
|
var s3Path = fileNameWithoutExtension + ".json";
|
||||||
|
|
||||||
|
var request = s3Config.CreatePutRequest(s3Path);
|
||||||
|
|
||||||
|
var compressedBytes = CompresseBytes(jsontoSend);
|
||||||
|
var base64String = Convert.ToBase64String(compressedBytes);
|
||||||
|
var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64");
|
||||||
|
|
||||||
|
var uploadSucceeded = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await request.PutAsync(stringContent);
|
||||||
|
|
||||||
|
if (response.StatusCode != 200)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ERROR: PUT");
|
||||||
|
var error = await response.GetStringAsync();
|
||||||
|
Console.WriteLine(error);
|
||||||
|
|
||||||
|
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
||||||
|
Heartbit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadSucceeded = true;
|
||||||
|
Console.WriteLine("✅ File uploaded to S3 successfully.");
|
||||||
|
|
||||||
|
Console.WriteLine(
|
||||||
|
"---------------------------------------- Resending FailedUploadedFiles----------------------------------------");
|
||||||
|
Heartbit();
|
||||||
|
|
||||||
|
await ResendLocalFailedFilesAsync(s3Config); // retry any pending failed files
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Upload exception: " + ex.Message);
|
||||||
|
|
||||||
|
if (!uploadSucceeded)
|
||||||
|
{
|
||||||
|
await SaveToLocalCompressedFallback(compressedBytes, fileNameWithoutExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
Heartbit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_fileCounter++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Heartbit()
|
||||||
|
{
|
||||||
|
var s3Bucket = Config.Load().S3?.Bucket;
|
||||||
|
var tryParse = int.TryParse(s3Bucket?.Split("-")[0], out var installationId);
|
||||||
|
if (tryParse)
|
||||||
|
{
|
||||||
|
var returnedStatus = new StatusMessage
|
||||||
|
{
|
||||||
|
InstallationId = installationId,
|
||||||
|
Product = 3,
|
||||||
|
Status = _sodiAlarmState,
|
||||||
|
Type = MessageType.Heartbit,
|
||||||
|
};
|
||||||
|
if (s3Bucket != null)
|
||||||
|
RabbitMqManager.InformMiddleware(returnedStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task SaveToLocalCompressedFallback(Byte[] compressedData, String fileNameWithoutExtension)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var fallbackDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FailedUploads");
|
||||||
|
Directory.CreateDirectory(fallbackDir);
|
||||||
|
|
||||||
|
var fileName = fileNameWithoutExtension + ".json"; // Save as .json, but still compressed
|
||||||
|
var fullPath = Path.Combine(fallbackDir, fileName);
|
||||||
|
|
||||||
|
await File.WriteAllBytesAsync(fullPath, compressedData); // Compressed data
|
||||||
|
Console.WriteLine($"Saved compressed failed upload to: {fullPath}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to save compressed file locally: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Byte[] CompresseBytes(String jsonToSend)
|
||||||
|
{
|
||||||
|
//Compress JSON data to a byte array
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
//Create a zip directory and put the compressed file inside
|
||||||
|
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
||||||
|
{
|
||||||
|
var entry = archive.CreateEntry("data.json", CompressionLevel.SmallestSize); // Add JSON data to the ZIP archive
|
||||||
|
using (var entryStream = entry.Open())
|
||||||
|
using (var writer = new StreamWriter(entryStream))
|
||||||
|
{
|
||||||
|
writer.Write(jsonToSend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressedBytes = memoryStream.ToArray();
|
||||||
|
|
||||||
|
return compressedBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task ResendLocalFailedFilesAsync(S3Config s3Config)
|
||||||
|
{
|
||||||
|
var fallbackDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "FailedUploads");
|
||||||
|
|
||||||
|
if (!Directory.Exists(fallbackDir))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var files = Directory.GetFiles(fallbackDir, "*.json");
|
||||||
|
files.Length.WriteLine(" Number of failed files, to upload");
|
||||||
|
|
||||||
|
foreach (var filePath in files)
|
||||||
|
{
|
||||||
|
var fileName = Path.GetFileName(filePath); // e.g., "1720023600.json"
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] compressedBytes = await File.ReadAllBytesAsync(filePath);
|
||||||
|
var base64String = Convert.ToBase64String(compressedBytes);
|
||||||
|
var stringContent = new StringContent(base64String, Encoding.UTF8, "application/base64");
|
||||||
|
|
||||||
|
var request = s3Config.CreatePutRequest(fileName);
|
||||||
|
var response = await request.PutAsync(stringContent);
|
||||||
|
|
||||||
|
if (response.StatusCode == 200)
|
||||||
|
{
|
||||||
|
File.Delete(filePath);
|
||||||
|
Console.WriteLine($"✅ Successfully resent and deleted: {fileName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Failed to resend {fileName}, status: {response.StatusCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"⚠️ Exception while resending {fileName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Boolean> SaveModbusTcpFile(StatusRecord status)
|
||||||
|
{
|
||||||
|
var modbusData = new Dictionary<String, UInt16>();
|
||||||
|
|
||||||
|
// SYSTEM DATA
|
||||||
|
var result1 = ConvertToModbusRegisters((status.Config.MinSoc * 10), "UInt16", 30001); // this to be updated to modbusTCP version
|
||||||
|
var result2 = ConvertToModbusRegisters(status.InverterRecord!.PcuError, "UInt32", 30002);
|
||||||
|
|
||||||
|
// Merge all results into one dictionary
|
||||||
|
|
||||||
|
var allResults = new[]
|
||||||
|
{
|
||||||
|
result1,result2
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var result in allResults)
|
||||||
|
{
|
||||||
|
foreach (var entry in result)
|
||||||
|
{
|
||||||
|
modbusData[entry.Key] = entry.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write to JSON
|
||||||
|
var json = JsonSerializer.Serialize(modbusData, new JsonSerializerOptions { WriteIndented = true });
|
||||||
|
await File.WriteAllTextAsync("/home/inesco/SodiStoreHome/ModbusTCP/modbus_tcp_data.json", json);
|
||||||
|
|
||||||
|
//Console.WriteLine("JSON file written successfully.");
|
||||||
|
//Console.WriteLine(json);
|
||||||
|
var stopTime = DateTime.Now;
|
||||||
|
Console.WriteLine(stopTime.ToString("HH:mm:ss.fff" )+ " Finish the loop");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<String, UInt16> ConvertToModbusRegisters(Object value, String outputType,
|
||||||
|
Int32 startingAddress)
|
||||||
|
{
|
||||||
|
var registers = new Dictionary<String, UInt16>();
|
||||||
|
|
||||||
|
switch (outputType)
|
||||||
|
{
|
||||||
|
case "UInt16":
|
||||||
|
registers[startingAddress.ToString()] = Convert.ToUInt16(value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Int16":
|
||||||
|
var int16Val = Convert.ToInt16(value);
|
||||||
|
registers[startingAddress.ToString()] = (UInt16)int16Val; // reinterpret signed as ushort
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UInt32":
|
||||||
|
var uint32Val = Convert.ToUInt32(value);
|
||||||
|
registers[startingAddress.ToString()] = (UInt16)(uint32Val & 0xFFFF); // Low word
|
||||||
|
registers[(startingAddress + 1).ToString()] = (UInt16)(uint32Val >> 16); // High word
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "Int32":
|
||||||
|
var int32Val = Convert.ToInt32(value);
|
||||||
|
var raw = unchecked((UInt32)int32Val); // reinterprets signed int as unsigned
|
||||||
|
registers[startingAddress.ToString()] = (UInt16)(raw & 0xFFFF);
|
||||||
|
registers[(startingAddress + 1).ToString()] = (UInt16)(raw >> 16);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unsupported output type: " + outputType);
|
||||||
|
}
|
||||||
|
return registers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void InitializeKacoStartup(StatusRecord? statusRecord)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// 1. Apply DC – This part is physical and cannot be done in software.
|
||||||
|
// We assume DC power is already present.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// 2. Send valid battery limits (Model 64202)
|
||||||
|
// All values temporarily set to "1" as requested.
|
||||||
|
// You will replace them later with real values.
|
||||||
|
//
|
||||||
|
if (statusRecord?.InverterRecord != null)
|
||||||
|
{
|
||||||
|
statusRecord.InverterRecord.MinDischargeVoltage = 700f; // 64202.DisMinV
|
||||||
|
statusRecord.InverterRecord.MaxDischargeCurrent = 140f; // 64202.DisMaxA
|
||||||
|
statusRecord.InverterRecord.DischargeCutoffCurrent = 10f; // 64202.DisCutoffA
|
||||||
|
|
||||||
|
statusRecord.InverterRecord.MaxChargeVoltage = 800f; // 64202.ChaMaxV
|
||||||
|
statusRecord.InverterRecord.MaxChargeCurrent = 140f; // 64202.ChaMaxA
|
||||||
|
statusRecord.InverterRecord.ChargeCutoffCurrent = 10f; // 64202.ChaCutoffA
|
||||||
|
|
||||||
|
statusRecord.InverterRecord.WatchdogSeconds = 30; // this is additional from my seid
|
||||||
|
|
||||||
|
//
|
||||||
|
// 3. Enable limits (EnLimit)
|
||||||
|
//
|
||||||
|
statusRecord.InverterRecord.BatteryLimitsEnable = EnableDisableEnum.Enabled;
|
||||||
|
|
||||||
|
//
|
||||||
|
// After writing all values in software, send them to the inverter
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// 4. Read model 64201 to observe CurrentState transition
|
||||||
|
//
|
||||||
|
// Expected sequence:
|
||||||
|
// - Before valid limits: CurrentState == 7 (ERROR)
|
||||||
|
// - After valid limits: CurrentState == 8 (STANDBY)
|
||||||
|
// - Then after grid/DC conditions: CurrentState == 1 (OFF) or 11 (GRID_CONNECTED)
|
||||||
|
//
|
||||||
|
|
||||||
|
var state = statusRecord.InverterRecord.CurrentState;
|
||||||
|
|
||||||
|
Console.WriteLine($"KACO 64201.CurrentState = {state}");
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case CurrentState.Standby:
|
||||||
|
Console.WriteLine("Device is in STANDBY (8) — battery limits accepted.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CurrentState.Off:
|
||||||
|
Console.WriteLine("Device is OFF (1) — OK for non-battery operation.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CurrentState.GridConnected:
|
||||||
|
Console.WriteLine("Device is GRID CONNECTED (11).");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Console.WriteLine("Device in unexpected state: " + state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void InsertIntoJson(Dictionary<String, Object> jsonDict, String[] keys, String value)
|
||||||
|
{
|
||||||
|
var currentDict = jsonDict;
|
||||||
|
for (Int16 i = 1; i < keys.Length; i++) // Start at 1 to skip empty root
|
||||||
|
{
|
||||||
|
var key = keys[i];
|
||||||
|
if (!currentDict.ContainsKey(key))
|
||||||
|
{
|
||||||
|
currentDict[key] = new Dictionary<String, Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == keys.Length - 1) // Last key, store the value
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!value.Contains(",") && double.TryParse(value, out Double doubleValue)) // Try to parse value as a number
|
||||||
|
{
|
||||||
|
currentDict[key] = Math.Round(doubleValue, 2); // Round to 2 decimal places
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentDict[key] = value; // Store as string if not a number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentDict = (Dictionary<String, Object>)currentDict[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
using InnovEnergy.App.KacoCommunication.ESS;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using static System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.SystemConfig;
|
||||||
|
|
||||||
|
public class Config
|
||||||
|
{
|
||||||
|
private static String DefaultConfigFilePath => Path.Combine(Environment.CurrentDirectory, "config.json");
|
||||||
|
private static DateTime DefaultDatetime => new(2024, 03, 11, 09, 00, 00);
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };
|
||||||
|
|
||||||
|
public required Double MinSoc { get; set; }
|
||||||
|
|
||||||
|
/* public required Double MaximumDischargingCurrent { get; set; }
|
||||||
|
public required Double MaximumChargingCurrent { get; set; }
|
||||||
|
public required Int16 BatteriesCount { get; set; }
|
||||||
|
public required Double ModbusProtcolNumber { get; set; }*/
|
||||||
|
public required DeviceConfig Devices { get; set; }
|
||||||
|
|
||||||
|
public required S3Config? S3 { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private static String? LastSavedData { get; set; }
|
||||||
|
|
||||||
|
public static Config Default => new()
|
||||||
|
{
|
||||||
|
MinSoc = 20,
|
||||||
|
/* MaximumChargingCurrent = 180,
|
||||||
|
MaximumDischargingCurrent = 180,
|
||||||
|
BatteriesCount = 0,
|
||||||
|
ModbusProtcolNumber = 1.2,*/
|
||||||
|
Devices = new ()
|
||||||
|
{
|
||||||
|
KacoIp = new() { Host = "10.0.3.1", Port = 502, DeviceState = DeviceState.Measured},
|
||||||
|
DcDcIp = new() { Host = "10.0.2.1", Port = 502, DeviceState = DeviceState.Measured},
|
||||||
|
GridMeterIp = new() { Host = "192.168.1.5", Port = 502, DeviceState = DeviceState.Measured},
|
||||||
|
|
||||||
|
BatteryNodes = new []{0,1, 2, 3, 4, 5 }
|
||||||
|
},
|
||||||
|
|
||||||
|
S3 = new()
|
||||||
|
{
|
||||||
|
Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
||||||
|
Region = "sos-ch-dk-2",
|
||||||
|
Provider = "exo.io",
|
||||||
|
Key = "EXObb5a49acb1061781761895e7",
|
||||||
|
Secret = "sKhln0w8ii3ezZ1SJFF33yeDo8NWR1V4w2H0D4-350I",
|
||||||
|
ContentType = "text/plain; charset=utf-8"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void Save(String? path = null)
|
||||||
|
{
|
||||||
|
var configFilePath = path ?? DefaultConfigFilePath;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonString = Serialize(this, JsonOptions);
|
||||||
|
|
||||||
|
if (LastSavedData == jsonString)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LastSavedData = jsonString;
|
||||||
|
|
||||||
|
File.WriteAllText(configFilePath, jsonString);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
$"Failed to write config file {configFilePath}\n{e}".WriteLine();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Config Load(String? path = null)
|
||||||
|
{
|
||||||
|
var configFilePath = path ?? DefaultConfigFilePath;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonString = File.ReadAllText(configFilePath);
|
||||||
|
return Deserialize<Config>(jsonString)!;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
$"Failed to read config file {configFilePath}, using default config\n{e}".WriteLine();
|
||||||
|
return Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<Config> LoadAsync(String? path = null)
|
||||||
|
{
|
||||||
|
var configFilePath = path ?? DefaultConfigFilePath;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonString = await File.ReadAllTextAsync(configFilePath);
|
||||||
|
return Deserialize<Config>(jsonString)!;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Couldn't read config file {configFilePath}, using default config");
|
||||||
|
e.Message.WriteLine();
|
||||||
|
return Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.SystemConfig;
|
||||||
|
|
||||||
|
public class DeviceConfig
|
||||||
|
{
|
||||||
|
public required SalimaxDevice KacoIp { get; init; }
|
||||||
|
public required SalimaxDevice DcDcIp { get; init; }
|
||||||
|
public required SalimaxDevice GridMeterIp { get; init; }
|
||||||
|
|
||||||
|
public required Int32[] BatteryNodes { get; init; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Flurl;
|
||||||
|
using Flurl.Http;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using static System.Text.Encoding;
|
||||||
|
using Convert = System.Convert;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication.SystemConfig;
|
||||||
|
|
||||||
|
public record S3Config
|
||||||
|
{
|
||||||
|
public required String Bucket { get; init; }
|
||||||
|
public required String Region { get; init; }
|
||||||
|
public required String Provider { get; init; }
|
||||||
|
public required String Key { get; init; }
|
||||||
|
public required String Secret { get; init; }
|
||||||
|
public required String ContentType { get; init; }
|
||||||
|
|
||||||
|
private String Host => $"{Bucket}.{Region}.{Provider}";
|
||||||
|
private String Url => $"https://{Host}";
|
||||||
|
|
||||||
|
public IFlurlRequest CreatePutRequest(String s3Path) => CreateRequest("PUT", s3Path);
|
||||||
|
public IFlurlRequest CreateGetRequest(String s3Path) => CreateRequest("GET", s3Path);
|
||||||
|
|
||||||
|
private IFlurlRequest CreateRequest(String method, String s3Path)
|
||||||
|
{
|
||||||
|
var date = DateTime.UtcNow.ToString("r");
|
||||||
|
var auth = CreateAuthorization(method, s3Path, date);
|
||||||
|
|
||||||
|
return Url
|
||||||
|
.AppendPathSegment(s3Path)
|
||||||
|
.WithHeader("Host", Host)
|
||||||
|
.WithHeader("Date", date)
|
||||||
|
.WithHeader("Authorization", auth)
|
||||||
|
.AllowAnyHttpStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String CreateAuthorization(String method,
|
||||||
|
String s3Path,
|
||||||
|
String date)
|
||||||
|
{
|
||||||
|
return CreateAuthorization
|
||||||
|
(
|
||||||
|
method : method,
|
||||||
|
bucket : Bucket,
|
||||||
|
s3Path : s3Path,
|
||||||
|
date : date,
|
||||||
|
s3Key : Key,
|
||||||
|
s3Secret : Secret,
|
||||||
|
contentType: ContentType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static String CreateAuthorization(String method,
|
||||||
|
String bucket,
|
||||||
|
String s3Path,
|
||||||
|
String date,
|
||||||
|
String s3Key,
|
||||||
|
String s3Secret,
|
||||||
|
String contentType = "application/base64",
|
||||||
|
String md5Hash = "")
|
||||||
|
{
|
||||||
|
|
||||||
|
contentType = "application/base64; charset=utf-8";
|
||||||
|
//contentType = "text/plain; charset=utf-8"; //this to use when sending plain csv to S3
|
||||||
|
|
||||||
|
var payload = $"{method}\n{md5Hash}\n{contentType}\n{date}\n/{bucket.Trim('/')}/{s3Path.Trim('/')}";
|
||||||
|
using var hmacSha1 = new HMACSHA1(UTF8.GetBytes(s3Secret));
|
||||||
|
|
||||||
|
var signature = UTF8
|
||||||
|
.GetBytes(payload)
|
||||||
|
.Apply(hmacSha1.ComputeHash)
|
||||||
|
.Apply(Convert.ToBase64String);
|
||||||
|
|
||||||
|
return $"AWS {s3Key}:{signature}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,265 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using InnovEnergy.App.KacoCommunication.Devices;
|
||||||
|
using InnovEnergy.App.KacoCommunication.ESS;
|
||||||
|
using InnovEnergy.Lib.Devices.BatteryDeligreen;
|
||||||
|
using InnovEnergy.Lib.Units.Power;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.KacoCommunication;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ┌────┐
|
||||||
|
// │ Pv │
|
||||||
|
// └────┘
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// (i) 13.2 kW ┌────────────┐ ┌────────────┐ ┌────────────┐
|
||||||
|
// V │ Battery K1│ │ Battery K2│ │ Battery K3│
|
||||||
|
// ┌─────────┐ ┌─────────┐ V ├────────────┤ ├────────────┤ ├────────────┤
|
||||||
|
// │ Grid │ │ AC/DC │ ┌────────┐ ┌───────┐ │ 52.3 V │ │ 52.3 V │ │ 52.3 V │
|
||||||
|
// ├─────────┤ -10.3 kW├─────────┤ -11.7 kW │ Dc Bus │ 1008 W │ DC/DC │ 1008 W │ 99.1 % │ │ 99.1 % │ │ 99.1 % │
|
||||||
|
// │ -3205 W │<<<<<<<<<│ -6646 W │<<<<<<<<<<├────────┤>>>>>>>>>>├───────┤>>>>>>>>>>│ 490 mA │ │ 490 mA │ │ 490 mA │
|
||||||
|
// │ -3507 W │ (a) │ -5071 W │ (h) │ 776 V │ (k) │ 56 V │ (l) │ 250 °C │ │ 250 °C │ │ 250 °C │
|
||||||
|
// │ -3605 W │ └─────────┘ └────────┘ └───────┘ │ 445 A │ │ 445 A │ │ 445 A │
|
||||||
|
// └─────────┘ V │ │ │ │ │ │
|
||||||
|
// V │ │ │ │ │ │
|
||||||
|
// (j) 0 W └────────────┘ └────────────┘ └────────────┘
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// ┌──────┐
|
||||||
|
// │ Load │
|
||||||
|
// └──────┘
|
||||||
|
//
|
||||||
|
|
||||||
|
// New (simplified) topology:
|
||||||
|
//
|
||||||
|
// ┌────┐
|
||||||
|
// │ PV │
|
||||||
|
// └────┘
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// (i) 13.2 kW
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// ┌─────────┐ ┌─────────┐ (h) ┌────────┐ (k) ┌───────┐ (l) ┌────────────┐ ┌────────────┐ ┌────────────┐
|
||||||
|
// │ Grid │<<<│ AC/DC │<<<<<<<<<<<<<│ Dc Bus │>>>>>>>>│ DC/DC │>>>>>>>>│ Battery K1 │ │ Battery K2 │ │ Battery K3 │
|
||||||
|
// ├─────────┤ ├─────────┤ ├────────┤ ├───────┤ ├────────────┤ ├────────────┤ ├────────────┤
|
||||||
|
// │ -3205 W │ │ -6646 W │ │ 776 V │ │ 56 V │ │ 52.3 V ... │ │ 52.3 V ... │ │ 52.3 V ... │
|
||||||
|
// │ -3507 W │ │ -5071 W │ └────────┘ └───────┘ └────────────┘ └────────────┘ └────────────┘
|
||||||
|
// │ -3605 W │
|
||||||
|
// └─────────┘
|
||||||
|
//
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// (j) 0 W
|
||||||
|
// V
|
||||||
|
// V
|
||||||
|
// ┌──────┐
|
||||||
|
// │ Load │
|
||||||
|
// └──────┘
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// - (a) is grid power (to/from grid)
|
||||||
|
// - (h) is AC/DC -> DC link power (or your chosen link variable)
|
||||||
|
// - (i) PV -> DC bus
|
||||||
|
// - (j) DC load
|
||||||
|
// - (k) DC bus -> DC/DC link
|
||||||
|
// - (l) DC/DC -> battery power (or total battery power)
|
||||||
|
|
||||||
|
public static class SimpleTopology
|
||||||
|
{
|
||||||
|
public static TextBlock CreateSimpleTopologyTextBlock(this StatusRecord status)
|
||||||
|
{
|
||||||
|
// Keep the same variable letters as your diagrams (where possible)
|
||||||
|
var a = status.GridMeterRecord?.ActivePowerTotal;
|
||||||
|
|
||||||
|
// In your existing code, "AC/DC column" shows per-device AC power;
|
||||||
|
// and "h" is a separate link (AcDcToDcLink?.Power.Value).
|
||||||
|
var h = 0;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var j = 0;
|
||||||
|
|
||||||
|
var k = 0;
|
||||||
|
|
||||||
|
// You mentioned this changed: l is now equal total battery power
|
||||||
|
var l = status.BatteryKabinet1.Power;
|
||||||
|
|
||||||
|
var grid = status.CreateGridColumn(a);
|
||||||
|
var acdc = status.CreateAcDcColumn(h);
|
||||||
|
var dcBus = status.CreateDcBusColumn(i, j, k);
|
||||||
|
var dcdc = status.CreateDcDcColumn(l);
|
||||||
|
var batteries = status.CreateBatteriesRow();
|
||||||
|
|
||||||
|
return TextBlock.AlignCenterVertical(
|
||||||
|
grid,
|
||||||
|
acdc,
|
||||||
|
dcBus,
|
||||||
|
dcdc,
|
||||||
|
batteries
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateGridColumn(this StatusRecord status, ActivePower? a)
|
||||||
|
{
|
||||||
|
// ┌─────────┐
|
||||||
|
// │ Grid │
|
||||||
|
// ├─────────┤
|
||||||
|
// │ L1 P │
|
||||||
|
// │ L2 P │
|
||||||
|
// │ L3 P │
|
||||||
|
// └─────────┘ (a) flow to AC/DC
|
||||||
|
|
||||||
|
var gridMeterAc = status.GridMeterRecord;
|
||||||
|
|
||||||
|
var gridBox = TextBlock
|
||||||
|
.AlignLeft(
|
||||||
|
gridMeterAc?.ActivePowerL1.Value.ToString(CultureInfo.InvariantCulture) ?? "???",
|
||||||
|
gridMeterAc?.ActivePowerL2.Value.ToString(CultureInfo.InvariantCulture) ?? "???",
|
||||||
|
gridMeterAc?.ActivePowerL3.Value.ToString(CultureInfo.InvariantCulture) ?? "???"
|
||||||
|
)
|
||||||
|
.TitleBox("Grid");
|
||||||
|
|
||||||
|
// Flow from Grid to AC/DC in the picture is horizontal (left -> right), using <<<<<< for export/import.
|
||||||
|
// Your Flow.Horizontal(power) already handles arrow direction by sign (based on your existing outputs).
|
||||||
|
var flow = Flow.Horizontal(a);
|
||||||
|
|
||||||
|
return TextBlock.AlignCenterVertical(gridBox, flow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateAcDcColumn(this StatusRecord status, ActivePower? h)
|
||||||
|
{
|
||||||
|
// ┌─────────┐
|
||||||
|
// │ AC/DC │
|
||||||
|
// ├─────────┤
|
||||||
|
// │ dev1 P │
|
||||||
|
// │ dev2 P │
|
||||||
|
// └─────────┘ (h) flow to DC Bus
|
||||||
|
|
||||||
|
var acdcBox = TextBlock
|
||||||
|
.AlignLeft(status.InverterRecord?.ActivePowerSetPercent.ToString() ?? "???")
|
||||||
|
.TitleBox("AC/DC");
|
||||||
|
|
||||||
|
var flowToDcBus = Flow.Horizontal(h);
|
||||||
|
|
||||||
|
return TextBlock.AlignCenterVertical(acdcBox, flowToDcBus);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateDcBusColumn(
|
||||||
|
this StatusRecord status,
|
||||||
|
ActivePower? i,
|
||||||
|
ActivePower? j,
|
||||||
|
ActivePower? k)
|
||||||
|
{
|
||||||
|
// ┌────┐
|
||||||
|
// │ PV │
|
||||||
|
// └────┘
|
||||||
|
// V
|
||||||
|
// (i) 13.2 kW
|
||||||
|
// V
|
||||||
|
// ┌────────┐ (k) >>>>>>>>> to DC/DC
|
||||||
|
// │ Dc Bus │>>>>>>>>>>>>>>>>>>>
|
||||||
|
// ├────────┤
|
||||||
|
// │ 776 V │
|
||||||
|
// └────────┘
|
||||||
|
// V
|
||||||
|
// (j) 0 W
|
||||||
|
// V
|
||||||
|
// ┌──────┐
|
||||||
|
// │ Load │
|
||||||
|
// └──────┘
|
||||||
|
|
||||||
|
// PV box + vertical flow
|
||||||
|
var pvBox = TextBlock.FromString("PV").Box();
|
||||||
|
var pvToBus = Flow.Vertical(i);
|
||||||
|
|
||||||
|
// DC bus box (voltage from your DcDc record matches your existing code)
|
||||||
|
var dcBusVoltage = 0.0;
|
||||||
|
var dcBusBox = dcBusVoltage
|
||||||
|
.ToString(CultureInfo.InvariantCulture)
|
||||||
|
.Apply(TextBlock.FromString)
|
||||||
|
.TitleBox("Dc Bus");
|
||||||
|
|
||||||
|
// Horizontal flow from DC Bus to DC/DC
|
||||||
|
var busToDcDc = Flow.Horizontal(k);
|
||||||
|
|
||||||
|
// Load box + vertical flow
|
||||||
|
var busToLoad = Flow.Vertical(j);
|
||||||
|
var loadBox = TextBlock.FromString("Load").Box();
|
||||||
|
|
||||||
|
// Assemble: put PV above DC Bus, Load below DC Bus, and the (k) flow beside the bus.
|
||||||
|
return TextBlock.AlignCenterVertical(
|
||||||
|
TextBlock.AlignCenterHorizontal(pvBox, pvToBus, dcBusBox, busToLoad, loadBox),
|
||||||
|
busToDcDc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateDcDcColumn(this StatusRecord status, ActivePower? l)
|
||||||
|
{
|
||||||
|
// ┌───────┐
|
||||||
|
// │ DC/DC │
|
||||||
|
// ├───────┤
|
||||||
|
// │ 56 V │
|
||||||
|
// └───────┘ (l) flow to batteries
|
||||||
|
|
||||||
|
var dc48Voltage =0.0;
|
||||||
|
|
||||||
|
var dcdcBox = TextBlock
|
||||||
|
.AlignLeft(dc48Voltage)
|
||||||
|
.TitleBox("DC/DC");
|
||||||
|
|
||||||
|
var flowToBattery = Flow.Horizontal(l);
|
||||||
|
|
||||||
|
return TextBlock.AlignCenterVertical(dcdcBox, flowToBattery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateBatteriesRow(this StatusRecord status)
|
||||||
|
{
|
||||||
|
// Battery K1 | Battery K2 | Battery K3 (side-by-side)
|
||||||
|
// Each box: voltage, soc, current, temp, etc. (you can tailor)
|
||||||
|
|
||||||
|
var bat = status.BatteryKabinet1;
|
||||||
|
if (bat is null)
|
||||||
|
return TextBlock.AlignLeft("no battery").Box();
|
||||||
|
|
||||||
|
// If you actually have relay names K1/K2/K3 per battery, wire them here.
|
||||||
|
// For now we label by index as "Battery K{n}" to match your picture.
|
||||||
|
var boxes = bat.Devices
|
||||||
|
.Select((b, idx) => CreateBatteryKBox(b, idx))
|
||||||
|
.ToReadOnlyList();
|
||||||
|
|
||||||
|
// Align horizontally to match the diagram
|
||||||
|
return boxes.Any()
|
||||||
|
? TextBlock.AlignTop(boxes)
|
||||||
|
: TextBlock.AlignLeft("no battery devices").Box();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextBlock CreateBatteryKBox(BatteryDeligreenRecord battery, int idx)
|
||||||
|
{
|
||||||
|
// Minimal “K-style” battery box matching your diagram fields
|
||||||
|
var data = battery.BatteryDeligreenDataRecord;
|
||||||
|
|
||||||
|
// Some of your sample screen values look like:
|
||||||
|
// 52.3 V, 99.1 %, 490 mA, 250 °C, 445 A
|
||||||
|
// Map these to whatever fields you trust in your record.
|
||||||
|
var voltage = data.BusVoltage.ToDisplayString();
|
||||||
|
var soc = data.Soc.ToDisplayString();
|
||||||
|
var current = data.BusCurrent.ToDisplayString();
|
||||||
|
var temp = data.TemperaturesList.PowerTemperature.ToDisplayString();
|
||||||
|
|
||||||
|
// If you have a better “pack current” field, replace this line.
|
||||||
|
// Keeping it as a separate line to mimic the picture’s extra current-like line.
|
||||||
|
var extraCurrent = data.BusCurrent.ToDisplayString();
|
||||||
|
|
||||||
|
return TextBlock
|
||||||
|
.AlignLeft(
|
||||||
|
voltage,
|
||||||
|
soc,
|
||||||
|
current,
|
||||||
|
temp,
|
||||||
|
extraCurrent
|
||||||
|
)
|
||||||
|
.TitleBox($"Battery K{idx + 1}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
dotnet_version='net6.0'
|
||||||
|
salimax_ip="$1"
|
||||||
|
username='ie-entwicklung'
|
||||||
|
root_password='Salimax4x25'
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo -e "\n============================ Build ============================\n"
|
||||||
|
|
||||||
|
dotnet publish \
|
||||||
|
./KacoCommunication.csproj \
|
||||||
|
-p:PublishTrimmed=false \
|
||||||
|
-c Release \
|
||||||
|
-r linux-x64
|
||||||
|
|
||||||
|
echo -e "\n============================ Deploy ============================\n"
|
||||||
|
|
||||||
|
rsync -v \
|
||||||
|
--exclude '*.pdb' \
|
||||||
|
./bin/Release/$dotnet_version/linux-x64/publish/* \
|
||||||
|
$username@"$salimax_ip":~/salimax
|
||||||
|
|
@ -54,6 +54,7 @@ internal static class Program
|
||||||
private static readonly Channel RelaysTsChannel;
|
private static readonly Channel RelaysTsChannel;
|
||||||
private static readonly Channel BatteriesChannel;
|
private static readonly Channel BatteriesChannel;
|
||||||
|
|
||||||
|
private static readonly String SwVersionNumber = " V1.00." + DateTime.Today;
|
||||||
private static Boolean _curtailFlag = false;
|
private static Boolean _curtailFlag = false;
|
||||||
private const String VpnServerIp = "10.2.0.11";
|
private const String VpnServerIp = "10.2.0.11";
|
||||||
private static Boolean _subscribedToQueue = false;
|
private static Boolean _subscribedToQueue = false;
|
||||||
|
|
@ -247,34 +248,6 @@ internal static class Program
|
||||||
Watchdog.NotifyAlive();
|
Watchdog.NotifyAlive();
|
||||||
|
|
||||||
var record = ReadStatus();
|
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
|
SendSalimaxStateAlarm(GetSalimaxStateAlarm(record), record); // to improve
|
||||||
|
|
||||||
|
|
@ -518,12 +491,15 @@ internal static class Program
|
||||||
|
|
||||||
private static void ControlConstants(this StatusRecord r)
|
private static void ControlConstants(this StatusRecord r)
|
||||||
{
|
{
|
||||||
|
r.DcDc.ResetAlarms();// move this first to be sure it's executed
|
||||||
|
r.AcDc.ResetAlarms();// move this first to be sure it's executed
|
||||||
var inverters = r.AcDc.Devices;
|
var inverters = r.AcDc.Devices;
|
||||||
var dcDevices = r.DcDc.Devices;
|
var dcDevices = r.DcDc.Devices;
|
||||||
var configFile = r.Config;
|
var configFile = r.Config;
|
||||||
var maxBatteryDischargingCurrentLive = 0.0;
|
var maxBatteryDischargingCurrentLive = 0.0;
|
||||||
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
|
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
|
||||||
|
|
||||||
|
|
||||||
// This adapting the max discharging current to the current Active Strings
|
// This adapting the max discharging current to the current Active Strings
|
||||||
if (r.Battery != null)
|
if (r.Battery != null)
|
||||||
{
|
{
|
||||||
|
|
@ -572,8 +548,7 @@ internal static class Program
|
||||||
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = devicesConfig.DcDc.MinDischargeBatteryVoltage);
|
dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = devicesConfig.DcDc.MinDischargeBatteryVoltage);
|
||||||
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
|
dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop);
|
||||||
|
|
||||||
r.DcDc.ResetAlarms();
|
|
||||||
r.AcDc.ResetAlarms();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be used for provider throttling, this example is only for either 100% or 0 %
|
// This will be used for provider throttling, this example is only for either 100% or 0 %
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ public class CombinedAdamRelaysRecord : IRelaysRecord
|
||||||
set => _recordAdam6360D.K2ConnectIslandBusToGridBus = value;
|
set => _recordAdam6360D.K2ConnectIslandBusToGridBus = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean OldK2ConnectIslandBusToGridBus { get; set; }
|
||||||
|
|
||||||
public Boolean Inverter1WagoStatus => _recordAdam6360D.Inverter1WagoStatus;
|
public Boolean Inverter1WagoStatus => _recordAdam6360D.Inverter1WagoStatus;
|
||||||
public Boolean Inverter2WagoStatus => _recordAdam6360D.Inverter2WagoStatus;
|
public Boolean Inverter2WagoStatus => _recordAdam6360D.Inverter2WagoStatus;
|
||||||
public Boolean Inverter3WagoStatus => _recordAdam6360D.Inverter3WagoStatus;
|
public Boolean Inverter3WagoStatus => _recordAdam6360D.Inverter3WagoStatus;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ public interface IRelaysRecord
|
||||||
Boolean FiWarning { get; }
|
Boolean FiWarning { get; }
|
||||||
Boolean FiError { get; }
|
Boolean FiError { get; }
|
||||||
Boolean K2ConnectIslandBusToGridBus { get; set; }
|
Boolean K2ConnectIslandBusToGridBus { get; set; }
|
||||||
|
Boolean OldK2ConnectIslandBusToGridBus { get; set; }
|
||||||
|
|
||||||
// Boolean Inverter1WagoRelay { get; set; } // to add in the future
|
// Boolean Inverter1WagoRelay { get; set; } // to add in the future
|
||||||
// Boolean Inverter2WagoRelay { get; set; } // to add in the future
|
// Boolean Inverter2WagoRelay { get; set; } // to add in the future
|
||||||
|
|
|
||||||
|
|
@ -122,12 +122,17 @@ public class RelaysRecordAmax : IRelaysRecord
|
||||||
public Boolean FiWarning => !_Regs.DigitalInput21;
|
public Boolean FiWarning => !_Regs.DigitalInput21;
|
||||||
public Boolean FiError => !_Regs.DigitalInput23;
|
public Boolean FiError => !_Regs.DigitalInput23;
|
||||||
|
|
||||||
public Boolean K2ConnectIslandBusToGridBus
|
public Boolean K2ConnectIslandBusToGridBus // IMPORTANT: this should be changed to Relay22 for Tschirren installation
|
||||||
{
|
{
|
||||||
get => _Regs.Relay23;
|
get => _Regs.Relay23;
|
||||||
set => _Regs.Relay23 = value;
|
set => _Regs.Relay23 = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean OldK2ConnectIslandBusToGridBus
|
||||||
|
{
|
||||||
|
get => _Regs.Relay23;
|
||||||
|
set => _Regs.Relay23 = value;
|
||||||
|
}
|
||||||
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._Regs;
|
public static implicit operator Amax5070Registers(RelaysRecordAmax d) => d._Regs;
|
||||||
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
|
public static implicit operator RelaysRecordAmax(Amax5070Registers d) => new RelaysRecordAmax(d);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,12 @@ public class Configuration
|
||||||
public Double MaximumChargingCurrent { get; set; }
|
public Double MaximumChargingCurrent { get; set; }
|
||||||
public WorkingMode OperatingPriority { get; set; }
|
public WorkingMode OperatingPriority { get; set; }
|
||||||
public Int16 BatteriesCount { get; set; }
|
public Int16 BatteriesCount { get; set; }
|
||||||
|
public Int16 ClusterNumber { get; set; }
|
||||||
|
public Int16 PvNumber { get; set; }
|
||||||
|
public DateTime StartTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
public DateTime StopTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
public Single TimeChargeandDischargePower { get; set; }
|
||||||
|
public Boolean ControlPermission { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,10 @@ public static class MiddlewareAgent
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Received a configuration message: " +
|
Console.WriteLine($"Received a configuration message: " +
|
||||||
"MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount + config.MaximumChargingCurrent + config.MaximumDischargingCurrent);
|
"MinimumSoC is " + config.MinimumSoC + " and operating priorty is " +config.OperatingPriority + "Number of batteries is " + config.BatteriesCount
|
||||||
|
+ "Maximum Charging current is "+ config.MaximumChargingCurrent + "/n" + "Maximum Discharging current is " + config.MaximumDischargingCurrent
|
||||||
|
+ "StartTimeChargeandDischargeDayandTime is" + config.StartTimeChargeandDischargeDayandTime + "StopTimeChargeandDischargeDayandTime is" + config.StopTimeChargeandDischargeDayandTime
|
||||||
|
+ "TimeChargeandDischargePowert is " + config.TimeChargeandDischargePower + " Control permission is" + config.ControlPermission);
|
||||||
|
|
||||||
// Send the reply to the sender's endpoint
|
// Send the reply to the sender's endpoint
|
||||||
_udpListener.Send(replyData, replyData.Length, serverEndpoint);
|
_udpListener.Send(replyData, replyData.Length, serverEndpoint);
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,11 @@ using RabbitMQ.Client;
|
||||||
namespace InnovEnergy.App.SinexcelCommunication.MiddlewareClasses;
|
namespace InnovEnergy.App.SinexcelCommunication.MiddlewareClasses;
|
||||||
|
|
||||||
|
|
||||||
[SuppressMessage("Trimming", "IL2026:Members annotated with \'RequiresUnreferencedCodeAttribute\' require dynamic access otherwise can break functionality when trimming application code")]
|
|
||||||
public static class RabbitMqManager
|
public static class RabbitMqManager
|
||||||
{
|
{
|
||||||
public static ConnectionFactory? Factory ;
|
private static ConnectionFactory? _factory ;
|
||||||
public static IConnection ? Connection;
|
private static IConnection ? _connection;
|
||||||
public static IModel? Channel;
|
private static IModel? _channel;
|
||||||
|
|
||||||
public static Boolean SubscribeToQueue(StatusMessage currentSalimaxState, String? s3Bucket,String VpnServerIp)
|
public static Boolean SubscribeToQueue(StatusMessage currentSalimaxState, String? s3Bucket,String VpnServerIp)
|
||||||
{
|
{
|
||||||
|
|
@ -20,19 +19,22 @@ public static class RabbitMqManager
|
||||||
{
|
{
|
||||||
//_factory = new ConnectionFactory { HostName = VpnServerIp };
|
//_factory = new ConnectionFactory { HostName = VpnServerIp };
|
||||||
|
|
||||||
Factory = new ConnectionFactory
|
_factory = new ConnectionFactory
|
||||||
{
|
{
|
||||||
HostName = VpnServerIp,
|
HostName = VpnServerIp,
|
||||||
Port = 5672,
|
Port = 5672,
|
||||||
VirtualHost = "/",
|
VirtualHost = "/",
|
||||||
UserName = "producer",
|
UserName = "producer",
|
||||||
Password = "b187ceaddb54d5485063ddc1d41af66f",
|
Password = "b187ceaddb54d5485063ddc1d41af66f",
|
||||||
|
AutomaticRecoveryEnabled = true,
|
||||||
|
NetworkRecoveryInterval = TimeSpan.FromSeconds(10),
|
||||||
|
RequestedHeartbeat = TimeSpan.FromSeconds(30)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Connection = Factory.CreateConnection();
|
_connection = _factory.CreateConnection();
|
||||||
Channel = Connection.CreateModel();
|
_channel = _connection.CreateModel();
|
||||||
Channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
|
_channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
Console.WriteLine("The controller sends its status to the middleware for the first time");
|
Console.WriteLine("The controller sends its status to the middleware for the first time");
|
||||||
if (s3Bucket != null) InformMiddleware(currentSalimaxState);
|
if (s3Bucket != null) InformMiddleware(currentSalimaxState);
|
||||||
|
|
@ -52,7 +54,7 @@ public static class RabbitMqManager
|
||||||
var message = JsonSerializer.Serialize(status);
|
var message = JsonSerializer.Serialize(status);
|
||||||
var body = Encoding.UTF8.GetBytes(message);
|
var body = Encoding.UTF8.GetBytes(message);
|
||||||
|
|
||||||
Channel.BasicPublish(exchange: string.Empty,
|
_channel.BasicPublish(exchange: string.Empty,
|
||||||
routingKey: "statusQueue",
|
routingKey: "statusQueue",
|
||||||
basicProperties: null,
|
basicProperties: null,
|
||||||
body: body);
|
body: body);
|
||||||
|
|
@ -60,5 +62,28 @@ public static class RabbitMqManager
|
||||||
Console.WriteLine($"Producer sent message: {message}");
|
Console.WriteLine($"Producer sent message: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Boolean EnsureConnected(StatusMessage currentSalimaxState, string? s3Bucket, string vpnServerIp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_connection == null || !_connection.IsOpen ||
|
||||||
|
_channel == null || _channel.IsClosed)
|
||||||
|
{
|
||||||
|
Console.WriteLine("⚠ RabbitMQ connection lost. Reconnecting...");
|
||||||
|
|
||||||
|
_connection?.Dispose();
|
||||||
|
_channel?.Dispose();
|
||||||
|
|
||||||
|
return SubscribeToQueue(currentSalimaxState, s3Bucket, vpnServerIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("❌ Error while ensuring RabbitMQ connection: " + ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,15 @@ public class Config
|
||||||
public required Double MaximumChargingCurrent { get; set; }
|
public required Double MaximumChargingCurrent { get; set; }
|
||||||
public required WorkingMode OperatingPriority { get; set; }
|
public required WorkingMode OperatingPriority { get; set; }
|
||||||
public required Int16 BatteriesCount { get; set; }
|
public required Int16 BatteriesCount { get; set; }
|
||||||
|
public required Int16 ClusterNumber { get; set; }
|
||||||
|
public required Int16 PvNumber { get; set; }
|
||||||
public required Double ModbusProtcolNumber { get; set; }
|
public required Double ModbusProtcolNumber { get; set; }
|
||||||
|
public required DateTime StartTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
public required DateTime StopTimeChargeandDischargeDayandTime { get; set; }
|
||||||
|
|
||||||
|
public required Single TimeChargeandDischargePower { get; set; }
|
||||||
|
public required Boolean ControlPermission { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public required S3Config? S3 { get; set; }
|
public required S3Config? S3 { get; set; }
|
||||||
|
|
||||||
|
|
@ -36,7 +44,14 @@ public class Config
|
||||||
MaximumDischargingCurrent = 180,
|
MaximumDischargingCurrent = 180,
|
||||||
OperatingPriority = WorkingMode.TimeChargeDischarge,
|
OperatingPriority = WorkingMode.TimeChargeDischarge,
|
||||||
BatteriesCount = 0,
|
BatteriesCount = 0,
|
||||||
|
ClusterNumber = 0,
|
||||||
|
PvNumber = 0,
|
||||||
ModbusProtcolNumber = 1.2,
|
ModbusProtcolNumber = 1.2,
|
||||||
|
StartTimeChargeandDischargeDayandTime = DefaultDatetime,
|
||||||
|
StopTimeChargeandDischargeDayandTime = DefaultDatetime,
|
||||||
|
TimeChargeandDischargePower = 3,
|
||||||
|
ControlPermission = false,
|
||||||
|
|
||||||
S3 = new()
|
S3 = new()
|
||||||
{
|
{
|
||||||
Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
Bucket = "1-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
||||||
|
|
|
||||||
|
|
@ -28,5 +28,5 @@ rsync -v \
|
||||||
echo -e "\n✅ Real release. Triggering sync to server..."
|
echo -e "\n✅ Real release. Triggering sync to server..."
|
||||||
touch "$release_flag_file"
|
touch "$release_flag_file"
|
||||||
else
|
else
|
||||||
echo -e "\n Test build. Not syncing to main release server."
|
echo -e "\n🚫 Test build. Not syncing to main release server."
|
||||||
fi
|
fi
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
WATCHDIR="$HOME/sync/work/Code/CSharp/git_trunk/csharp/App/SinexcelCommunication/bin/Release/net6.0/linux-arm64/publish"
|
||||||
|
DEST="ubuntu@91.92.155.224:/home/ubuntu/SinexcelReleases"
|
||||||
|
|
||||||
|
echo "👀 Watching for real releases in $WATCHDIR..."
|
||||||
|
|
||||||
|
inotifywait -m -e close_write --format '%w%f' "$WATCHDIR" | while read file; do
|
||||||
|
filename="$(basename "$file")"
|
||||||
|
|
||||||
|
if [[ "$filename" == ".release.flag" ]]; then
|
||||||
|
echo "🚀 Release flag detected. Syncing full release to $DEST..."
|
||||||
|
|
||||||
|
rm "$file"
|
||||||
|
rsync -avz \
|
||||||
|
--exclude '*.pdb' \
|
||||||
|
"$WATCHDIR/" "$DEST/"
|
||||||
|
|
||||||
|
echo "✅ Sync completed and flag cleared."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
@ -58,7 +58,7 @@ internal static class Program
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
private const String SwVersionNumber =" V1.35.220925 beta";
|
private static readonly String SwVersionNumber = " V1.00." + DateTime.Today;
|
||||||
private static Boolean _curtailFlag = false;
|
private static Boolean _curtailFlag = false;
|
||||||
private const String VpnServerIp = "10.2.0.11";
|
private const String VpnServerIp = "10.2.0.11";
|
||||||
private static Boolean _subscribedToQueue = false;
|
private static Boolean _subscribedToQueue = false;
|
||||||
|
|
@ -281,7 +281,7 @@ internal static class Program
|
||||||
//record.PerformLed();
|
//record.PerformLed();
|
||||||
|
|
||||||
WriteControl(record);
|
WriteControl(record);
|
||||||
record.Battery.Eoc.WriteLine(" Total EOC");
|
record.Battery?.Eoc.WriteLine(" Total EOC");
|
||||||
|
|
||||||
$"{record.StateMachine.State}: {record.StateMachine.Message}".WriteLine();
|
$"{record.StateMachine.State}: {record.StateMachine.Message}".WriteLine();
|
||||||
$"{DateTime.Now.Round(UpdateInterval).ToUnixTime()} : {DateTime.Now.Round(UpdateInterval):dd/MM/yyyy HH:mm:ss} : { SwVersionNumber}".WriteLine();
|
$"{DateTime.Now.Round(UpdateInterval).ToUnixTime()} : {DateTime.Now.Round(UpdateInterval):dd/MM/yyyy HH:mm:ss} : { SwVersionNumber}".WriteLine();
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ namespace InnovEnergy.Lib.Devices.Amax5070
|
||||||
|
|
||||||
[Coil(25)] public Boolean Relay20 { get; set; } // Relay5060 2
|
[Coil(25)] public Boolean Relay20 { get; set; } // Relay5060 2
|
||||||
[Coil(26)] public Boolean Relay21 { get; set; } // Relay5060 2
|
[Coil(26)] public Boolean Relay21 { get; set; } // Relay5060 2
|
||||||
[Coil(27)] public Boolean Relay22 { get; set; } // Relay5060 2
|
[Coil(27, writable: true)] public Boolean Relay22 { get; set; }
|
||||||
[Coil(28)] public Boolean Relay23 { get; set; } // Relay5060 2
|
[Coil(28)] public Boolean Relay23 { get; set; }
|
||||||
|
|
||||||
[Coil(33)] public Boolean Relay30 { get; set; } // Relay5060 3
|
[Coil(33)] public Boolean Relay30 { get; set; } // Relay5060 3
|
||||||
[Coil(34)] public Boolean Relay31 { get; set; } // Relay5060 3
|
[Coil(34)] public Boolean Relay31 { get; set; } // Relay5060 3
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
public enum ControlMode
|
||||||
|
{
|
||||||
|
RpcLocal = 0,
|
||||||
|
RpcRemote = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum EnableDisableEnum : ushort
|
||||||
|
{
|
||||||
|
Disabled = 0,
|
||||||
|
Enabled = 1
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
public enum CurrentState
|
||||||
|
{
|
||||||
|
Off = 1,
|
||||||
|
Sleeping = 2,
|
||||||
|
Starting = 3,
|
||||||
|
Mppt = 4,
|
||||||
|
Throttled = 5,
|
||||||
|
ShuttingDown = 6,
|
||||||
|
Fault = 7,
|
||||||
|
Standby = 8,
|
||||||
|
Precharge = 9,
|
||||||
|
GridPreConnected = 10,
|
||||||
|
GridConnected = 11,
|
||||||
|
NoErrorPending = 12
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
public enum ErrorPcu
|
||||||
|
{
|
||||||
|
NoEvent = 0,
|
||||||
|
OverTemp = 1,
|
||||||
|
OverVolt = 2,
|
||||||
|
UnderVolt = 3,
|
||||||
|
BattPolIncorrect = 4,
|
||||||
|
CounterTooHigh = 5,
|
||||||
|
DuringPrecharge = 6,
|
||||||
|
BattVoltOutOfRange = 7,
|
||||||
|
I2CComm = 8,
|
||||||
|
CanComm = 9,
|
||||||
|
SwitchOffAcDsp = 10,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
public enum ReuqestedState
|
||||||
|
{
|
||||||
|
Off = 1,
|
||||||
|
Standby = 8,
|
||||||
|
GridPreConnected = 10,
|
||||||
|
GridConnected = 11,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
public enum StatePcu
|
||||||
|
{
|
||||||
|
WaitForStartup = 1,
|
||||||
|
Standby = 2,
|
||||||
|
SwitchRelMinus = 3,
|
||||||
|
SwitchRelPrecharge = 4,
|
||||||
|
SwitchRelPlus = 5,
|
||||||
|
Running = 6,
|
||||||
|
Cooldown = 7,
|
||||||
|
Error = 8,
|
||||||
|
ClearError = 9,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>InnovEnergy.Lib.Devices.Kaco92L3;</RootNamespace>
|
||||||
|
<LangVersion>preview</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Protocols\Modbus\Modbus.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Units\Units.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Utils\Utils.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Slaves;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3;
|
||||||
|
|
||||||
|
|
||||||
|
public class KacoDevice: ModbusDevice<KacoRecord>
|
||||||
|
{
|
||||||
|
public KacoDevice(String hostname, UInt16 port = 502, Byte slaveId = 1) : this(new TcpChannel(hostname, port), slaveId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public KacoDevice(Channel channel, Byte slaveId = 1) : base(new ModbusTcpClient(channel, slaveId))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public KacoDevice(ModbusClient client) : base(client)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public new KacoRecord? Read()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.Read();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
"Failed to read data from Kaco".WriteLine();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public new void Write(KacoRecord registers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
base.Write(registers);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// TODO: Log
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
using InnovEnergy.Lib.Devices.Kaco92L3.DataType;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3;
|
||||||
|
|
||||||
|
public partial class KacoRecord
|
||||||
|
{
|
||||||
|
|
||||||
|
private static float ScaleSunspec(Int16 value, Int16 sf)
|
||||||
|
{
|
||||||
|
// -32768 is SunSpec "not implemented"
|
||||||
|
if (value == -32768)
|
||||||
|
return float.NaN;
|
||||||
|
|
||||||
|
if (sf == 0)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
return (float)(value * Math.Pow(10, sf));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float ScaleSunspec(UInt16 value, Int16 sf)
|
||||||
|
{
|
||||||
|
// interpret unsigned as signed when applying SF; range is the same
|
||||||
|
return ScaleSunspec(unchecked((Int16)value), sf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Int16 UnscaleSunspecToInt16(float value, Int16 sf)
|
||||||
|
{
|
||||||
|
if (float.IsNaN(value) || float.IsInfinity(value))
|
||||||
|
return -32768; // "not implemented" / invalid
|
||||||
|
|
||||||
|
if (sf == 0)
|
||||||
|
return (Int16)Math.Round(value);
|
||||||
|
|
||||||
|
var raw = value / (float)Math.Pow(10, sf);
|
||||||
|
return (Int16)Math.Round(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UInt16 UnscaleSunspecToUInt16(float value, Int16 sf)
|
||||||
|
{
|
||||||
|
var raw = UnscaleSunspecToInt16(value, sf);
|
||||||
|
return unchecked((UInt16)raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************** High-level API for Model 64201 ****************************/
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// States & control
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Header
|
||||||
|
public UInt16 BattCharId => _battCharId; // ID = 64202
|
||||||
|
public UInt16 BattCharLength => _battCharLength; // L = 6 + (RBCount * 8)
|
||||||
|
|
||||||
|
public ReuqestedState RequestedState
|
||||||
|
{
|
||||||
|
get => (ReuqestedState)_requestedState;
|
||||||
|
set => _requestedState = (UInt16)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CurrentState CurrentState => (CurrentState)_currentState;
|
||||||
|
|
||||||
|
public ControlMode ControlMode
|
||||||
|
{
|
||||||
|
get => (ControlMode)_controlMode;
|
||||||
|
set => _controlMode = (UInt16)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsGridConnected => CurrentState == CurrentState.GridConnected;
|
||||||
|
public bool IsStandby => CurrentState == CurrentState.Standby;
|
||||||
|
public bool IsOff => CurrentState == CurrentState.Off;
|
||||||
|
|
||||||
|
// Watchdog seconds (no scale factor)
|
||||||
|
public UInt16 WatchdogSeconds
|
||||||
|
{
|
||||||
|
get => _watchdog;
|
||||||
|
set => _watchdog = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Setpoints (scaled)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Active power setpoint in percent of WMax [%].</summary>
|
||||||
|
public float ActivePowerSetPercent
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_wSetPct, _wSetPctSf);
|
||||||
|
set => _wSetPct = UnscaleSunspecToInt16(value, _wSetPctSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reactive power setpoint in percent of SMax [%].</summary>
|
||||||
|
public float ReactivePowerSetPercent
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_varWMaxSetPct, _varSetPctSf);
|
||||||
|
set => _varWMaxSetPct = UnscaleSunspecToInt16(value, _varSetPctSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Ramp parameters (scaled)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Active power PT1 ramp time [s].</summary>
|
||||||
|
public float ActivePowerRampTimeSeconds
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_wParamRmpTms, _rmpTmsSf);
|
||||||
|
set => _wParamRmpTms = UnscaleSunspecToUInt16(value, _rmpTmsSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Active power ramp-down rate [% ref / min].</summary>
|
||||||
|
public float ActivePowerRampDownPercentPerMin
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_wParamRmpDecTmn, _rmpIncDecSf);
|
||||||
|
set => _wParamRmpDecTmn = UnscaleSunspecToUInt16(value, _rmpIncDecSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Active power ramp-up rate [% ref / min].</summary>
|
||||||
|
public float ActivePowerRampUpPercentPerMin
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_wParamRmpIncTmn, _rmpIncDecSf);
|
||||||
|
set => _wParamRmpIncTmn = UnscaleSunspecToUInt16(value, _rmpIncDecSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reactive power PT1 ramp time [s].</summary>
|
||||||
|
public float ReactivePowerRampTimeSeconds
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_varParamRmpTms, _rmpTmsSf);
|
||||||
|
set => _varParamRmpTms = UnscaleSunspecToUInt16(value, _rmpTmsSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reactive power ramp-down rate [% ref / min].</summary>
|
||||||
|
public float ReactivePowerRampDownPercentPerMin
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_varParamRmpDecTmn, _rmpIncDecSf);
|
||||||
|
set => _varParamRmpDecTmn = UnscaleSunspecToUInt16(value, _rmpIncDecSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reactive power ramp-up rate [% ref / min].</summary>
|
||||||
|
public float ReactivePowerRampUpPercentPerMin
|
||||||
|
{
|
||||||
|
get => ScaleSunspec(_varParamRmpIncTmn, _rmpIncDecSf);
|
||||||
|
set => _varParamRmpIncTmn = UnscaleSunspecToUInt16(value, _rmpIncDecSf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ramp enable flags
|
||||||
|
public EnableDisableEnum ActivePowerRampEnable
|
||||||
|
{
|
||||||
|
get => (EnableDisableEnum)_wParamEna;
|
||||||
|
set => _wParamEna = (UInt16)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnableDisableEnum ReactivePowerRampEnable
|
||||||
|
{
|
||||||
|
get => (EnableDisableEnum)_varParamEna;
|
||||||
|
set => _varParamEna = (UInt16)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Status / error (read-only enum views)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
//public VendorStateEnum VendorState => (VendorStateEnum)_stVnd;
|
||||||
|
//public PuStateEnum PuState => (PuStateEnum)_stPu;
|
||||||
|
public StatePcu PcuState => (StatePcu)_stPcu;
|
||||||
|
public ErrorPcu PcuError => (ErrorPcu)_errPcu;
|
||||||
|
|
||||||
|
public UInt16 BatteryCharVersion => _battCharVersion;
|
||||||
|
public UInt16 BatteryCharMinorVersion => _battCharVerMinor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scale factor for battery voltages (V_SF).
|
||||||
|
/// </summary>
|
||||||
|
public Int16 BatteryVoltageScaleFactor => _battCharVSf;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scale factor for battery currents (A_SF).
|
||||||
|
/// </summary>
|
||||||
|
public Int16 BatteryCurrentScaleFactor => _battCharASf;
|
||||||
|
|
||||||
|
// Helper wrappers for scaled values
|
||||||
|
private float ScaleBattVoltage(UInt16 raw) => ScaleSunspec(raw, _battCharVSf);
|
||||||
|
private float ScaleBattCurrent(UInt16 raw) => ScaleSunspec(raw, _battCharASf);
|
||||||
|
private UInt16 UnscaleBattVoltage(float value) => UnscaleSunspecToUInt16(value, _battCharVSf);
|
||||||
|
private UInt16 UnscaleBattCurrent(float value) => UnscaleSunspecToUInt16(value, _battCharASf);
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Battery discharge limits (scaled, RW)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Minimum discharge voltage [V].</summary>
|
||||||
|
public float MinDischargeVoltage
|
||||||
|
{
|
||||||
|
get => ScaleBattVoltage(_disMinVRaw);
|
||||||
|
set => _disMinVRaw = UnscaleBattVoltage(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Maximum discharge current [A].</summary>
|
||||||
|
public float MaxDischargeCurrent
|
||||||
|
{
|
||||||
|
get => ScaleBattCurrent(_disMaxARaw);
|
||||||
|
set => _disMaxARaw = UnscaleBattCurrent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Discharge cutoff current [A]. If discharge current falls below this, it disconnects (optional according to sheet).</summary>
|
||||||
|
public float DischargeCutoffCurrent
|
||||||
|
{
|
||||||
|
get => ScaleBattCurrent(_disCutoffARaw);
|
||||||
|
set => _disCutoffARaw = UnscaleBattCurrent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Battery charge limits (scaled, RW)
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>Maximum charge voltage [V].</summary>
|
||||||
|
public float MaxChargeVoltage
|
||||||
|
{
|
||||||
|
get => ScaleBattVoltage(_chaMaxVRaw);
|
||||||
|
set => _chaMaxVRaw = UnscaleBattVoltage(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Maximum charge current [A].</summary>
|
||||||
|
public float MaxChargeCurrent
|
||||||
|
{
|
||||||
|
get => ScaleBattCurrent(_chaMaxARaw);
|
||||||
|
set => _chaMaxARaw = UnscaleBattCurrent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Charge cutoff current [A]. If charge current falls below this, it disconnects.</summary>
|
||||||
|
public float ChargeCutoffCurrent
|
||||||
|
{
|
||||||
|
get => ScaleBattCurrent(_chaCutoffARaw);
|
||||||
|
set => _chaCutoffARaw = UnscaleBattCurrent(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Limit enable flag
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When EnLimit = 1, new battery limits are activated.
|
||||||
|
/// </summary>
|
||||||
|
public EnableDisableEnum BatteryLimitsEnable
|
||||||
|
{
|
||||||
|
get => (EnableDisableEnum)_enLimitRaw;
|
||||||
|
set => _enLimitRaw = (UInt16)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Convenience bool wrapper for EnLimit.</summary>
|
||||||
|
public bool BatteryLimitsEnabled
|
||||||
|
{
|
||||||
|
get => BatteryLimitsEnable == EnableDisableEnum.Enabled;
|
||||||
|
set => BatteryLimitsEnable = value ? EnableDisableEnum.Enabled : EnableDisableEnum.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
|
using InnovEnergy.Lib.Units.Power;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.Kaco92L3;
|
||||||
|
|
||||||
|
[AddressOffset(-1)]
|
||||||
|
public partial class KacoRecord
|
||||||
|
{
|
||||||
|
/****************************** Holding registers – SunSpec Model 64201 ****************************/
|
||||||
|
|
||||||
|
// Model 64201 header
|
||||||
|
[HoldingRegister<UInt16>(41061)] private UInt16 _model64201Id; // 0xA065 (DID = 64201)
|
||||||
|
[HoldingRegister<UInt16>(41062)] private UInt16 _model64201Length; // 0xA066 (L = 52)
|
||||||
|
|
||||||
|
// Version info
|
||||||
|
[HoldingRegister<UInt16>(41063)] private UInt16 _version; // 0xA067
|
||||||
|
[HoldingRegister<UInt16>(41064)] private UInt16 _versionMinor; // 0xA068
|
||||||
|
|
||||||
|
// State control
|
||||||
|
[HoldingRegister<UInt16>(41065, writable: true)] private UInt16 _requestedState; // 0xA069 – RequestedState (enum16, RW)
|
||||||
|
[HoldingRegister<UInt16>(41066)] private UInt16 _currentState; // 0xA06A – CurrentState (enum16, R)
|
||||||
|
[HoldingRegister<UInt16>(41067, writable: true)] private UInt16 _controlMode; // 0xA06B – ControlMode (enum16, RW)
|
||||||
|
[HoldingRegister<UInt16>(41068)] private UInt16 _reserved7; // 0xA06C – Reserved
|
||||||
|
|
||||||
|
// Watchdog / setpoints
|
||||||
|
//Enable Watchdog countdown. Register must be filled with the desired watchdog timeout in seconds. 0 means watchdog is disabled. It is recommended to re-write the register at least 10 seconds before the timeout is activated.
|
||||||
|
[HoldingRegister<UInt16>(41069, writable: true)] private UInt16 _watchdog; // 0xA06D – Watchdog (uint16, RW, seconds) 0 to 600
|
||||||
|
[HoldingRegister<Int16> (41070, writable: true)] private Int16 _wSetPct; // 0xA06E – WSetPct (int16, RW, %WMax) unscaled: -100 to 100. Set power output to specified level.
|
||||||
|
[HoldingRegister<Int16> (41071, writable: true)] private Int16 _varWMaxSetPct; // 0xA06F – VarWMaxSetPct (int16, RW, %SMax) unscaled: -100 to 100
|
||||||
|
|
||||||
|
// Reserved padding
|
||||||
|
[HoldingRegister<UInt16>(41072)] private UInt16 _reserved11; // 0xA070
|
||||||
|
[HoldingRegister<UInt16>(41073)] private UInt16 _reserved12; // 0xA071
|
||||||
|
[HoldingRegister<UInt16>(41074)] private UInt16 _reserved13; // 0xA072
|
||||||
|
[HoldingRegister<UInt16>(41075)] private UInt16 _reserved14; // 0xA073
|
||||||
|
[HoldingRegister<UInt16>(41076)] private UInt16 _reserved15; // 0xA074
|
||||||
|
[HoldingRegister<UInt16>(41077)] private UInt16 _reserved16; // 0xA075
|
||||||
|
|
||||||
|
// Status / error
|
||||||
|
[HoldingRegister<UInt16>(41078)] private UInt16 _stVnd; // 0xA076 – StVnd (enum16, R) PrologState
|
||||||
|
[HoldingRegister<UInt16>(41079)] private UInt16 _stPu; // 0xA077 – StPu (enum16, R) Power Unit State (DSP)
|
||||||
|
[HoldingRegister<UInt16>(41080)] private UInt16 _stPcu; // 0xA078 – StPcu (enum16, R) pcu state
|
||||||
|
[HoldingRegister<UInt16>(41081)] private UInt16 _errPcu; // 0xA079 – ErrPcu (enum16, R) pcu error
|
||||||
|
|
||||||
|
// Active power ramp parameters
|
||||||
|
[HoldingRegister<UInt16>(41082, writable: true)] private UInt16 _wParamRmpTms; // 0xA07A – WParamRmpTms (uint16, RW, s)
|
||||||
|
[HoldingRegister<UInt16>(41083, writable: true)] private UInt16 _wParamRmpDecTmn; // 0xA07B – WParamRmpDecTmn (uint16, RW, %ref/min)
|
||||||
|
[HoldingRegister<UInt16>(41084, writable: true)] private UInt16 _wParamRmpIncTmn; // 0xA07C – WParamRmpIncTmn (uint16, RW, %ref/min)
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(41085)] private UInt16 _reserved24; // 0xA07D – Reserved
|
||||||
|
[HoldingRegister<UInt16>(41086)] private UInt16 _reserved25; // 0xA07E – Reserved
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(41087, writable: true)] private UInt16 _wParamEna; // 0xA07F – WParam_Ena (enum16, RW) WSet_Ena control 0 or 1
|
||||||
|
|
||||||
|
// Reactive power ramp parameters
|
||||||
|
[HoldingRegister<UInt16>(41088, writable: true)] private UInt16 _varParamRmpTms; // 0xA080 – VarParamRmpTms (uint16, RW, s)
|
||||||
|
[HoldingRegister<UInt16>(41089, writable: true)] private UInt16 _varParamRmpDecTmn; // 0xA081 – VarParamRmpDecTmn (uint16, RW, %ref/min)
|
||||||
|
[HoldingRegister<UInt16>(41090, writable: true)] private UInt16 _varParamRmpIncTmn; // 0xA082 – VarParamRmpIncTmn (uint16, RW, %ref/min)
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(41091)] private UInt16 _reserved30; // 0xA083 – Reserved
|
||||||
|
[HoldingRegister<UInt16>(41092)] private UInt16 _reserved31; // 0xA084 – Reserved
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(41093, writable: true)] private UInt16 _varParamEna; // 0xA085 – VarParam_Ena (enum16, RW) Enumerated valued. Percent limit VAr enable/disable control.
|
||||||
|
|
||||||
|
// Measurements (read-only)
|
||||||
|
[HoldingRegister<UInt16>(41094)] private UInt16 _phaseVoltageAN; // 0xA086 – PhVphA (uint16, R, V, V_SF)
|
||||||
|
[HoldingRegister<UInt16>(41095)] private UInt16 _phaseVoltageBN; // 0xA087 – PhVphB (uint16, R, V, V_SF)
|
||||||
|
[HoldingRegister<UInt16>(41096)] private UInt16 _phaseVoltageCN; // 0xA088 – PhVphC (uint16, R, V, V_SF)
|
||||||
|
|
||||||
|
[HoldingRegister<Int16> (41097)] private Int16 _activePowerW; // 0xA089 – W (int16, R, W, W_SF)
|
||||||
|
[HoldingRegister<Int16> (41098)] private Int16 _reactivePowerVar; // 0xA08A – VAR (int16, R, var, Var_SF)
|
||||||
|
[HoldingRegister<Int16> (41099)] private Int16 _lineFrequencyHz; // 0xA08B – Hz (int16, R, Hz, Hz_SF)
|
||||||
|
|
||||||
|
// Scale factors (SunSpec sunsf)
|
||||||
|
// Scale factor for active power percent.
|
||||||
|
[HoldingRegister<Int16>(41107)] private Int16 _wSetPctSf; // 0xA0F3 – WSetPct_SF
|
||||||
|
// Scale factor for reactive power percent.
|
||||||
|
[HoldingRegister<Int16>(41108)] private Int16 _varSetPctSf; // 0xA0F4 – VarSetPct_SF
|
||||||
|
// Scale factor for PT1 (ramp time).
|
||||||
|
[HoldingRegister<Int16>(41109)] private Int16 _rmpTmsSf; // 0xA0F5 – RmpTms_SF
|
||||||
|
// Scale factor for increment and decrement ramps.
|
||||||
|
[HoldingRegister<Int16>(41110)] private Int16 _rmpIncDecSf; // 0xA0F6 – RmpIncDec_SF
|
||||||
|
|
||||||
|
// Header
|
||||||
|
[HoldingRegister<UInt16>(41115)] private UInt16 _battCharId; // ID = 64202
|
||||||
|
[HoldingRegister<UInt16>(41116)] private UInt16 _battCharLength; // L = 6 + (RBCount * 8)
|
||||||
|
|
||||||
|
// Fixed block
|
||||||
|
[HoldingRegister<UInt16>(41117)] private UInt16 _battCharVersion; // Version (uint16, R)
|
||||||
|
[HoldingRegister<UInt16>(41118)] private UInt16 _battCharVerMinor; // VerMinor (uint16, R)
|
||||||
|
[HoldingRegister<UInt16>(41119)] private UInt16 _battCharRsrvd1; // Rsrvd_1 (pad)
|
||||||
|
[HoldingRegister<UInt16>(41120)] private UInt16 _battCharRsrvd2; // Rsrvd_2 (pad)
|
||||||
|
[HoldingRegister<Int16> (41121)] private Int16 _battCharVSf; // V_SF (sunsf, R)
|
||||||
|
[HoldingRegister<Int16> (41122)] private Int16 _battCharASf; // A_SF (sunsf, R)
|
||||||
|
|
||||||
|
// Repeating block #0 (you said there is only one block)
|
||||||
|
[HoldingRegister<UInt16>(41123, writable: true)] private UInt16 _disMinVRaw; // DisMinV (uint16, V, V_SF, RW) min. discharge voltage
|
||||||
|
[HoldingRegister<UInt16>(41124, writable: true)] private UInt16 _disMaxARaw; // DisMaxA (uint16, A, A_SF, RW)max. discharge current
|
||||||
|
[HoldingRegister<UInt16>(41125, writable: true)] private UInt16 _disCutoffARaw; // DisCutoffA (uint16, A, A_SF, RW)Disconnect if discharge current lower than DisCutoffA
|
||||||
|
[HoldingRegister<UInt16>(41126, writable: true)] private UInt16 _chaMaxVRaw; // ChaMaxV (uint16, V, V_SF, RW)max. charge voltage
|
||||||
|
[HoldingRegister<UInt16>(41127, writable: true)] private UInt16 _chaMaxARaw; // ChaMaxA (uint16, A, A_SF, RW)max. charge current
|
||||||
|
[HoldingRegister<UInt16>(41128, writable: true)] private UInt16 _chaCutoffARaw; // ChaCutoffA (uint16, A, A_SF, RW)Disconnect if charge current lower than ChaCuttoffA
|
||||||
|
[HoldingRegister<UInt16>(41129)] private UInt16 _battCharPad; // Pad (pad, R)
|
||||||
|
[HoldingRegister<UInt16>(41130, writable: true)] private UInt16 _enLimitRaw; // EnLimit (uint16, RW)new battery limits are activated when EnLimit is 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<RootNamespace>InnovEnergy.Lib.Devices.PLVario2Meter</RootNamespace>
|
||||||
|
<LangVersion>preview</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Protocols\Modbus\Modbus.csproj" />
|
||||||
|
<ProjectReference Include="..\..\StatusApi\StatusApi.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Units\Units.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Channels;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Slaves;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.PLVario2Meter;
|
||||||
|
|
||||||
|
public class PlVarioMeterDevice: ModbusDevice<PlVarioMeterRegisters>
|
||||||
|
{
|
||||||
|
public PlVarioMeterDevice(String hostname, UInt16 port = 502, Byte slaveId = 2) : this(new TcpChannel(hostname, port), slaveId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlVarioMeterDevice(Channel channel, Byte slaveId = 2) : base(new ModbusTcpClient(channel, slaveId))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlVarioMeterDevice(ModbusClient client) : base(client)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public new PlVarioMeterRegisters? Read()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return base.Read();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
"Failed to read data from PLVario-II meter".WriteLine();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Write(PlVarioMeterRegisters registers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
base.Write(registers);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// TODO: Log
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
|
using InnovEnergy.Lib.Units.Power;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.PLVario2Meter;
|
||||||
|
|
||||||
|
public class PlVarioMeterRecord
|
||||||
|
{
|
||||||
|
private readonly PlVarioMeterRegisters _raw;
|
||||||
|
|
||||||
|
public PlVarioMeterRecord(PlVarioMeterRegisters raw)
|
||||||
|
{
|
||||||
|
_raw = raw ?? throw new ArgumentNullException(nameof(raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ushort SwapBytes16(ushort x)
|
||||||
|
=> (ushort)((x << 8) | (x >> 8));
|
||||||
|
|
||||||
|
static float DecodeFloat_BADC(ushort w0, ushort w1)
|
||||||
|
{
|
||||||
|
// Build bytes: [B A D C] = swap bytes in each word, and use W1 then W0
|
||||||
|
ushort w0s = SwapBytes16(w0); // F1AA
|
||||||
|
ushort w1s = SwapBytes16(w1); // 4247
|
||||||
|
uint raw = ((uint)w1s << 16) | w0s; // 0x4247F1AA
|
||||||
|
return BitConverter.Int32BitsToSingle(unchecked((int)raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Decoders (DCBA) from raw UInt16 pairs
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
private static uint DecodeUInt32DCBA(ushort lowWord, ushort highWord)
|
||||||
|
=> ((uint)highWord << 16) | (uint)lowWord;
|
||||||
|
|
||||||
|
private static float DecodeFloatDCBA(ushort lowWord, ushort highWord)
|
||||||
|
{
|
||||||
|
uint raw = ((uint)highWord << 16) | (uint)lowWord;
|
||||||
|
return BitConverter.Int32BitsToSingle(unchecked((int)raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Double F(ushort w0, ushort w1) => DecodeFloat_BADC(w0, w1);
|
||||||
|
private static UInt32 U32(ushort w0, ushort w1) => DecodeUInt32DCBA(w0, w1);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Time
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
/// <summary>Timestamp in seconds (as provided by the device).</summary>
|
||||||
|
public uint TimestampSeconds => U32(_raw.Timestamp_W0, _raw.Timestamp_W1);
|
||||||
|
|
||||||
|
/// <summary>Timestamp interpreted as Unix epoch seconds (UTC).</summary>
|
||||||
|
public DateTimeOffset TimestampUtc => DateTimeOffset.FromUnixTimeSeconds(TimestampSeconds);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Voltages (phase-neutral and line-line)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public VoltageRms VoltageU1 => new(F(_raw.VoltageU1_W0, _raw.VoltageU1_W1)); // U-1
|
||||||
|
public VoltageRms VoltageU2 => new(F(_raw.VoltageU2_W0, _raw.VoltageU2_W1)); // U-2
|
||||||
|
public VoltageRms VoltageU3 => new(F(_raw.VoltageU3_W0, _raw.VoltageU3_W1)); // U-3
|
||||||
|
|
||||||
|
// Friendly aliases (match your naming style)
|
||||||
|
public VoltageRms GridAbLineVoltage => (VoltageRms)new(F(_raw.VoltageUL1_W0, _raw.VoltageUL1_W1));
|
||||||
|
public VoltageRms GridBcLineVoltage => (VoltageRms)new(F(_raw.VoltageUL2_W0, _raw.VoltageUL2_W1));
|
||||||
|
public VoltageRms GridCaLineVoltage => (VoltageRms)new(F(_raw.VoltageUL3_W0, _raw.VoltageUL3_W1));
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Frequency
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public Double Frequency => (F(_raw.Frequency_W0, _raw.Frequency_W1));
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Currents
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public CurrentRms CurrentI1 => new(F(_raw.CurrentI1_W0, _raw.CurrentI1_W1));
|
||||||
|
public CurrentRms CurrentI2 => new(F(_raw.CurrentI2_W0, _raw.CurrentI2_W1));
|
||||||
|
public CurrentRms CurrentI3 => new(F(_raw.CurrentI3_W0, _raw.CurrentI3_W1));
|
||||||
|
public CurrentRms CurrentI4 => new(F(_raw.CurrentI4_W0, _raw.CurrentI4_W1)); // optional channel
|
||||||
|
public CurrentRms CurrentTotal => new(F(_raw.CurrentTotal_W0, _raw.CurrentTotal_W1));
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Active Power (kW)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public ActivePower ActivePowerL1 => new(F(_raw.ActivePowerP1_W0, _raw.ActivePowerP1_W1) * 1000);
|
||||||
|
public ActivePower ActivePowerL2 => new(F(_raw.ActivePowerP2_W0, _raw.ActivePowerP2_W1) * 1000);
|
||||||
|
public ActivePower ActivePowerL3 => new(F(_raw.ActivePowerP3_W0, _raw.ActivePowerP3_W1) * 1000);
|
||||||
|
public ActivePower ActivePowerCh4 => new(F(_raw.ActivePowerP4_W0, _raw.ActivePowerP4_W1) * 1000); // optional channel
|
||||||
|
public ActivePower ActivePowerTotal => new(F(_raw.ActivePowerTotal_W0, _raw.ActivePowerTotal_W1) * 1000);
|
||||||
|
|
||||||
|
// If you later map import/export separately, you can redefine these.
|
||||||
|
// For now, the table provides only total P (signed), so we expose it as "GridPower".
|
||||||
|
public ActivePower GridPower => ActivePowerTotal;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Reactive Power (kVAr)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public ReactivePower ReactivePowerL1 => new(F(_raw.ReactivePowerQ1_W0, _raw.ReactivePowerQ1_W1));
|
||||||
|
public ReactivePower ReactivePowerL2 => new(F(_raw.ReactivePowerQ2_W0, _raw.ReactivePowerQ2_W1));
|
||||||
|
public ReactivePower ReactivePowerL3 => new(F(_raw.ReactivePowerQ3_W0, _raw.ReactivePowerQ3_W1));
|
||||||
|
public ReactivePower ReactivePowerCh4 => new(F(_raw.ReactivePowerQ4_W0, _raw.ReactivePowerQ4_W1));
|
||||||
|
public ReactivePower ReactivePowerTotal => new(F(_raw.ReactivePowerTotal_W0, _raw.ReactivePowerTotal_W1));
|
||||||
|
|
||||||
|
// Friendly alias
|
||||||
|
public ReactivePower GridReactivePower => ReactivePowerTotal;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Apparent Power (kVA)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public ApparentPower ApparentPowerL1 => new(F(_raw.ApparentPowerS1_W0, _raw.ApparentPowerS1_W1));
|
||||||
|
public ApparentPower ApparentPowerL2 => new(F(_raw.ApparentPowerS2_W0, _raw.ApparentPowerS2_W1));
|
||||||
|
public ApparentPower ApparentPowerL3 => new(F(_raw.ApparentPowerS3_W0, _raw.ApparentPowerS3_W1));
|
||||||
|
public ApparentPower ApparentPowerCh4 => new(F(_raw.ApparentPowerS4_W0, _raw.ApparentPowerS4_W1));
|
||||||
|
public ApparentPower ApparentPowerTotal => new(F(_raw.ApparentPowerTotal_W0, _raw.ApparentPowerTotal_W1));
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Power Factor
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public Double PowerFactorL1 => (F(_raw.PowerFactorPF1_W0, _raw.PowerFactorPF1_W1));
|
||||||
|
public Double PowerFactorL2 => (F(_raw.PowerFactorPF2_W0, _raw.PowerFactorPF2_W1));
|
||||||
|
public Double PowerFactorL3 => (F(_raw.PowerFactorPF3_W0, _raw.PowerFactorPF3_W1));
|
||||||
|
public Double PowerFactorCh4 => (F(_raw.PowerFactorPF4_W0, _raw.PowerFactorPF4_W1));
|
||||||
|
public Double PowerFactorTotal => (F(_raw.PowerFactorTotal_W0, _raw.PowerFactorTotal_W1));
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Repeated totals (table shows duplicates at offsets 66..74)
|
||||||
|
// Keep them exposed in case your installation populates them differently.
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
public CurrentRms CurrentTotalRepeat => new(F(_raw.CurrentTotalRepeat_W0, _raw.CurrentTotalRepeat_W1));
|
||||||
|
public ActivePower ActivePowerTotalRepeat => new(F(_raw.ActivePowerTotalRepeat_W0, _raw.ActivePowerTotalRepeat_W1));
|
||||||
|
public ReactivePower ReactivePowerTotalRepeat => new(F(_raw.ReactivePowerTotalRepeat_W0, _raw.ReactivePowerTotalRepeat_W1));
|
||||||
|
public ApparentPower ApparentPowerTotalRepeat => new(F(_raw.ApparentPowerTotalRepeat_W0, _raw.ApparentPowerTotalRepeat_W1));
|
||||||
|
public Double PowerFactorTotalRepeat => (F(_raw.PowerFactorTotalRepeat_W0, _raw.PowerFactorTotalRepeat_W1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
|
||||||
|
using InnovEnergy.Lib.StatusApi.DeviceTypes;
|
||||||
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.PLVario2Meter;
|
||||||
|
|
||||||
|
using Float32 = Single;
|
||||||
|
|
||||||
|
// If your project already defines Float32 as an alias/struct, keep that.
|
||||||
|
// Otherwise, you can just use float. Here we use float for simplicity.
|
||||||
|
public class PlVarioMeterRegisters : IAc3Meter
|
||||||
|
{
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Timestamp (UINT32) -> 2 x UInt16
|
||||||
|
// Offset 0: Timestamp [s]
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1000, writable: false)] public UInt16 Timestamp_W0; // low word
|
||||||
|
[HoldingRegister<UInt16>(1001, writable: false)] public UInt16 Timestamp_W1; // high word
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Voltages phase-neutral (FLOAT32) -> 2 x UInt16 each
|
||||||
|
// Offsets 2,4,6
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1002, writable: false)] public UInt16 VoltageU1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1003, writable: false)] public UInt16 VoltageU1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1004, writable: false)] public UInt16 VoltageU2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1005, writable: false)] public UInt16 VoltageU2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1006, writable: false)] public UInt16 VoltageU3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1007, writable: false)] public UInt16 VoltageU3_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Voltages line-line (FLOAT32) -> 2 x UInt16 each
|
||||||
|
// Offsets 8,10,12
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1008, writable: false)] public UInt16 VoltageUL1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1009, writable: false)] public UInt16 VoltageUL1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1010, writable: false)] public UInt16 VoltageUL2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1011, writable: false)] public UInt16 VoltageUL2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1012, writable: false)] public UInt16 VoltageUL3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1013, writable: false)] public UInt16 VoltageUL3_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Frequency (FLOAT32) -> 2 x UInt16
|
||||||
|
// Offset 14
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1014, writable: false)] public UInt16 Frequency_W0;
|
||||||
|
[HoldingRegister<UInt16>(1015, writable: false)] public UInt16 Frequency_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Currents (FLOAT32) -> 2 x UInt16 each
|
||||||
|
// Offsets 16..24
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1016, writable: false)] public UInt16 CurrentI1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1017, writable: false)] public UInt16 CurrentI1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1018, writable: false)] public UInt16 CurrentI2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1019, writable: false)] public UInt16 CurrentI2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1020, writable: false)] public UInt16 CurrentI3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1021, writable: false)] public UInt16 CurrentI3_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1022, writable: false)] public UInt16 CurrentI4_W0;
|
||||||
|
[HoldingRegister<UInt16>(1023, writable: false)] public UInt16 CurrentI4_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1024, writable: false)] public UInt16 CurrentTotal_W0;
|
||||||
|
[HoldingRegister<UInt16>(1025, writable: false)] public UInt16 CurrentTotal_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Active power P (FLOAT32, kW) -> 2 x UInt16 each
|
||||||
|
// Offsets 26..34
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1026, writable: false)] public UInt16 ActivePowerP1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1027, writable: false)] public UInt16 ActivePowerP1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1028, writable: false)] public UInt16 ActivePowerP2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1029, writable: false)] public UInt16 ActivePowerP2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1030, writable: false)] public UInt16 ActivePowerP3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1031, writable: false)] public UInt16 ActivePowerP3_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1032, writable: false)] public UInt16 ActivePowerP4_W0;
|
||||||
|
[HoldingRegister<UInt16>(1033, writable: false)] public UInt16 ActivePowerP4_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1034, writable: false)] public UInt16 ActivePowerTotal_W0;
|
||||||
|
[HoldingRegister<UInt16>(1035, writable: false)] public UInt16 ActivePowerTotal_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Reactive power Q (FLOAT32, kVAr) -> 2 x UInt16 each
|
||||||
|
// Offsets 36..44
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1036, writable: false)] public UInt16 ReactivePowerQ1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1037, writable: false)] public UInt16 ReactivePowerQ1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1038, writable: false)] public UInt16 ReactivePowerQ2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1039, writable: false)] public UInt16 ReactivePowerQ2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1040, writable: false)] public UInt16 ReactivePowerQ3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1041, writable: false)] public UInt16 ReactivePowerQ3_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1042, writable: false)] public UInt16 ReactivePowerQ4_W0;
|
||||||
|
[HoldingRegister<UInt16>(1043, writable: false)] public UInt16 ReactivePowerQ4_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1044, writable: false)] public UInt16 ReactivePowerTotal_W0;
|
||||||
|
[HoldingRegister<UInt16>(1045, writable: false)] public UInt16 ReactivePowerTotal_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Apparent power S (FLOAT32, kVA) -> 2 x UInt16 each
|
||||||
|
// Offsets 46..54
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1046, writable: false)] public UInt16 ApparentPowerS1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1047, writable: false)] public UInt16 ApparentPowerS1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1048, writable: false)] public UInt16 ApparentPowerS2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1049, writable: false)] public UInt16 ApparentPowerS2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1050, writable: false)] public UInt16 ApparentPowerS3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1051, writable: false)] public UInt16 ApparentPowerS3_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1052, writable: false)] public UInt16 ApparentPowerS4_W0;
|
||||||
|
[HoldingRegister<UInt16>(1053, writable: false)] public UInt16 ApparentPowerS4_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1054, writable: false)] public UInt16 ApparentPowerTotal_W0;
|
||||||
|
[HoldingRegister<UInt16>(1055, writable: false)] public UInt16 ApparentPowerTotal_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Power factor PF (FLOAT32) -> 2 x UInt16 each
|
||||||
|
// Offsets 56..64
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1056, writable: false)] public UInt16 PowerFactorPF1_W0;
|
||||||
|
[HoldingRegister<UInt16>(1057, writable: false)] public UInt16 PowerFactorPF1_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1058, writable: false)] public UInt16 PowerFactorPF2_W0;
|
||||||
|
[HoldingRegister<UInt16>(1059, writable: false)] public UInt16 PowerFactorPF2_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1060, writable: false)] public UInt16 PowerFactorPF3_W0;
|
||||||
|
[HoldingRegister<UInt16>(1061, writable: false)] public UInt16 PowerFactorPF3_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1062, writable: false)] public UInt16 PowerFactorPF4_W0;
|
||||||
|
[HoldingRegister<UInt16>(1063, writable: false)] public UInt16 PowerFactorPF4_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1064, writable: false)] public UInt16 PowerFactorTotal_W0;
|
||||||
|
[HoldingRegister<UInt16>(1065, writable: false)] public UInt16 PowerFactorTotal_W1;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Repeated totals (as shown in the table) at offsets 66..74
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
[HoldingRegister<UInt16>(1066, writable: false)] public UInt16 CurrentTotalRepeat_W0;
|
||||||
|
[HoldingRegister<UInt16>(1067, writable: false)] public UInt16 CurrentTotalRepeat_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1068, writable: false)] public UInt16 ActivePowerTotalRepeat_W0;
|
||||||
|
[HoldingRegister<UInt16>(1069, writable: false)] public UInt16 ActivePowerTotalRepeat_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1070, writable: false)] public UInt16 ReactivePowerTotalRepeat_W0;
|
||||||
|
[HoldingRegister<UInt16>(1071, writable: false)] public UInt16 ReactivePowerTotalRepeat_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1072, writable: false)] public UInt16 ApparentPowerTotalRepeat_W0;
|
||||||
|
[HoldingRegister<UInt16>(1073, writable: false)] public UInt16 ApparentPowerTotalRepeat_W1;
|
||||||
|
|
||||||
|
[HoldingRegister<UInt16>(1074, writable: false)] public UInt16 PowerFactorTotalRepeat_W0;
|
||||||
|
[HoldingRegister<UInt16>(1075, writable: false)] public UInt16 PowerFactorTotalRepeat_W1;
|
||||||
|
|
||||||
|
public Ac3Bus Ac { get; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K.DataType;
|
||||||
|
|
||||||
|
public enum GrowattErrorCode
|
||||||
|
{
|
||||||
|
NoAlarm = 0,
|
||||||
|
ExportLimitationFailSafe = 311,
|
||||||
|
|
||||||
|
DcBiasAbnormal = 400,
|
||||||
|
HighDcComponentOutputCurrent = 402,
|
||||||
|
BusVoltageSamplingAbnormal = 404,
|
||||||
|
RelayFault = 405,
|
||||||
|
OverTemperature = 408,
|
||||||
|
BusVoltageAbnormal = 409,
|
||||||
|
|
||||||
|
InternalCommunicationFailure = 411,
|
||||||
|
TemperatureSensorDisconnected = 412,
|
||||||
|
IgbtDriveFault = 413,
|
||||||
|
EepromError = 414,
|
||||||
|
AuxiliaryPowerAbnormal = 415,
|
||||||
|
DcAcOvercurrentProtection = 416,
|
||||||
|
|
||||||
|
CommunicationProtocolMismatch = 417,
|
||||||
|
DspComFirmwareMismatch = 418,
|
||||||
|
DspSoftwareHardwareMismatch = 419,
|
||||||
|
CpldAbnormal = 421,
|
||||||
|
RedundancySamplingInconsistent = 422,
|
||||||
|
PwmPassThroughSignalFailure = 423,
|
||||||
|
AfciSelfTestFailure = 425,
|
||||||
|
PvCurrentSamplingAbnormal = 426,
|
||||||
|
AcCurrentSamplingAbnormal = 427,
|
||||||
|
BusSoftbootFailure = 429,
|
||||||
|
EpoFault = 430,
|
||||||
|
MonitoringChipBootVerificationFailed = 431,
|
||||||
|
|
||||||
|
BmsCommunicationFailure = 500,
|
||||||
|
BmsChargeDischargeFailure = 501,
|
||||||
|
BatteryVoltageLow = 502,
|
||||||
|
BatteryVoltageHigh = 503,
|
||||||
|
BatteryTemperatureAbnormal = 504,
|
||||||
|
BatteryReversed = 505,
|
||||||
|
BatteryOpenCircuit = 506,
|
||||||
|
BatteryOverloadProtection = 507,
|
||||||
|
Bus2VoltageAbnormal = 508,
|
||||||
|
|
||||||
|
BatteryChargeOcp = 509,
|
||||||
|
BatteryDischargeOcp = 510,
|
||||||
|
BatterySoftStartFailed = 511,
|
||||||
|
|
||||||
|
EpsOutputShortCircuited = 600,
|
||||||
|
OffGridBusVoltageLow = 601,
|
||||||
|
OffGridTerminalVoltageAbnormal = 602,
|
||||||
|
SoftStartFailed = 603,
|
||||||
|
OffGridOutputVoltageAbnormal = 604,
|
||||||
|
BalancedCircuitSelfTestFailed = 605,
|
||||||
|
HighDcComponentOutputVoltage = 606,
|
||||||
|
OffGridOutputOverload = 607,
|
||||||
|
OffGridParallelSignalAbnormal = 608
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K.DataType;
|
||||||
|
|
||||||
|
public enum GrowattWarningCode : UInt16
|
||||||
|
{
|
||||||
|
NoAlarm =0,
|
||||||
|
StringFault = 200,
|
||||||
|
PvStringPidQuickConnectAbnormal = 201,
|
||||||
|
DcSpdFunctionAbnormal = 202,
|
||||||
|
PvShortCircuited = 203,
|
||||||
|
PvBoostDriverAbnormal = 204,
|
||||||
|
AcSpdFunctionAbnormal = 205,
|
||||||
|
DcFuseBlown = 208,
|
||||||
|
|
||||||
|
DcInputVoltageTooHigh = 209,
|
||||||
|
PvReversed = 210,
|
||||||
|
PidFunctionAbnormal = 219,
|
||||||
|
PvStringDisconnected = 220,
|
||||||
|
PvStringCurrentUnbalanced = 221,
|
||||||
|
|
||||||
|
NoUtilityGrid = 300,
|
||||||
|
GridVoltageOutOfRange = 301,
|
||||||
|
GridFrequencyOutOfRange = 302,
|
||||||
|
Overload = 303,
|
||||||
|
MeterDisconnected = 308,
|
||||||
|
MeterReverselyConnected = 309,
|
||||||
|
LinePeVoltageAbnormal = 310,
|
||||||
|
PhaseSequenceError = 311,
|
||||||
|
|
||||||
|
FanFailure = 400,
|
||||||
|
MeterAbnormal = 401,
|
||||||
|
OptimizerCommunicationAbnormal = 402,
|
||||||
|
OverTemperature = 407,
|
||||||
|
OverTemperatureAlarm = 408,
|
||||||
|
NtcTemperatureSensorBroken = 409,
|
||||||
|
SyncSignalAbnormal = 411,
|
||||||
|
GridStartupConditionsNotMet = 412,
|
||||||
|
|
||||||
|
BatteryCommunicationFailure = 500,
|
||||||
|
BatteryDisconnected = 501,
|
||||||
|
BatteryVoltageTooHigh = 502,
|
||||||
|
BatteryVoltageTooLow = 503,
|
||||||
|
BatteryReverseConnected = 504,
|
||||||
|
LeadAcidTempSensorDisconnected = 505,
|
||||||
|
BatteryTemperatureOutOfRange = 506,
|
||||||
|
BmsFault = 507,
|
||||||
|
LithiumBatteryOverload = 508,
|
||||||
|
BmsCommunicationAbnormal = 509,
|
||||||
|
BatterySpdAbnormal = 510,
|
||||||
|
|
||||||
|
OutputDcComponentBiasAbnormal = 600,
|
||||||
|
DcComponentOverHighOutputVoltage = 601,
|
||||||
|
OffGridOutputVoltageTooLow = 602,
|
||||||
|
OffGridOutputVoltageTooHigh = 603,
|
||||||
|
OffGridOutputOverCurrent = 604,
|
||||||
|
OffGridBusVoltageTooLow = 605,
|
||||||
|
OffGridOutputOverload = 606,
|
||||||
|
BalancedCircuitAbnormal = 609
|
||||||
|
}
|
||||||
|
|
@ -42,9 +42,9 @@ public partial class WITGrowatRecord
|
||||||
public BatteryoperatinStatus BatteryOperatingMode => (BatteryoperatinStatus) _batteryOperatingMode;
|
public BatteryoperatinStatus BatteryOperatingMode => (BatteryoperatinStatus) _batteryOperatingMode;
|
||||||
public OperatingPriority OperatingMode => (OperatingPriority)_operatingPriority;
|
public OperatingPriority OperatingMode => (OperatingPriority)_operatingPriority;
|
||||||
|
|
||||||
public UInt16 FaultMainCode => _faultMainCode; // need to pre proceesed
|
public GrowattErrorCode FaultMainCode => _faultMainCode; // need to pre proceesed
|
||||||
public UInt16 FaultSubCode => _faultSubCode; // need to pre proceesed
|
public UInt16 FaultSubCode => _faultSubCode; // need to pre proceesed
|
||||||
public UInt16 WarningMainCode => _warningMainCode; // need to pre proceesed
|
public GrowattWarningCode WarningMainCode => _warningMainCode; // need to pre proceesed
|
||||||
public UInt16 WarningSubCode => _warningSubCode; // need to pre proceesed
|
public UInt16 WarningSubCode => _warningSubCode; // need to pre proceesed
|
||||||
|
|
||||||
public Voltage Pv1Voltage => _pv1Voltage;
|
public Voltage Pv1Voltage => _pv1Voltage;
|
||||||
|
|
@ -96,10 +96,10 @@ public partial class WITGrowatRecord
|
||||||
get => _VppProtocolVerNumber;
|
get => _VppProtocolVerNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean ControlPermession
|
public Boolean ControlPermission
|
||||||
{
|
{
|
||||||
get => _ControlPermession;
|
get => _ControlPermission;
|
||||||
set => _ControlPermession = value;
|
set => _ControlPermission = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean EnableCommand
|
public Boolean EnableCommand
|
||||||
|
|
@ -172,16 +172,16 @@ public partial class WITGrowatRecord
|
||||||
set => _BatteryMaxDischargePower = value;
|
set => _BatteryMaxDischargePower = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Percent ChargeCutoffSocVoltage
|
public Voltage ChargeCutoffSocVoltage
|
||||||
{
|
{
|
||||||
get => _BatteryChargeCutoffVoltage;
|
get => _batteryChargeCutoffVoltage;
|
||||||
set => _BatteryChargeCutoffVoltage = (UInt16)value;
|
set => _batteryChargeCutoffVoltage = (UInt16)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Percent DischargeCutoffVoltage
|
public Voltage DischargeCutoffVoltage
|
||||||
{
|
{
|
||||||
get => _BatteryDischargeCutoffVoltage;
|
get => _batteryDischargeCutoffVoltage;
|
||||||
set => _BatteryDischargeCutoffVoltage = (UInt16)value;
|
set => _batteryDischargeCutoffVoltage = (UInt16)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Percent LoadPriorityDischargeCutoffSoc
|
public Percent LoadPriorityDischargeCutoffSoc
|
||||||
|
|
@ -250,7 +250,6 @@ public partial class WITGrowatRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double Battery1MaxDischargingCurrent => _batteryMaxDischargingCurrent;
|
public Double Battery1MaxDischargingCurrent => _batteryMaxDischargingCurrent;
|
||||||
|
|
||||||
public Double Battery1MaxChargingCurrent => _batteryMaxChargingCurrent;
|
public Double Battery1MaxChargingCurrent => _batteryMaxChargingCurrent;
|
||||||
|
|
||||||
public Percent Battery1Soc => _BatterySoc1;
|
public Percent Battery1Soc => _BatterySoc1;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using InnovEnergy.Lib.Devices.WITGrowatt4_15K.DataType;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
|
using InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K;
|
namespace InnovEnergy.Lib.Devices.WITGrowatt4_15K;
|
||||||
|
|
@ -19,9 +20,9 @@ public partial class WITGrowatRecord
|
||||||
[InputRegister(31002)] private UInt16 _operatingPriority;
|
[InputRegister(31002)] private UInt16 _operatingPriority;
|
||||||
[InputRegister(31003)] private UInt16 _reserved1;
|
[InputRegister(31003)] private UInt16 _reserved1;
|
||||||
[InputRegister(31004)] private UInt16 _reserved2;
|
[InputRegister(31004)] private UInt16 _reserved2;
|
||||||
[InputRegister(31005)] private UInt16 _faultMainCode;// Can we change this to warning?
|
[InputRegister(31005)] private GrowattErrorCode _faultMainCode;// Can we change this to warning?
|
||||||
[InputRegister(31006)] private UInt16 _faultSubCode; // Can we change this to warning?
|
[InputRegister(31006)] private UInt16 _faultSubCode; // Can we change this to warning?
|
||||||
[InputRegister(31007)] private UInt16 _warningMainCode;
|
[InputRegister(31007)] private GrowattWarningCode _warningMainCode;
|
||||||
[InputRegister(31008)] private UInt16 _warningSubCode;
|
[InputRegister(31008)] private UInt16 _warningSubCode;
|
||||||
[InputRegister(31009)] private UInt16 _reserved3;
|
[InputRegister(31009)] private UInt16 _reserved3;
|
||||||
// 31010–31099 — PV Parameters
|
// 31010–31099 — PV Parameters
|
||||||
|
|
@ -144,7 +145,7 @@ public partial class WITGrowatRecord
|
||||||
[HoldingRegister(30030)] private UInt16 _BatteryType;
|
[HoldingRegister(30030)] private UInt16 _BatteryType;
|
||||||
|
|
||||||
[HoldingRegister(30099)] private UInt16 _VppProtocolVerNumber;
|
[HoldingRegister(30099)] private UInt16 _VppProtocolVerNumber;
|
||||||
[HoldingRegister(30100, writable: true)] private Boolean _ControlPermession; // 0 Disabled, 1 enabled
|
[HoldingRegister(30100, writable: true)] private Boolean _ControlPermission; // 0 Disabled, 1 enabled
|
||||||
[HoldingRegister(30101, writable: true)] private Boolean _EnableCommand; // 0: Off, 1: On; Defaut is 1; not stored, must enable this register to control inverter
|
[HoldingRegister(30101, writable: true)] private Boolean _EnableCommand; // 0: Off, 1: On; Defaut is 1; not stored, must enable this register to control inverter
|
||||||
[HoldingRegister(30102)] private UInt16 _CountryRegionCode;
|
[HoldingRegister(30102)] private UInt16 _CountryRegionCode;
|
||||||
[HoldingRegister(30103)] private UInt16 _Reserved8;
|
[HoldingRegister(30103)] private UInt16 _Reserved8;
|
||||||
|
|
@ -166,7 +167,7 @@ public partial class WITGrowatRecord
|
||||||
|
|
||||||
[HoldingRegister(30162)] private UInt16 _PowerFactor; // [0, 2000] ∪ [18000, 20000]; Default: 20000; Actual PF = (Register Value - 10000)
|
[HoldingRegister(30162)] private UInt16 _PowerFactor; // [0, 2000] ∪ [18000, 20000]; Default: 20000; Actual PF = (Register Value - 10000)
|
||||||
[HoldingRegister(30203, writable : true)] private UInt16 _EmsCommunicationFailureTime; // [1,300] TODO to 30
|
[HoldingRegister(30203, writable : true)] private UInt16 _EmsCommunicationFailureTime; // [1,300] TODO to 30
|
||||||
[HoldingRegister(30204, writable : true)] private Boolean _EnableEmsCommunicationFailureTime; // 0: disabled, 1 = enabled we should enable this TODO
|
[HoldingRegister(30204, writable : true)] private Boolean _EnableEmsCommunicationFailureTime; // 0: disabled, 1 = enabled we should not enable this the naming is not correct
|
||||||
[HoldingRegister(30300)] private UInt16 _BatteryClusterIndex; // [0..3]
|
[HoldingRegister(30300)] private UInt16 _BatteryClusterIndex; // [0..3]
|
||||||
|
|
||||||
[HoldingRegister<UInt32>(30400 , writable: true)] private UInt32 _BatteryMaxChargePower; //
|
[HoldingRegister<UInt32>(30400 , writable: true)] private UInt32 _BatteryMaxChargePower; //
|
||||||
|
|
@ -182,8 +183,8 @@ public partial class WITGrowatRecord
|
||||||
[HoldingRegister(30474)] private UInt16 _ActualChargeDischargePowerControlValue; //
|
[HoldingRegister(30474)] private UInt16 _ActualChargeDischargePowerControlValue; //
|
||||||
[HoldingRegister(30475)] private UInt16 _OffGridDischargeCutoffSoc; //
|
[HoldingRegister(30475)] private UInt16 _OffGridDischargeCutoffSoc; //
|
||||||
|
|
||||||
[HoldingRegister(30496, writable: true, Scale = 0.1)] private UInt16 _BatteryChargeCutoffVoltage; //
|
[HoldingRegister(30496, writable: true, Scale = 0.1)] private UInt16 _batteryChargeCutoffVoltage; //
|
||||||
[HoldingRegister(30497, writable: true, Scale = 0.1)] private UInt16 _BatteryDischargeCutoffVoltage; //
|
[HoldingRegister(30497, writable: true, Scale = 0.1)] private UInt16 _batteryDischargeCutoffVoltage; //
|
||||||
[HoldingRegister(30498, writable: true, Scale = 0.1)] private UInt16 _batteryMaxChargingCurrent; //
|
[HoldingRegister(30498, writable: true, Scale = 0.1)] private UInt16 _batteryMaxChargingCurrent; //
|
||||||
[HoldingRegister(30499, writable: true, Scale = 0.1)] private UInt16 _batteryMaxDischargingCurrent; //*/
|
[HoldingRegister(30499, writable: true, Scale = 0.1)] private UInt16 _batteryMaxDischargingCurrent; //*/
|
||||||
}
|
}
|
||||||
|
|
@ -15,5 +15,6 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Channels
|
||||||
{
|
{
|
||||||
public abstract IReadOnlyList<byte> Read(int nBytes);
|
public abstract IReadOnlyList<byte> Read(int nBytes);
|
||||||
public abstract void Write(IReadOnlyList<byte> bytes);
|
public abstract void Write(IReadOnlyList<byte> bytes);
|
||||||
|
public virtual void ClearBuffers() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,11 @@ public abstract class ConnectionChannel<T> : Channel, IDisposable
|
||||||
_CloseAfterSuccessfulWrite = closeAfterSuccessfulWrite;
|
_CloseAfterSuccessfulWrite = closeAfterSuccessfulWrite;
|
||||||
_CloseAfterException = closeAfterException ?? (_ => true);
|
_CloseAfterException = closeAfterException ?? (_ => true);
|
||||||
}
|
}
|
||||||
|
// 👇 NEW: optional per-connection buffer clear
|
||||||
|
protected virtual void ClearBuffers(T connection)
|
||||||
|
{
|
||||||
|
// default: do nothing
|
||||||
|
}
|
||||||
public override IReadOnlyList<Byte> Read(Int32 nBytes)
|
public override IReadOnlyList<Byte> Read(Int32 nBytes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,22 @@ public class SerialPortChannel : ConnectionChannel<SerialPort>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 👇 NEW: this is where we actually flush the serial buffers
|
||||||
|
protected override void ClearBuffers(SerialPort serialPort)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serialPort.DiscardInBuffer();
|
||||||
|
serialPort.DiscardOutBuffer();
|
||||||
|
Console.WriteLine(" Buffer in and out discarded.");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine(" couldn't clear the Buffer .");
|
||||||
|
// ignore if port is closed / disposed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override SerialPort Open() => _Open();
|
protected override SerialPort Open() => _Open();
|
||||||
|
|
||||||
protected override void Close(SerialPort serialPort) => serialPort.Dispose();
|
protected override void Close(SerialPort serialPort) => serialPort.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,5 @@ public abstract class ModbusClient
|
||||||
SlaveId = slaveId;
|
SlaveId = slaveId;
|
||||||
Endian = endian;
|
Endian = endian;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,11 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Reflection.Attributes;
|
||||||
|
|
||||||
public class Coil : ModbusBoolean
|
public class Coil : ModbusBoolean
|
||||||
{
|
{
|
||||||
public Coil(UInt16 address) : base(address, ModbusKind.Coil)
|
public bool Writable { get; }
|
||||||
|
|
||||||
|
public Coil(ushort address, bool writable = true)
|
||||||
|
: base(address, ModbusKind.Coil)
|
||||||
{
|
{
|
||||||
|
Writable = writable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ public static class Batches
|
||||||
{
|
{
|
||||||
if (CloseBatch(member))
|
if (CloseBatch(member))
|
||||||
{
|
{
|
||||||
//PrintBatchStart(batchMembers);
|
PrintBatchStart(batchMembers);
|
||||||
yield return MakeBatch<R>(mb, batchMembers);
|
yield return MakeBatch<R>(mb, batchMembers);
|
||||||
batchMembers = new List<ModbusMember>();
|
batchMembers = new List<ModbusMember>();
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,6 @@ public static class Batches
|
||||||
|
|
||||||
if (batchMembers.Count > 0)
|
if (batchMembers.Count > 0)
|
||||||
{
|
{
|
||||||
//PrintBatchStart(batchMembers);
|
|
||||||
yield return MakeBatch<R>(mb, batchMembers);
|
yield return MakeBatch<R>(mb, batchMembers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,8 +73,16 @@ public static class Batches
|
||||||
: $"{first.StartAddress}-{last.EndAddress - 1}";
|
: $"{first.StartAddress}-{last.EndAddress - 1}";
|
||||||
|
|
||||||
var writable = first.IsWritable ? "Writable" : "ReadOnly";
|
var writable = first.IsWritable ? "Writable" : "ReadOnly";
|
||||||
Console.WriteLine($"📦 New Batch: {first.Kind} [{range}] ({writable})");
|
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
if (member.Kind == ModbusKind.Coil)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"👀 Coil: {member.StartAddress}, Name: {member.Member.Name}, Writable: {member.IsWritable}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Batch<R> MakeBatch<R>(ModbusClient modbusClient, IReadOnlyList<ModbusMember> members)
|
private static Batch<R> MakeBatch<R>(ModbusClient modbusClient, IReadOnlyList<ModbusMember> members)
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +93,7 @@ public static class Batches
|
||||||
//var isWritable = kind is ModbusKind.HoldingRegister or ModbusKind.Coil;
|
//var isWritable = kind is ModbusKind.HoldingRegister or ModbusKind.Coil;
|
||||||
var debugString = $"{kind}: {startAddress}" + (endAddress - 1 == startAddress ? "" : $"-{endAddress - 1}");
|
var debugString = $"{kind}: {startAddress}" + (endAddress - 1 == startAddress ? "" : $"-{endAddress - 1}");
|
||||||
|
|
||||||
var isWritable = members.Any(m => m.IsWritable);
|
var isWritable = kind == ModbusKind.Coil || members.Any(m => m.IsWritable);
|
||||||
//foreach (var m in members)
|
//foreach (var m in members)
|
||||||
// Console.WriteLine($"🧪 Address {m.StartAddress} Writable: {m.IsWritable}");
|
// Console.WriteLine($"🧪 Address {m.StartAddress} Writable: {m.IsWritable}");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@ namespace InnovEnergy.Lib.Protocols.Modbus.Slaves;
|
||||||
public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull
|
public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull
|
||||||
{
|
{
|
||||||
private readonly IReadOnlyList<Batch<R>> _Batches;
|
private readonly IReadOnlyList<Batch<R>> _Batches;
|
||||||
|
private readonly ModbusClient _modbusClient;
|
||||||
|
|
||||||
public ModbusDevice(ModbusClient modbusClient, Int32 addressOffset = 0)
|
public ModbusDevice(ModbusClient modbusClient, Int32 addressOffset = 0)
|
||||||
{
|
{
|
||||||
_Batches = modbusClient.MakeBatchesFor<R>(addressOffset);
|
_Batches = modbusClient.MakeBatchesFor<R>(addressOffset);
|
||||||
|
_modbusClient = modbusClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public R Read()
|
public R Read()
|
||||||
|
|
@ -40,6 +42,7 @@ public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull
|
||||||
//Console.WriteLine($"Reading { _Batches.Count } Modbus batches.");
|
//Console.WriteLine($"Reading { _Batches.Count } Modbus batches.");
|
||||||
foreach (var batch in _Batches)
|
foreach (var batch in _Batches)
|
||||||
{
|
{
|
||||||
|
_modbusClient.Channel.ClearBuffers();
|
||||||
batch.Read(record);
|
batch.Read(record);
|
||||||
//Thread.Sleep(30); // this added mainly for Growatt reading
|
//Thread.Sleep(30); // this added mainly for Growatt reading
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +53,7 @@ public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull
|
||||||
{
|
{
|
||||||
foreach (var batch in _Batches)
|
foreach (var batch in _Batches)
|
||||||
{
|
{
|
||||||
|
_modbusClient.Channel.ClearBuffers();
|
||||||
batch.Write(record);
|
batch.Write(record);
|
||||||
//Thread.Sleep(50); // this added mainly for Growatt reading
|
//Thread.Sleep(50); // this added mainly for Growatt reading
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
namespace InnovEnergy.Lib.Devices.Sinexcel_12K_TL.DataType;
|
||||||
|
|
||||||
|
public enum SinexcelAbnormalFlag : ushort
|
||||||
|
{
|
||||||
|
Normal = 0,
|
||||||
|
Abnormal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelInverterOperatingState : ushort
|
||||||
|
{
|
||||||
|
Standby = 0,
|
||||||
|
Ready = 1,
|
||||||
|
OffGrid = 2,
|
||||||
|
OnGrid = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelPvOperatingState : ushort
|
||||||
|
{
|
||||||
|
Standby = 0,
|
||||||
|
Run = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelBatteryOperatingState : ushort
|
||||||
|
{
|
||||||
|
Standby = 0,
|
||||||
|
Charging = 1,
|
||||||
|
Discharging = 2,
|
||||||
|
FullyCharged = 3,
|
||||||
|
SelfInspection = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelDcdcState : ushort
|
||||||
|
{
|
||||||
|
Shutdown = 0,
|
||||||
|
Booting = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelPowerGridStatus : ushort
|
||||||
|
{
|
||||||
|
Normal = 0,
|
||||||
|
Abnormal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SinexcelOredState : ushort
|
||||||
|
{
|
||||||
|
State0 = 0, State1, State2, State3, State4,
|
||||||
|
State5, State6, State7, State8, State9
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,192 @@ namespace InnovEnergy.Lib.Devices.Sinexcel_12K_TL;
|
||||||
public partial class SinexcelRecord
|
public partial class SinexcelRecord
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// --------------------------------------------------------
|
||||||
|
private static ushort Bit(ushort v) => (ushort)(v & 0x01);
|
||||||
|
|
||||||
|
private static ushort Compose3(ushort b0, ushort b1, ushort b2)
|
||||||
|
=> (ushort)(Bit(b0) | (Bit(b1) << 1) | (Bit(b2) << 2));
|
||||||
|
|
||||||
|
private static ushort Compose4(ushort b0, ushort b1, ushort b2, ushort b3)
|
||||||
|
=> (ushort)(Bit(b0) | (Bit(b1) << 1) | (Bit(b2) << 2) | (Bit(b3) << 3));
|
||||||
|
|
||||||
|
// ---- Operating states
|
||||||
|
public SinexcelInverterOperatingState InverterOperatingState
|
||||||
|
=> (SinexcelInverterOperatingState)(_inverterOperatingState & 0x07);
|
||||||
|
|
||||||
|
public SinexcelPvOperatingState Pv1OperatingState
|
||||||
|
=> (SinexcelPvOperatingState)Bit(_pv1OperatingState);
|
||||||
|
|
||||||
|
public SinexcelPvOperatingState Pv2OperatingState
|
||||||
|
=> (SinexcelPvOperatingState)Bit(_pv2OperatingState);
|
||||||
|
|
||||||
|
public SinexcelPvOperatingState Pv3OperatingState
|
||||||
|
=> (SinexcelPvOperatingState)Bit(_pv3OperatingState);
|
||||||
|
|
||||||
|
public SinexcelPvOperatingState Pv4OperatingState
|
||||||
|
=> (SinexcelPvOperatingState)Bit(_pv4OperatingState);
|
||||||
|
|
||||||
|
public SinexcelBatteryOperatingState Battery1OperatingState
|
||||||
|
=> (SinexcelBatteryOperatingState)(_battery1OperatingState & 0x07);
|
||||||
|
|
||||||
|
public SinexcelBatteryOperatingState Battery2OperatingState
|
||||||
|
=> (SinexcelBatteryOperatingState)Compose3(_bat2StateBit0, _bat2StateBit1, _bat2StateBit2);
|
||||||
|
|
||||||
|
public SinexcelOredState OredState
|
||||||
|
=> (SinexcelOredState)Compose4(_oredStateBit0, _oredStateBit1, _oredStateBit2, _oredStateBit3);
|
||||||
|
|
||||||
|
public SinexcelDcdcState DcdcStateDc
|
||||||
|
=> (SinexcelDcdcState)Bit(_dcdcStateDc);
|
||||||
|
|
||||||
|
public SinexcelDcdcState DcdcStateAc
|
||||||
|
=> (SinexcelDcdcState)Bit(_dcdcStateAc);
|
||||||
|
|
||||||
|
public SinexcelPowerGridStatus PowerGridStatus
|
||||||
|
=> (SinexcelPowerGridStatus)Bit(_powerGridStatus);
|
||||||
|
|
||||||
|
// ---- Alarm / fault flags
|
||||||
|
public SinexcelAbnormalFlag AbnormalGridVoltage => (SinexcelAbnormalFlag)Bit(_abnormalGridVoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalGridFrequency => (SinexcelAbnormalFlag)Bit(_abnormalGridFrequency);
|
||||||
|
public SinexcelAbnormalFlag InvertedSequenceOfGridVoltage => (SinexcelAbnormalFlag)Bit(_invertedSequenceOfGridVoltage);
|
||||||
|
public SinexcelAbnormalFlag GridVoltagePhaseLoss => (SinexcelAbnormalFlag)Bit(_gridVoltagePhaseLoss);
|
||||||
|
public SinexcelAbnormalFlag AbnormalOutputVoltage => (SinexcelAbnormalFlag)Bit(_abnormalOutputVoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalOutputFrequency => (SinexcelAbnormalFlag)Bit(_abnormalOutputFrequency);
|
||||||
|
public SinexcelAbnormalFlag AbnormalNullLine => (SinexcelAbnormalFlag)Bit(_abnormalNullLine);
|
||||||
|
public SinexcelAbnormalFlag ExcessivelyHighAmbientTemperature => (SinexcelAbnormalFlag)Bit(_excessivelyHighAmbientTemperature);
|
||||||
|
public SinexcelAbnormalFlag ExcessiveRadiatorTemperature => (SinexcelAbnormalFlag)Bit(_excessiveRadiatorTemperature);
|
||||||
|
public SinexcelAbnormalFlag InsulationFault => (SinexcelAbnormalFlag)Bit(_insulationFault);
|
||||||
|
public SinexcelAbnormalFlag LeakageProtectionFault => (SinexcelAbnormalFlag)Bit(_leakageProtectionFault);
|
||||||
|
public SinexcelAbnormalFlag AuxiliaryPowerFault => (SinexcelAbnormalFlag)Bit(_auxiliaryPowerFault);
|
||||||
|
public SinexcelAbnormalFlag FanFault => (SinexcelAbnormalFlag)Bit(_fanFault);
|
||||||
|
public SinexcelAbnormalFlag ModelCapacityFault => (SinexcelAbnormalFlag)Bit(_modelCapacityFault);
|
||||||
|
public SinexcelAbnormalFlag AbnormalLightningArrester => (SinexcelAbnormalFlag)Bit(_abnormalLightningArrester);
|
||||||
|
public SinexcelAbnormalFlag IslandProtection => (SinexcelAbnormalFlag)Bit(_islandProtection);
|
||||||
|
public SinexcelAbnormalFlag Battery1NotConnected => (SinexcelAbnormalFlag)Bit(_battery1NotConnected);
|
||||||
|
public SinexcelAbnormalFlag Battery1Overvoltage => (SinexcelAbnormalFlag)Bit(_battery1Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag Battery1Undervoltage => (SinexcelAbnormalFlag)Bit(_battery1Undervoltage);
|
||||||
|
public SinexcelAbnormalFlag Battery1DischargeEnd => (SinexcelAbnormalFlag)Bit(_battery1DischargeEnd);
|
||||||
|
public SinexcelAbnormalFlag Battery1Inverted => (SinexcelAbnormalFlag)Bit(_battery1Inverted);
|
||||||
|
public SinexcelAbnormalFlag Battery2NotConnected => (SinexcelAbnormalFlag)Bit(_battery2NotConnected);
|
||||||
|
public SinexcelAbnormalFlag Battery2Overvoltage => (SinexcelAbnormalFlag)Bit(_battery2Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag Battery2Undervoltage => (SinexcelAbnormalFlag)Bit(_battery2Undervoltage);
|
||||||
|
public SinexcelAbnormalFlag Battery2DischargeEnd => (SinexcelAbnormalFlag)Bit(_battery2DischargeEnd);
|
||||||
|
public SinexcelAbnormalFlag Battery2Inverted => (SinexcelAbnormalFlag)Bit(_battery2Inverted);
|
||||||
|
public SinexcelAbnormalFlag Pv1NotAccessed => (SinexcelAbnormalFlag)Bit(_pv1NotAccessed);
|
||||||
|
public SinexcelAbnormalFlag Pv1Overvoltage => (SinexcelAbnormalFlag)Bit(_pv1Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalPv1CurrentSharing => (SinexcelAbnormalFlag)Bit(_abnormalPv1CurrentSharing);
|
||||||
|
public SinexcelAbnormalFlag Pv2NotAccessed => (SinexcelAbnormalFlag)Bit(_pv2NotAccessed);
|
||||||
|
public SinexcelAbnormalFlag Pv2Overvoltage => (SinexcelAbnormalFlag)Bit(_pv2Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalPv2CurrentSharing => (SinexcelAbnormalFlag)Bit(_abnormalPv2CurrentSharing);
|
||||||
|
public SinexcelAbnormalFlag DcBusOvervoltage => (SinexcelAbnormalFlag)Bit(_dcBusOvervoltage);
|
||||||
|
public SinexcelAbnormalFlag DcBusUndervoltage => (SinexcelAbnormalFlag)Bit(_dcBusUndervoltage);
|
||||||
|
public SinexcelAbnormalFlag DcBusVoltageUnbalance => (SinexcelAbnormalFlag)Bit(_dcBusVoltageUnbalance);
|
||||||
|
public SinexcelAbnormalFlag Pv1PowerTubeFault => (SinexcelAbnormalFlag)Bit(_pv1PowerTubeFault);
|
||||||
|
public SinexcelAbnormalFlag Pv2PowerTubeFault => (SinexcelAbnormalFlag)Bit(_pv2PowerTubeFault);
|
||||||
|
public SinexcelAbnormalFlag Battery1PowerTubeFault => (SinexcelAbnormalFlag)Bit(_battery1PowerTubeFault);
|
||||||
|
public SinexcelAbnormalFlag Battery2PowerTubeFault => (SinexcelAbnormalFlag)Bit(_battery2PowerTubeFault);
|
||||||
|
public SinexcelAbnormalFlag InverterPowerTubeFault => (SinexcelAbnormalFlag)Bit(_inverterPowerTubeFault);
|
||||||
|
public SinexcelAbnormalFlag SystemOutputOverload => (SinexcelAbnormalFlag)Bit(_systemOutputOverload);
|
||||||
|
public SinexcelAbnormalFlag InverterOverload => (SinexcelAbnormalFlag)Bit(_inverterOverload);
|
||||||
|
public SinexcelAbnormalFlag InverterOverloadTimeout => (SinexcelAbnormalFlag)Bit(_inverterOverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Battery1OverloadTimeout => (SinexcelAbnormalFlag)Bit(_battery1OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Battery2OverloadTimeout => (SinexcelAbnormalFlag)Bit(_battery2OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag InverterSoftStartFailure => (SinexcelAbnormalFlag)Bit(_inverterSoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Battery1SoftStartFailure => (SinexcelAbnormalFlag)Bit(_battery1SoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Battery2SoftStartFailure => (SinexcelAbnormalFlag)Bit(_battery2SoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Dsp1ParameterSettingFault => (SinexcelAbnormalFlag)Bit(_dsp1ParameterSettingFault);
|
||||||
|
public SinexcelAbnormalFlag Dsp2ParameterSettingFault => (SinexcelAbnormalFlag)Bit(_dsp2ParameterSettingFault);
|
||||||
|
public SinexcelAbnormalFlag DspVersionCompatibilityFault => (SinexcelAbnormalFlag)Bit(_dspVersionCompatibilityFault);
|
||||||
|
public SinexcelAbnormalFlag CpldVersionCompatibilityFault => (SinexcelAbnormalFlag)Bit(_cpldVersionCompatibilityFault);
|
||||||
|
public SinexcelAbnormalFlag CpldCommunicationFault => (SinexcelAbnormalFlag)Bit(_cpldCommunicationFault);
|
||||||
|
public SinexcelAbnormalFlag DspCommunicationFault => (SinexcelAbnormalFlag)Bit(_dspCommunicationFault);
|
||||||
|
public SinexcelAbnormalFlag OutputVoltageDcOverlimit => (SinexcelAbnormalFlag)Bit(_outputVoltageDcOverlimit);
|
||||||
|
public SinexcelAbnormalFlag OutputCurrentDcOverlimit => (SinexcelAbnormalFlag)Bit(_outputCurrentDcOverlimit);
|
||||||
|
public SinexcelAbnormalFlag RelaySelfCheckFails => (SinexcelAbnormalFlag)Bit(_relaySelfCheckFails);
|
||||||
|
public SinexcelAbnormalFlag AbnormalInverter => (SinexcelAbnormalFlag)Bit(_abnormalInverter);
|
||||||
|
public SinexcelAbnormalFlag PoorGrounding => (SinexcelAbnormalFlag)Bit(_poorGrounding);
|
||||||
|
public SinexcelAbnormalFlag Pv1SoftStartFailure => (SinexcelAbnormalFlag)Bit(_pv1SoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Pv2SoftStartFailure => (SinexcelAbnormalFlag)Bit(_pv2SoftStartFailure2);
|
||||||
|
public SinexcelAbnormalFlag BalancedCircuitOverloadTimeout => (SinexcelAbnormalFlag)Bit(_balancedCircuitOverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Pv1OverloadTimeout => (SinexcelAbnormalFlag)Bit(_pv1OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Pv2OverloadTimeout => (SinexcelAbnormalFlag)Bit(_pv2OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag PcbOvertemperature => (SinexcelAbnormalFlag)Bit(_pcbOvertemperature);
|
||||||
|
public SinexcelAbnormalFlag DcConverterOvertemperature => (SinexcelAbnormalFlag)Bit(_dcConverterOvertemperature);
|
||||||
|
public SinexcelAbnormalFlag BusSlowOvervoltage => (SinexcelAbnormalFlag)Bit(_busSlowOvervoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalOffGridOutputVoltage => (SinexcelAbnormalFlag)Bit(_abnormalOffGridOutputVoltage);
|
||||||
|
public SinexcelAbnormalFlag HardwareBusOvervoltage => (SinexcelAbnormalFlag)Bit(_hardwareBusOvervoltage);
|
||||||
|
public SinexcelAbnormalFlag HardwareOvercurrent => (SinexcelAbnormalFlag)Bit(_hardwareOvercurrent);
|
||||||
|
public SinexcelAbnormalFlag DcConverterOvervoltage => (SinexcelAbnormalFlag)Bit(_dcConverterOvervoltage);
|
||||||
|
public SinexcelAbnormalFlag DcConverterHardwareOvervoltage => (SinexcelAbnormalFlag)Bit(_dcConverterHardwareOvervoltage);
|
||||||
|
public SinexcelAbnormalFlag DcConverterOvercurrent => (SinexcelAbnormalFlag)Bit(_dcConverterOvercurrent);
|
||||||
|
public SinexcelAbnormalFlag DcConverterHardwareOvercurrent => (SinexcelAbnormalFlag)Bit(_dcConverterHardwareOvercurrent);
|
||||||
|
public SinexcelAbnormalFlag DcConverterResonatorOvercurrent => (SinexcelAbnormalFlag)Bit(_dcConverterResonatorOvercurrent);
|
||||||
|
public SinexcelAbnormalFlag Pv1InsufficientPower => (SinexcelAbnormalFlag)Bit(_pv1InsufficientPower);
|
||||||
|
public SinexcelAbnormalFlag Pv2InsufficientPower => (SinexcelAbnormalFlag)Bit(_pv2InsufficientPower);
|
||||||
|
public SinexcelAbnormalFlag Battery1InsufficientPower => (SinexcelAbnormalFlag)Bit(_battery1InsufficientPower);
|
||||||
|
public SinexcelAbnormalFlag Battery2InsufficientPower => (SinexcelAbnormalFlag)Bit(_battery2InsufficientPower);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery1ChargeForbidden => (SinexcelAbnormalFlag)Bit(_lithiumBattery1ChargeForbidden);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery1DischargeForbidden => (SinexcelAbnormalFlag)Bit(_lithiumBattery1DischargeForbidden);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery2ChargeForbidden => (SinexcelAbnormalFlag)Bit(_lithiumBattery2ChargeForbidden);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery2DischargeForbidden => (SinexcelAbnormalFlag)Bit(_lithiumBattery2DischargeForbidden);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery1Full => (SinexcelAbnormalFlag)Bit(_lithiumBattery1Full);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery1DischargeEnd => (SinexcelAbnormalFlag)Bit(_lithiumBattery1DischargeEnd);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery2Full => (SinexcelAbnormalFlag)Bit(_lithiumBattery2Full);
|
||||||
|
public SinexcelAbnormalFlag LithiumBattery2DischargeEnd => (SinexcelAbnormalFlag)Bit(_lithiumBattery2DischargeEnd);
|
||||||
|
public SinexcelAbnormalFlag LoadPowerOverload => (SinexcelAbnormalFlag)Bit(_loadPowerOverload);
|
||||||
|
public SinexcelAbnormalFlag AbnormalLeakageSelfCheck => (SinexcelAbnormalFlag)Bit(_abnormalLeakageSelfCheck);
|
||||||
|
public SinexcelAbnormalFlag InverterOvertemperatureAlarm => (SinexcelAbnormalFlag)Bit(_inverterOvertemperatureAlarm);
|
||||||
|
public SinexcelAbnormalFlag InverterOvertemperature => (SinexcelAbnormalFlag)Bit(_inverterOvertemperature);
|
||||||
|
public SinexcelAbnormalFlag DcConverterOvertemperatureAlarm => (SinexcelAbnormalFlag)Bit(_dcConverterOvertemperatureAlarm);
|
||||||
|
public SinexcelAbnormalFlag ParallelCommunicationAlarm => (SinexcelAbnormalFlag)Bit(_parallelCommunicationAlarm);
|
||||||
|
public SinexcelAbnormalFlag SystemDerating => (SinexcelAbnormalFlag)Bit(_systemDerating);
|
||||||
|
public SinexcelAbnormalFlag InverterRelayOpen => (SinexcelAbnormalFlag)Bit(_inverterRelayOpen);
|
||||||
|
public SinexcelAbnormalFlag InverterRelayShortCircuit => (SinexcelAbnormalFlag)Bit(_inverterRelayShortCircuit);
|
||||||
|
public SinexcelAbnormalFlag PvAccessMethodErrorAlarm => (SinexcelAbnormalFlag)Bit(_pvAccessMethodErrorAlarm);
|
||||||
|
public SinexcelAbnormalFlag ParallelModuleMissing => (SinexcelAbnormalFlag)Bit(_parallelModuleMissing);
|
||||||
|
public SinexcelAbnormalFlag DuplicateMachineNumbersForParallelModules=> (SinexcelAbnormalFlag)Bit(_duplicateMachineNumbersForParallelModules);
|
||||||
|
public SinexcelAbnormalFlag ParameterConflictInParallelModule => (SinexcelAbnormalFlag)Bit(_parameterConflictInParallelModule);
|
||||||
|
public SinexcelAbnormalFlag ReservedAlarms4 => (SinexcelAbnormalFlag)Bit(_reservedAlarms4);
|
||||||
|
public SinexcelAbnormalFlag ReverseMeterConnection => (SinexcelAbnormalFlag)Bit(_reverseMeterConnection);
|
||||||
|
public SinexcelAbnormalFlag InverterSealPulse => (SinexcelAbnormalFlag)Bit(_inverterSealPulse);
|
||||||
|
public SinexcelAbnormalFlag Pv3NotConnected => (SinexcelAbnormalFlag)Bit(_pv3NotConnected);
|
||||||
|
public SinexcelAbnormalFlag Pv3Overvoltage => (SinexcelAbnormalFlag)Bit(_pv3Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag Pv3AverageCurrentAnomaly => (SinexcelAbnormalFlag)Bit(_pv3AverageCurrentAnomaly);
|
||||||
|
public SinexcelAbnormalFlag Pv4NotConnected => (SinexcelAbnormalFlag)Bit(_pv4NotConnected);
|
||||||
|
public SinexcelAbnormalFlag Pv4Overvoltage => (SinexcelAbnormalFlag)Bit(_pv4Overvoltage);
|
||||||
|
public SinexcelAbnormalFlag Pv4AverageCurrentAnomaly => (SinexcelAbnormalFlag)Bit(_pv4AverageCurrentAnomaly);
|
||||||
|
public SinexcelAbnormalFlag Pv3PowerTubeFailure => (SinexcelAbnormalFlag)Bit(_pv3PowerTubeFailure);
|
||||||
|
public SinexcelAbnormalFlag Pv4PowerTubeFailure => (SinexcelAbnormalFlag)Bit(_pv4PowerTubeFailure);
|
||||||
|
public SinexcelAbnormalFlag Pv3SoftStartFailure => (SinexcelAbnormalFlag)Bit(_pv3SoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Pv4SoftStartFailure => (SinexcelAbnormalFlag)Bit(_pv4SoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag Pv3OverloadTimeout => (SinexcelAbnormalFlag)Bit(_pv3OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Pv4OverloadTimeout => (SinexcelAbnormalFlag)Bit(_pv4OverloadTimeout);
|
||||||
|
public SinexcelAbnormalFlag Pv3ReverseConnection => (SinexcelAbnormalFlag)Bit(_pv3ReverseConnection);
|
||||||
|
public SinexcelAbnormalFlag Pv4ReverseConnection => (SinexcelAbnormalFlag)Bit(_pv4ReverseConnection);
|
||||||
|
public SinexcelAbnormalFlag AbnormalDieselGeneratorVoltage => (SinexcelAbnormalFlag)Bit(_abnormalDieselGeneratorVoltage);
|
||||||
|
public SinexcelAbnormalFlag AbnormalDieselGeneratorFrequency => (SinexcelAbnormalFlag)Bit(_abnormalDieselGeneratorFrequency);
|
||||||
|
public SinexcelAbnormalFlag DieselGeneratorVoltageReverseSequence => (SinexcelAbnormalFlag)Bit(_dieselGeneratorVoltageReverseSequence);
|
||||||
|
public SinexcelAbnormalFlag DieselGeneratorVoltageOutOfPhase => (SinexcelAbnormalFlag)Bit(_dieselGeneratorVoltageOutOfPhase);
|
||||||
|
public SinexcelAbnormalFlag LeadBatteryTemperatureAbnormality => (SinexcelAbnormalFlag)Bit(_leadBatteryTemperatureAbnormality);
|
||||||
|
public SinexcelAbnormalFlag BatteryAccessMethodError => (SinexcelAbnormalFlag)Bit(_batteryAccessMethodError);
|
||||||
|
public SinexcelAbnormalFlag ReservedAlarms5 => (SinexcelAbnormalFlag)Bit(_reservedAlarms5);
|
||||||
|
public SinexcelAbnormalFlag Battery1BackupProhibited => (SinexcelAbnormalFlag)Bit(_battery1BackupProhibited);
|
||||||
|
public SinexcelAbnormalFlag Battery2BackupProhibited => (SinexcelAbnormalFlag)Bit(_battery2BackupProhibited);
|
||||||
|
public SinexcelAbnormalFlag AbnormalGridCurrent => (SinexcelAbnormalFlag)Bit(_abnormalGridCurrent);
|
||||||
|
public SinexcelAbnormalFlag GeneratorOverload => (SinexcelAbnormalFlag)Bit(_generatorOverload);
|
||||||
|
public SinexcelAbnormalFlag BusSoftStartFailure => (SinexcelAbnormalFlag)Bit(_busSoftStartFailure);
|
||||||
|
public SinexcelAbnormalFlag OpenCircuitOfPowerGridRelay => (SinexcelAbnormalFlag)Bit(_openCircuitOfPowerGridRelay);
|
||||||
|
public SinexcelAbnormalFlag ShortCircuitOfPowerGridRelay => (SinexcelAbnormalFlag)Bit(_shortCircuitOfPowerGridRelay);
|
||||||
|
public SinexcelAbnormalFlag GeneratorRelayOpenCircuit => (SinexcelAbnormalFlag)Bit(_generatorRelayOpenCircuit);
|
||||||
|
public SinexcelAbnormalFlag GeneratorRelayShortCircuit => (SinexcelAbnormalFlag)Bit(_generatorRelayShortCircuit);
|
||||||
|
public SinexcelAbnormalFlag InsufficientPhotovoltaicPower => (SinexcelAbnormalFlag)Bit(_insufficientPhotovoltaicPower);
|
||||||
|
public SinexcelAbnormalFlag Photovoltaic1Overcurrent => (SinexcelAbnormalFlag)Bit(_photovoltaic1Overcurrent);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private Int16 _factorFromKwtoW = 1000;
|
private Int16 _factorFromKwtoW = 1000;
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Public API — Decoded Float Values
|
// Public API — Decoded Float Values
|
||||||
|
|
@ -190,14 +376,15 @@ public partial class SinexcelRecord
|
||||||
public ActivePower TotalGridPower => ConvertBitPatternToFloat(_totalGridPower) * _factorFromKwtoW ;
|
public ActivePower TotalGridPower => ConvertBitPatternToFloat(_totalGridPower) * _factorFromKwtoW ;
|
||||||
public ActivePower ImportantLoadTotalPower => ConvertBitPatternToFloat(_importantLoadTotalPower)* _factorFromKwtoW;
|
public ActivePower ImportantLoadTotalPower => ConvertBitPatternToFloat(_importantLoadTotalPower)* _factorFromKwtoW;
|
||||||
public ActivePower GeneralLoadTotalPower => ConvertBitPatternToFloat(_generalLoadTotalPower)* _factorFromKwtoW;
|
public ActivePower GeneralLoadTotalPower => ConvertBitPatternToFloat(_generalLoadTotalPower)* _factorFromKwtoW;
|
||||||
public Voltage Pv3Voltage => ConvertBitPatternToFloat(_pv3Voltage);
|
public Voltage PvVoltage3 => ConvertBitPatternToFloat(_pv3Voltage);
|
||||||
public Current Pv3Current => ConvertBitPatternToFloat(_pv3Current);
|
public Current PvCurrent3 => ConvertBitPatternToFloat(_pv3Current);
|
||||||
public ActivePower Pv3Power => ConvertBitPatternToFloat(_pv3Power) * _factorFromKwtoW;
|
public ActivePower PvPower3 => ConvertBitPatternToFloat(_pv3Power) * _factorFromKwtoW;
|
||||||
public Voltage Pv4Voltage => ConvertBitPatternToFloat(_pv4Voltage);
|
public Voltage PvVoltage4 => ConvertBitPatternToFloat(_pv4Voltage);
|
||||||
public Current Pv4Current => ConvertBitPatternToFloat(_pv4Current);
|
public Current PvCurrent4 => ConvertBitPatternToFloat(_pv4Current);
|
||||||
public ActivePower Pv4Power => ConvertBitPatternToFloat(_pv4Power) * _factorFromKwtoW;
|
public ActivePower PvPower4 => ConvertBitPatternToFloat(_pv4Power) * _factorFromKwtoW;
|
||||||
public ActivePower GeneratorTotalPower => ConvertBitPatternToFloat(_generatorTotalPower);
|
public ActivePower GeneratorTotalPower => ConvertBitPatternToFloat(_generatorTotalPower);
|
||||||
|
|
||||||
|
public ActivePower PvTotalPower => PvPower1 + PvPower2 + PvPower3 + PvPower4;
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Manufacturer Information & Software Versions
|
// Manufacturer Information & Software Versions
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
|
|
@ -215,269 +402,292 @@ public partial class SinexcelRecord
|
||||||
public ApparentPower RatedPowerKva => _ratedPower / 100f; // 0x2008 (value / 100)
|
public ApparentPower RatedPowerKva => _ratedPower / 100f; // 0x2008 (value / 100)
|
||||||
public SinexcelModel Model => (SinexcelModel)_model; // 0x200D
|
public SinexcelModel Model => (SinexcelModel)_model; // 0x200D
|
||||||
|
|
||||||
//public ThreePhaseWireSystem ThreePhaseWireSystem
|
public ThreePhaseWireSystem ThreePhaseWireSystem
|
||||||
//{
|
{
|
||||||
// get => (ThreePhaseWireSystem)_threePhaseWireSystem;
|
get => (ThreePhaseWireSystem)_threePhaseWireSystem;
|
||||||
// //set => _threePhaseWireSystem = (UInt32)value;
|
set => _threePhaseWireSystem = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
//
|
||||||
//public InputFrequency InputFrequencyClass
|
public InputFrequency InputFrequencyClass
|
||||||
//{
|
{
|
||||||
// get => (InputFrequency)_inputFrequencyClass;
|
get => (InputFrequency)_inputFrequencyClass;
|
||||||
// //set => _inputFrequencyClass = (UInt32)value;
|
set => _inputFrequencyClass = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
//
|
||||||
public WorkingMode WorkingMode
|
public WorkingMode WorkingMode
|
||||||
{
|
{
|
||||||
get => (WorkingMode)(int)BitConverter.Int32BitsToSingle(unchecked((int)_workingMode));
|
get => (WorkingMode)(Int32)BitConverter.Int32BitsToSingle(unchecked((Int32)_workingMode));
|
||||||
set => _workingMode = BitConverter.ToUInt32(BitConverter.GetBytes((float)value), 0);
|
set => _workingMode = BitConverter.ToUInt32(BitConverter.GetBytes((Single)value), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public GridSwitchMethod GridSwitchMethod
|
||||||
|
{
|
||||||
|
get => (GridSwitchMethod)(Int32)BitConverter.Int32BitsToSingle(unchecked((Int32)_methodSwitchMode));
|
||||||
|
set => _methodSwitchMode = BitConverter.ToUInt32(BitConverter.GetBytes((Single)value), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Inverter Control Configuration
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
public SinexcelUnbalanceCompensation EnableOnGridUnbalanceCompensation
|
||||||
|
{
|
||||||
|
get => (SinexcelUnbalanceCompensation)_enableOnGridUnbalanceCompensation;
|
||||||
|
set => _enableOnGridUnbalanceCompensation = (UInt32)value;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
public SinexcelTemperatureDrop TemperatureDrop
|
||||||
|
{
|
||||||
|
get => (SinexcelTemperatureDrop)ConvertBitPatternToFloat(_temperatureDrop);
|
||||||
|
//set => _temperatureDrop = (UInt32)value;
|
||||||
|
}
|
||||||
//
|
//
|
||||||
//public GridSwitchMethod GridSwitchMethod
|
public SinexcelHvrt Hvrt
|
||||||
//{
|
{
|
||||||
// get => (GridSwitchMethod)_methodSwitchMode;
|
get => (SinexcelHvrt)_hvrt;
|
||||||
// //set => _methodSwitchMode = (UInt32)value;
|
// set => _hvrt = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
//
|
||||||
//// ───────────────────────────────────────────────
|
public SinexcelLvrt Lvrt
|
||||||
//// Inverter Control Configuration
|
{
|
||||||
//// ───────────────────────────────────────────────
|
get => (SinexcelLvrt)_lvrt;
|
||||||
//public SinexcelUnbalanceCompensation EnableOnGridUnbalanceCompensation
|
// set => _lvrt = (UInt32)value;
|
||||||
//{
|
}
|
||||||
// get => (SinexcelUnbalanceCompensation)_enableOnGridUnbalanceCompensation;
|
|
||||||
// //set => _enableOnGridUnbalanceCompensation = (UInt32)value;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public SinexcelTemperatureDrop TemperatureDrop
|
|
||||||
//{
|
|
||||||
// get => (SinexcelTemperatureDrop)ConvertBitPatternToFloat(_temperatureDrop);
|
|
||||||
// //set => _temperatureDrop = (UInt32)value;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public SinexcelHvrt Hvrt
|
|
||||||
//{
|
|
||||||
// get => (SinexcelHvrt)_hvrt;
|
|
||||||
// //set => _hvrt = (UInt32)value;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public SinexcelLvrt Lvrt
|
|
||||||
//{
|
|
||||||
// get => (SinexcelLvrt)_lvrt;
|
|
||||||
// //set => _lvrt = (UInt32)value;
|
|
||||||
//}
|
|
||||||
//
|
//
|
||||||
// F//an Gear — numeric [0.5 ~ 1.0], default 1.0
|
// F//an Gear — numeric [0.5 ~ 1.0], default 1.0
|
||||||
//public float FanGear
|
public float FanGear
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_fanGear));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_fanGear));
|
||||||
// //set => _fanGear = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
// set => _fanGear = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
*/
|
||||||
//// ───────────────────────────────────────────────
|
//// ───────────────────────────────────────────────
|
||||||
//// Battery Configuration
|
//// Battery Configuration
|
||||||
//// ───────────────────────────────────────────────
|
//// ───────────────────────────────────────────────
|
||||||
//public SinexcelBatteryAccessMethod BatteryAccessMethod
|
public SinexcelBatteryAccessMethod BatteryAccessMethod
|
||||||
//{
|
{
|
||||||
// get => (SinexcelBatteryAccessMethod)_batteryAccessMethod;
|
get => (SinexcelBatteryAccessMethod)_batteryAccessMethod;
|
||||||
// //set => _batteryAccessMethod = (UInt32)value;
|
set => _batteryAccessMethod = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelMeterAccessEnable MeterAccessEnable
|
public SinexcelMeterAccessEnable MeterAccessEnable
|
||||||
//{
|
{
|
||||||
// get => (SinexcelMeterAccessEnable)ConvertBitPatternToFloat(_meterAccessEnable);
|
get => (SinexcelMeterAccessEnable)ConvertBitPatternToFloat(_meterAccessEnable);
|
||||||
// //set => _meterAccessEnable = (UInt32)value;
|
set => _meterAccessEnable = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelBatteryEnable EnableBattery1
|
public SinexcelBatteryEnable EnableBattery1
|
||||||
//{
|
{
|
||||||
// get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery1);
|
get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery1);
|
||||||
// //set => _enableBattery1 = (UInt32)value;
|
set => _enableBattery1 = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelBatteryEnable EnableBattery2
|
public SinexcelBatteryEnable EnableBattery2
|
||||||
//{
|
{
|
||||||
// get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery2);
|
get => (SinexcelBatteryEnable)ConvertBitPatternToFloat(_enableBattery2);
|
||||||
// //set => _enableBattery2 = (UInt32)value;
|
set => _enableBattery2 = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelPvEnable EnablePv1
|
public SinexcelPvEnable EnablePv1
|
||||||
//{
|
{
|
||||||
// get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv1);
|
get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv1);
|
||||||
// //set => _enablePv1 = (UInt32)value;
|
set => _enablePv1 = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelPvEnable EnablePv2
|
public SinexcelPvEnable EnablePv2
|
||||||
//{
|
{
|
||||||
// get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv2);
|
get => (SinexcelPvEnable)ConvertBitPatternToFloat(_enablePv2);
|
||||||
// //set => _enablePv2 = (UInt32)value;
|
set => _enablePv2 = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelBatteryType BatteryType
|
public SinexcelBatteryType BatteryType
|
||||||
//{
|
{
|
||||||
// get => (SinexcelBatteryType)_batteryType;
|
get => (SinexcelBatteryType)_batteryType;
|
||||||
// //set => _batteryType = (UInt32)value;
|
set => _batteryType = (UInt32)value;
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float BatteryCapacity1
|
public float BatteryCapacity1
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCapacity1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCapacity1));
|
||||||
// //set => _batteryCapacity1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _batteryCapacity1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float Battery1MaxChargingCurrent
|
public float Battery1MaxChargingCurrent
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxChargingCurrentBattery1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxChargingCurrentBattery1));
|
||||||
// //set => _maxChargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _maxChargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float Battery1MaxDischargingCurrent
|
public float Battery1MaxDischargingCurrent
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxDischargingCurrentBattery1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_maxDischargingCurrentBattery1));
|
||||||
// //set => _maxDischargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _maxDischargingCurrentBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float RatedBatteryVoltage1
|
public float RatedBatteryVoltage1
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_ratedBatteryVoltage1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_ratedBatteryVoltage1));
|
||||||
// //set => _ratedBatteryVoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _ratedBatteryVoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float Battery1MinSoc
|
public float Battery1MinSoc
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_minSocBattery1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_minSocBattery1));
|
||||||
// //set => _minSocBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _minSocBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public float SetValueBattery1
|
public float SetValueBattery1
|
||||||
//{
|
{
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_setValueBattery1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_setValueBattery1));
|
||||||
// //set => _setValueBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _setValueBattery1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//public SinexcelActiveChargeDischarge ActiveChargeDischarge
|
/* public SinexcelActiveChargeDischarge ActiveChargeDischarge
|
||||||
//{
|
{
|
||||||
// get => (SinexcelActiveChargeDischarge)ConvertBitPatternToFloat(_activeChargeDischarge);
|
get => (SinexcelActiveChargeDischarge)(Int32)BitConverter.Int32BitsToSingle(unchecked((Int32)_activeChargeDischarge));
|
||||||
// //set => _activeChargeDischarge = (UInt32)value;
|
set => _activeChargeDischarge = BitConverter.ToUInt32(BitConverter.GetBytes((Single)value), 0);
|
||||||
//}
|
}*/
|
||||||
//
|
|
||||||
//public float ActiveChargeDischargePower
|
public float ActiveChargeDischargePower
|
||||||
//{
|
{
|
||||||
// get => ConvertBitPatternToFloat(_activeChargeDischargePower) * _factorFromKwtoW;
|
get => ConvertBitPatternToFloat(_activeChargeDischargePower) * _factorFromKwtoW;
|
||||||
// //set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); // we should check this may need to convert from W to KW
|
set => _activeChargeDischargePower = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); // we should check this may need to convert from W to KW
|
||||||
//}
|
}
|
||||||
//
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
// Protection & PV Mode
|
||||||
//// Protection & PV Mode
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
public SinexcelIslandProtection EnableIslandProtection
|
||||||
//public SinexcelIslandProtection EnableIslandProtection
|
{
|
||||||
//{
|
get => (SinexcelIslandProtection)_enableIslandProtection;
|
||||||
// get => (SinexcelIslandProtection)_enableIslandProtection;
|
set => _enableIslandProtection = (UInt32)value;
|
||||||
// //set => _enableIslandProtection = (UInt32)value;
|
}
|
||||||
//}
|
|
||||||
//
|
public SinexcelPvAccessMode PvAccessMode
|
||||||
//public SinexcelPvAccessMode PvAccessMode
|
{
|
||||||
//{
|
get => (SinexcelPvAccessMode)_pvAccessMode;
|
||||||
// get => (SinexcelPvAccessMode)_pvAccessMode;
|
set => _pvAccessMode = (UInt32)value;
|
||||||
// //set => _pvAccessMode = (UInt32)value;
|
}
|
||||||
//}
|
|
||||||
//
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
// System-Level Parameters
|
||||||
//// System-Level Parameters
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
public float OutputVoltageAdjustmentFactor
|
||||||
//public float OutputVoltageAdjustmentFactor
|
{
|
||||||
//{
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_outputVoltageAdjustmentFactor));
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_outputVoltageAdjustmentFactor));
|
set => _outputVoltageAdjustmentFactor = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// //set => _outputVoltageAdjustmentFactor = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
}
|
||||||
//}
|
|
||||||
//
|
public float SetValueBatteryUndervoltage1
|
||||||
//public float SetValueBatteryUndervoltage1
|
{
|
||||||
//{
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_setValueBatteryUndervoltage1));
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_setValueBatteryUndervoltage1));
|
set => _setValueBatteryUndervoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// //set => _setValueBatteryUndervoltage1 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
}
|
||||||
//}
|
|
||||||
//
|
public float InverterPowerLimit
|
||||||
//public float InverterPowerLimit
|
{
|
||||||
//{
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_inverterPowerLimit));
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_inverterPowerLimit));
|
set => _inverterPowerLimit = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// //set => _inverterPowerLimit = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
}
|
||||||
//}
|
|
||||||
//
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
// Battery 2 Parameters
|
||||||
//// Battery 2 Parameters
|
// ───────────────────────────────────────────────
|
||||||
//// ───────────────────────────────────────────────
|
public float Battery2Capacity
|
||||||
//public float Battery2Capacity
|
{
|
||||||
//{
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2Capacity));
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2Capacity));
|
set => _battery2Capacity = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// //set => _battery2Capacity = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
}
|
||||||
//}
|
public float Battery2MaxChargingCurrent
|
||||||
//
|
{
|
||||||
//public float Battery2MaxChargingCurrent
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_maxChargingCurrentBattery2));
|
||||||
//{
|
set => _maxChargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_maxChargingCurrentBattery2));
|
}
|
||||||
// //set => _maxChargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery2MaxDischargingCurrent
|
||||||
//
|
{
|
||||||
//public float Battery2MaxDischargingCurrent
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_maxDischargingCurrentBattery2));
|
||||||
//{
|
set => _maxDischargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_maxDischargingCurrentBattery2));
|
}
|
||||||
// //set => _maxDischargingCurrentBattery2 = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery2RatedVoltage
|
||||||
//
|
{
|
||||||
//public float Battery2RatedVoltage
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2RatedVoltage));
|
||||||
//{
|
set => _battery2RatedVoltage = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2RatedVoltage));
|
}
|
||||||
// //set => _battery2RatedVoltage = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery2MinSoc
|
||||||
//
|
{
|
||||||
//public float Battery2MinSoc
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2MinSoc));
|
||||||
//{
|
set => _battery2MinSoc = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2MinSoc));
|
}
|
||||||
// //set => _battery2MinSoc = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery2OverVoltageSetting
|
||||||
//
|
{
|
||||||
//public float Battery2OverVoltageSetting
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2OverVoltageSetting));
|
||||||
//{
|
set => _battery2OverVoltageSetting = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2OverVoltageSetting));
|
}
|
||||||
// //set => _battery2OverVoltageSetting = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery2UnderVoltageSetpoint
|
||||||
//
|
{
|
||||||
//public float Battery2UnderVoltageSetpoint
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2UnderVoltageSetpoint));
|
||||||
//{
|
set => _battery2UnderVoltageSetpoint = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2UnderVoltageSetpoint));
|
}
|
||||||
// //set => _battery2UnderVoltageSetpoint = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
public float Battery1BackupSoc
|
||||||
//
|
{
|
||||||
//// ───────────────────────────────────────────────
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery1BackupSOC));
|
||||||
//// Parallel / System Settings
|
set => _battery1BackupSOC = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//// ───────────────────────────────────────────────
|
}
|
||||||
//public SinexcelMachineMode MachineMode
|
|
||||||
//{
|
public float Battery2BackupSoc
|
||||||
// get => (SinexcelMachineMode)ConvertBitPatternToFloat(_singleOrParallelMachine);
|
{
|
||||||
// //set => _singleOrParallelMachine = (UInt32)value;
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_battery2BackupSOC));
|
||||||
//}
|
set => _battery2BackupSOC = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//
|
}
|
||||||
//public UInt32 NumberOfSystemModules
|
// to be tested
|
||||||
//{
|
public float EnableGridExport
|
||||||
// get => (UInt32)ConvertBitPatternToFloat(_numberOfSystemModules);
|
|
||||||
// //set => _numberOfSystemModules = value;
|
{
|
||||||
//}
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_enableGridExport));
|
||||||
//
|
set => _enableGridExport = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//public UInt32 ParallelModuleMachineNumber
|
}
|
||||||
//{
|
|
||||||
// get => (UInt32)ConvertBitPatternToFloat(_parallelModuleMachineNumber);
|
public float PowerGridExportLimit
|
||||||
// //set => _parallelModuleMachineNumber = value;
|
{
|
||||||
//}
|
get => BitConverter.Int32BitsToSingle(unchecked((int)_powerGridExportLimit));
|
||||||
//
|
set => _powerGridExportLimit = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//public AccreditedCountry AccreditedCountry
|
}
|
||||||
//{
|
|
||||||
// get => (AccreditedCountry)ConvertBitPatternToFloat(_accreditedCountries);
|
// ───────────────────────────────────────────────
|
||||||
// //set => _accreditedCountries = (UInt32)value;
|
// Parallel / System Settings
|
||||||
//}
|
// ───────────────────────────────────────────────
|
||||||
//
|
public SinexcelMachineMode MachineMode
|
||||||
//// ───────────────────────────────────────────────
|
{
|
||||||
//// Control Commands
|
get => (SinexcelMachineMode)ConvertBitPatternToFloat(_singleOrParallelMachine);
|
||||||
//// ───────────────────────────────────────────────
|
set => _singleOrParallelMachine = (UInt32)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt32 NumberOfSystemModules
|
||||||
|
{
|
||||||
|
get => (UInt32)ConvertBitPatternToFloat(_numberOfSystemModules);
|
||||||
|
set => _numberOfSystemModules = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt32 ParallelModuleMachineNumber
|
||||||
|
{
|
||||||
|
get => (UInt32)ConvertBitPatternToFloat(_parallelModuleMachineNumber);
|
||||||
|
set => _parallelModuleMachineNumber = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccreditedCountry AccreditedCountry
|
||||||
|
{
|
||||||
|
get => (AccreditedCountry)(Int32)BitConverter.Int32BitsToSingle(unchecked((Int32)_accreditedCountries));
|
||||||
|
set => _accreditedCountries = BitConverter.ToUInt32(BitConverter.GetBytes((Single)value), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
|
// Control Commands
|
||||||
|
// ───────────────────────────────────────────────
|
||||||
public float PowerOn
|
public float PowerOn
|
||||||
{
|
{
|
||||||
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOn));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOn));
|
||||||
|
|
@ -489,39 +699,40 @@ public partial class SinexcelRecord
|
||||||
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOff));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_powerOff));
|
||||||
set => _powerOff = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
set => _powerOff = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float FaultClearing
|
||||||
|
{
|
||||||
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_faultClearing));
|
||||||
|
set => _faultClearing = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
//public float FaultClearingf
|
// ───────────────────────────────────────────────
|
||||||
//{
|
// Meter & Battery Control
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_faultClearing));
|
// ───────────────────────────────────────────────
|
||||||
// // //set => _faultClearing = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
public float MeterReverseManualDetection
|
||||||
//}
|
{
|
||||||
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_meterReverseManualDetection));
|
||||||
|
set => _meterReverseManualDetection = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This causing the inverter to switch off, please consider testing it first before uncommenting
|
||||||
|
/*public SinexcelBatteryRating SinexcelBatteryRating
|
||||||
|
{
|
||||||
|
get => (SinexcelBatteryRating)(Int32)BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryVoltageRating));
|
||||||
|
set => _batteryVoltageRating = BitConverter.ToUInt32(BitConverter.GetBytes((Single)value), 0);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public float Battery1Activation
|
||||||
|
{
|
||||||
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery1Activation));
|
||||||
|
set => _battery1Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
//// ───────────────────────────────────────────────
|
public float Battery2Activation
|
||||||
//// Meter & Battery Control
|
{
|
||||||
//// ───────────────────────────────────────────────
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery2Activation));
|
||||||
//public float MeterReverseManualDetection
|
set => _battery2Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
||||||
//{
|
}
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_meterReverseManualDetection));
|
|
||||||
// //set => _meterReverseManualDetection = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public SinexcelBatteryRating BatteryVoltageRating
|
|
||||||
//{
|
|
||||||
// get => (SinexcelBatteryRating)ConvertBitPatternToFloat(_batteryVoltageRating);
|
|
||||||
// //set => _batteryVoltageRating = (UInt32)value;
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public float Battery1Activation
|
|
||||||
//{
|
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery1Activation));
|
|
||||||
// //set => _battery1Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//public float Battery2Activation
|
|
||||||
//{
|
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_battery2Activation));
|
|
||||||
// //set => _battery2Activation = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Electric Meter Operating State
|
// Electric Meter Operating State
|
||||||
|
|
@ -609,23 +820,9 @@ public partial class SinexcelRecord
|
||||||
// Pack Voltage / Current / Temperature
|
// Pack Voltage / Current / Temperature
|
||||||
public Voltage Battery2PackTotalVoltage => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalVoltage)); // 0xB200 (0.01 V resolution)
|
public Voltage Battery2PackTotalVoltage => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalVoltage)); // 0xB200 (0.01 V resolution)
|
||||||
public Current Battery2PackTotalCurrent => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalCurrent)); // 0xB202 (0.01 A resolution)
|
public Current Battery2PackTotalCurrent => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2PackTotalCurrent)); // 0xB202 (0.01 A resolution)
|
||||||
public Temperature Battery2Socsecondvalue => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soc)); // 0xB206 %
|
public Percent Battery2Socsecondvalue => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soc)); // 0xB206 %
|
||||||
public Percent Battery2Soh => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soh)); // 0xB208 %
|
public Percent Battery2Soh => BitConverter.Int32BitsToSingle(unchecked((Int32)_batteryCab2Soh)); // 0xB208 %
|
||||||
|
|
||||||
// Repetitive-week mask (bit-mapped 0–6 = Sun–Sat)
|
|
||||||
//public float TimedChargeAndDischargeOff
|
|
||||||
//{
|
|
||||||
// get => BitConverter.Int32BitsToSingle(unchecked((Int32)_Timed_Charge_and_Discharge_Off)) ; // only 7 bits used
|
|
||||||
// //set => _Timed_Charge_and_Discharge_Off = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
|
|
||||||
//}
|
|
||||||
// Repetitive-week mask (bit-mapped 0–6 = Sun–Sat)
|
|
||||||
/* public float OtherTimePeriodMode
|
|
||||||
{
|
|
||||||
get => _Other_time_period_mode ; // only 7 bits used
|
|
||||||
//set => _Other_time_period_mode = (UInt32)value;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Repetitive-week mask (bit-mapped 0–6 = Sun–Sat)
|
// Repetitive-week mask (bit-mapped 0–6 = Sun–Sat)
|
||||||
public SinexcelWeekDays RepetitiveWeeks
|
public SinexcelWeekDays RepetitiveWeeks
|
||||||
|
|
@ -633,20 +830,20 @@ public partial class SinexcelRecord
|
||||||
get => (SinexcelWeekDays)(_repetitiveWeeks & 0x7F); // only 7 bits used
|
get => (SinexcelWeekDays)(_repetitiveWeeks & 0x7F); // only 7 bits used
|
||||||
set => _repetitiveWeeks = (UInt32)value;
|
set => _repetitiveWeeks = (UInt32)value;
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// Effective start / end as UNIX timestamps
|
// Effective start / end as UNIX timestamps
|
||||||
public DateTime EffectiveStartDate
|
public DateTime EffectiveStartDate
|
||||||
{
|
{
|
||||||
get => DateTimeOffset.FromUnixTimeSeconds(_effectiveStartDate).DateTime;
|
get => DateTimeOffset.FromUnixTimeSeconds(_effectiveStartDate).DateTime;
|
||||||
set => _effectiveStartDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds();
|
set => _effectiveStartDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
public DateTime EffectiveEndDate
|
public DateTime EffectiveEndDate
|
||||||
{
|
{
|
||||||
get => DateTimeOffset.FromUnixTimeSeconds(_effectiveEndDate).DateTime;
|
get => DateTimeOffset.FromUnixTimeSeconds(_effectiveEndDate).DateTime;
|
||||||
set => _effectiveEndDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds();
|
set => _effectiveEndDate = (UInt32)new DateTimeOffset(value).ToUnixTimeSeconds();
|
||||||
}
|
}
|
||||||
// // Charging power during time period 1 (kW)
|
// Charging power during time period 1 (kW)
|
||||||
public float ChargingPowerPeriod1
|
public float ChargingPowerPeriod1
|
||||||
{
|
{
|
||||||
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_chargingPowerPeriod1));
|
get => BitConverter.Int32BitsToSingle(unchecked((Int32)_chargingPowerPeriod1));
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,178 @@ namespace InnovEnergy.Lib.Devices.Sinexcel_12K_TL;
|
||||||
[BigEndian]
|
[BigEndian]
|
||||||
public partial class SinexcelRecord
|
public partial class SinexcelRecord
|
||||||
{
|
{
|
||||||
/****************************** Input registers ****************************/
|
/*
|
||||||
|
// ========================================================
|
||||||
|
// Register declarations
|
||||||
|
// ========================================================
|
||||||
|
// to test by block first
|
||||||
|
// ---- Operating states (0x1000–0x1014)
|
||||||
|
[HoldingRegister<UInt16>(4096, writable: false)] private UInt16 _notUsedRemain; // 0x1000
|
||||||
|
[HoldingRegister<UInt16>(4098, writable: false)] private UInt16 _inverterOperatingState; // 0x1002 (3 bits)
|
||||||
|
[HoldingRegister<UInt16>(4100, writable: false)] private UInt16 _pv1OperatingState; // 0x1004 (1 bit)
|
||||||
|
[HoldingRegister<UInt16>(4101, writable: false)] private UInt16 _pv2OperatingState; // 0x1005 (1 bit)
|
||||||
|
[HoldingRegister<UInt16>(4103, writable: false)] private UInt16 _battery1OperatingState; // 0x1007 (3 bits)
|
||||||
|
|
||||||
|
// Battery 2 operating state bits (0x1009–0x100B)
|
||||||
|
[HoldingRegister<UInt16>(4105, writable: false)] private UInt16 _bat2StateBit0; // 0x1009
|
||||||
|
[HoldingRegister<UInt16>(4106, writable: false)] private UInt16 _bat2StateBit1; // 0x100A
|
||||||
|
[HoldingRegister<UInt16>(4107, writable: false)] private UInt16 _bat2StateBit2; // 0x100B
|
||||||
|
|
||||||
|
// ORED state bits (0x100C–0x100F)
|
||||||
|
[HoldingRegister<UInt16>(4108, writable: false)] private UInt16 _oredStateBit0; // 0x100C
|
||||||
|
[HoldingRegister<UInt16>(4109, writable: false)] private UInt16 _oredStateBit1; // 0x100D
|
||||||
|
[HoldingRegister<UInt16>(4110, writable: false)] private UInt16 _oredStateBit2; // 0x100E
|
||||||
|
[HoldingRegister<UInt16>(4111, writable: false)] private UInt16 _oredStateBit3; // 0x100F
|
||||||
|
|
||||||
|
// DCDC state (separate bits for DC/AC variants)
|
||||||
|
[HoldingRegister<UInt16>(4112, writable: false)] private UInt16 _dcdcStateDc; // 0x1010
|
||||||
|
[HoldingRegister<UInt16>(4113, writable: false)] private UInt16 _dcdcStateAc; // 0x1011
|
||||||
|
|
||||||
|
// PV3 / PV4 operational state
|
||||||
|
[HoldingRegister<UInt16>(4114, writable: false)] private UInt16 _pv3OperatingState; // 0x1012
|
||||||
|
[HoldingRegister<UInt16>(4115, writable: false)] private UInt16 _pv4OperatingState; // 0x1013
|
||||||
|
|
||||||
|
// Power grid status
|
||||||
|
[HoldingRegister<UInt16>(4116, writable: false)] private UInt16 _powerGridStatus; // 0x1014
|
||||||
|
|
||||||
|
// ---- Alarm / fault flags (0x1048–0x10CE) (0: Normal, 1: Abnormal)
|
||||||
|
[HoldingRegister<UInt16>(4168, writable: false)] private UInt16 _abnormalGridVoltage; // 0x1048 Abnormal grid voltage
|
||||||
|
[HoldingRegister<UInt16>(4169, writable: false)] private UInt16 _abnormalGridFrequency; // 0x1049 Abnormal grid frequency
|
||||||
|
[HoldingRegister<UInt16>(4170, writable: false)] private UInt16 _invertedSequenceOfGridVoltage; // 0x104A Inverted sequence of grid voltage
|
||||||
|
[HoldingRegister<UInt16>(4171, writable: false)] private UInt16 _gridVoltagePhaseLoss; // 0x104B Grid voltage phase loss
|
||||||
|
[HoldingRegister<UInt16>(4172, writable: false)] private UInt16 _abnormalOutputVoltage; // 0x104C Abnormal output voltage
|
||||||
|
[HoldingRegister<UInt16>(4173, writable: false)] private UInt16 _abnormalOutputFrequency; // 0x104D Abnormal output frequency
|
||||||
|
[HoldingRegister<UInt16>(4174, writable: false)] private UInt16 _abnormalNullLine; // 0x104E Abnormal null line
|
||||||
|
[HoldingRegister<UInt16>(4175, writable: false)] private UInt16 _excessivelyHighAmbientTemperature; // 0x104F Excessively high ambient temperature
|
||||||
|
[HoldingRegister<UInt16>(4176, writable: false)] private UInt16 _excessiveRadiatorTemperature; // 0x1050 Excessive radiator temperature
|
||||||
|
[HoldingRegister<UInt16>(4177, writable: false)] private UInt16 _insulationFault; // 0x1051 Insulation fault
|
||||||
|
[HoldingRegister<UInt16>(4178, writable: false)] private UInt16 _leakageProtectionFault; // 0x1052 Leakage protection fault
|
||||||
|
[HoldingRegister<UInt16>(4179, writable: false)] private UInt16 _auxiliaryPowerFault; // 0x1053 Auxiliary power fault
|
||||||
|
[HoldingRegister<UInt16>(4180, writable: false)] private UInt16 _fanFault; // 0x1054 Fan fault
|
||||||
|
[HoldingRegister<UInt16>(4181, writable: false)] private UInt16 _modelCapacityFault; // 0x1055 Model capacity fault
|
||||||
|
[HoldingRegister<UInt16>(4182, writable: false)] private UInt16 _abnormalLightningArrester; // 0x1056 Abnormal lightning arrester
|
||||||
|
[HoldingRegister<UInt16>(4183, writable: false)] private UInt16 _islandProtection; // 0x1057 Island protection
|
||||||
|
[HoldingRegister<UInt16>(4184, writable: false)] private UInt16 _battery1NotConnected; // 0x1058 Battery 1 not connected
|
||||||
|
[HoldingRegister<UInt16>(4185, writable: false)] private UInt16 _battery1Overvoltage; // 0x1059 Battery 1 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4186, writable: false)] private UInt16 _battery1Undervoltage; // 0x105A Battery 1 undervoltage
|
||||||
|
[HoldingRegister<UInt16>(4187, writable: false)] private UInt16 _battery1DischargeEnd; // 0x105B Battery 1 discharge end
|
||||||
|
[HoldingRegister<UInt16>(4188, writable: false)] private UInt16 _battery1Inverted; // 0x105C Battery 1 inverted
|
||||||
|
[HoldingRegister<UInt16>(4189, writable: false)] private UInt16 _battery2NotConnected; // 0x105D Battery 2 not connected
|
||||||
|
[HoldingRegister<UInt16>(4190, writable: false)] private UInt16 _battery2Overvoltage; // 0x105E Battery 2 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4191, writable: false)] private UInt16 _battery2Undervoltage; // 0x105F Battery 2 undervoltage
|
||||||
|
[HoldingRegister<UInt16>(4192, writable: false)] private UInt16 _battery2DischargeEnd; // 0x1060 Battery 2 discharge end
|
||||||
|
[HoldingRegister<UInt16>(4193, writable: false)] private UInt16 _battery2Inverted; // 0x1061 Battery 2 inverted
|
||||||
|
[HoldingRegister<UInt16>(4194, writable: false)] private UInt16 _pv1NotAccessed; // 0x1062 PV1 not accessed
|
||||||
|
[HoldingRegister<UInt16>(4195, writable: false)] private UInt16 _pv1Overvoltage; // 0x1063 PV1 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4196, writable: false)] private UInt16 _abnormalPv1CurrentSharing; // 0x1064 Abnormal PV1 current sharing
|
||||||
|
[HoldingRegister<UInt16>(4197, writable: false)] private UInt16 _pv2NotAccessed; // 0x1065 PV2 not accessed
|
||||||
|
[HoldingRegister<UInt16>(4198, writable: false)] private UInt16 _pv2Overvoltage; // 0x1066 PV2 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4199, writable: false)] private UInt16 _abnormalPv2CurrentSharing; // 0x1067 Abnormal PV2 current sharing
|
||||||
|
[HoldingRegister<UInt16>(4200, writable: false)] private UInt16 _dcBusOvervoltage; // 0x1068 DC bus overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4201, writable: false)] private UInt16 _dcBusUndervoltage; // 0x1069 DC bus undervoltage
|
||||||
|
[HoldingRegister<UInt16>(4202, writable: false)] private UInt16 _dcBusVoltageUnbalance; // 0x106A DC bus voltage unbalance
|
||||||
|
[HoldingRegister<UInt16>(4203, writable: false)] private UInt16 _pv1PowerTubeFault; // 0x106B PV1 power tube fault
|
||||||
|
[HoldingRegister<UInt16>(4204, writable: false)] private UInt16 _pv2PowerTubeFault; // 0x106C PV2 power tube fault
|
||||||
|
[HoldingRegister<UInt16>(4205, writable: false)] private UInt16 _battery1PowerTubeFault; // 0x106D Battery 1 power tube fault
|
||||||
|
[HoldingRegister<UInt16>(4206, writable: false)] private UInt16 _battery2PowerTubeFault; // 0x106E Battery 2 power tube fault
|
||||||
|
[HoldingRegister<UInt16>(4207, writable: false)] private UInt16 _inverterPowerTubeFault; // 0x106F Inverter power tube fault
|
||||||
|
[HoldingRegister<UInt16>(4208, writable: false)] private UInt16 _systemOutputOverload; // 0x1070 System output overload
|
||||||
|
[HoldingRegister<UInt16>(4209, writable: false)] private UInt16 _inverterOverload; // 0x1071 Inverter overload
|
||||||
|
[HoldingRegister<UInt16>(4210, writable: false)] private UInt16 _inverterOverloadTimeout; // 0x1072 Inverter overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4211, writable: false)] private UInt16 _battery1OverloadTimeout; // 0x1073 Battery 1 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4212, writable: false)] private UInt16 _battery2OverloadTimeout; // 0x1074 Battery 2 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4213, writable: false)] private UInt16 _inverterSoftStartFailure; // 0x1075 Inverter soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4214, writable: false)] private UInt16 _battery1SoftStartFailure; // 0x1076 Battery 1 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4215, writable: false)] private UInt16 _battery2SoftStartFailure; // 0x1077 Battery 2 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4216, writable: false)] private UInt16 _dsp1ParameterSettingFault; // 0x1078 DSP1 parameter setting fault
|
||||||
|
[HoldingRegister<UInt16>(4217, writable: false)] private UInt16 _dsp2ParameterSettingFault; // 0x1079 DSP2 parameter setting fault
|
||||||
|
[HoldingRegister<UInt16>(4218, writable: false)] private UInt16 _dspVersionCompatibilityFault; // 0x107A DSP version compatibility fault
|
||||||
|
[HoldingRegister<UInt16>(4219, writable: false)] private UInt16 _cpldVersionCompatibilityFault; // 0x107B CPLD version compatibility fault
|
||||||
|
[HoldingRegister<UInt16>(4220, writable: false)] private UInt16 _cpldCommunicationFault; // 0x107C CPLD communication fault
|
||||||
|
[HoldingRegister<UInt16>(4221, writable: false)] private UInt16 _dspCommunicationFault; // 0x107D DSP communication fault
|
||||||
|
[HoldingRegister<UInt16>(4222, writable: false)] private UInt16 _outputVoltageDcOverlimit; // 0x107E Output voltage DC overlimit
|
||||||
|
[HoldingRegister<UInt16>(4223, writable: false)] private UInt16 _outputCurrentDcOverlimit; // 0x107F Output current DC overlimit
|
||||||
|
[HoldingRegister<UInt16>(4224, writable: false)] private UInt16 _relaySelfCheckFails; // 0x1080 Relay self-check fails
|
||||||
|
[HoldingRegister<UInt16>(4225, writable: false)] private UInt16 _abnormalInverter; // 0x1081 Abnormal inverter
|
||||||
|
[HoldingRegister<UInt16>(4226, writable: false)] private UInt16 _poorGrounding; // 0x1082 Poor grounding
|
||||||
|
[HoldingRegister<UInt16>(4227, writable: false)] private UInt16 _pv1SoftStartFailure; // 0x1083 PV1 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4228, writable: false)] private UInt16 _pv2SoftStartFailure2; // 0x1084 PV2 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4229, writable: false)] private UInt16 _balancedCircuitOverloadTimeout; // 0x1085 Balanced circuit overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4230, writable: false)] private UInt16 _pv1OverloadTimeout; // 0x1086 PV1 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4231, writable: false)] private UInt16 _pv2OverloadTimeout; // 0x1087 PV2 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4232, writable: false)] private UInt16 _pcbOvertemperature; // 0x1088 PCB overtemperature
|
||||||
|
[HoldingRegister<UInt16>(4233, writable: false)] private UInt16 _dcConverterOvertemperature; // 0x1089 DC converter overtemperature
|
||||||
|
[HoldingRegister<UInt16>(4234, writable: false)] private UInt16 _busSlowOvervoltage; // 0x108A Bus slow overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4235, writable: false)] private UInt16 _abnormalOffGridOutputVoltage; // 0x108B Abnormal off-grid output voltage
|
||||||
|
[HoldingRegister<UInt16>(4236, writable: false)] private UInt16 _hardwareBusOvervoltage; // 0x108C Hardware bus overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4237, writable: false)] private UInt16 _hardwareOvercurrent; // 0x108D Hardware overcurrent
|
||||||
|
[HoldingRegister<UInt16>(4238, writable: false)] private UInt16 _dcConverterOvervoltage; // 0x108E DC converter overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4239, writable: false)] private UInt16 _dcConverterHardwareOvervoltage; // 0x108F DC converter hardware overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4240, writable: false)] private UInt16 _dcConverterOvercurrent; // 0x1090 DC converter overcurrent
|
||||||
|
[HoldingRegister<UInt16>(4241, writable: false)] private UInt16 _dcConverterHardwareOvercurrent; // 0x1091 DC converter hardware overcurrent
|
||||||
|
[HoldingRegister<UInt16>(4242, writable: false)] private UInt16 _dcConverterResonatorOvercurrent; // 0x1092 DC converter resonator overcurrent
|
||||||
|
[HoldingRegister<UInt16>(4243, writable: false)] private UInt16 _pv1InsufficientPower; // 0x1093 PV1 insufficient power
|
||||||
|
[HoldingRegister<UInt16>(4244, writable: false)] private UInt16 _pv2InsufficientPower; // 0x1094 PV2 insufficient power
|
||||||
|
[HoldingRegister<UInt16>(4245, writable: false)] private UInt16 _battery1InsufficientPower; // 0x1095 Battery 1 insufficient power
|
||||||
|
[HoldingRegister<UInt16>(4246, writable: false)] private UInt16 _battery2InsufficientPower; // 0x1096 Battery 2 insufficient power
|
||||||
|
[HoldingRegister<UInt16>(4247, writable: false)] private UInt16 _lithiumBattery1ChargeForbidden; // 0x1097 Lithium battery 1 charge forbidden
|
||||||
|
[HoldingRegister<UInt16>(4248, writable: false)] private UInt16 _lithiumBattery1DischargeForbidden; // 0x1098 Lithium battery 1 discharge forbidden
|
||||||
|
[HoldingRegister<UInt16>(4249, writable: false)] private UInt16 _lithiumBattery2ChargeForbidden; // 0x1099 Lithium battery 2 charge forbidden
|
||||||
|
[HoldingRegister<UInt16>(4250, writable: false)] private UInt16 _lithiumBattery2DischargeForbidden; // 0x109A Lithium battery 2 discharge forbidden
|
||||||
|
[HoldingRegister<UInt16>(4251, writable: false)] private UInt16 _lithiumBattery1Full; // 0x109B Lithium battery 1 full
|
||||||
|
[HoldingRegister<UInt16>(4252, writable: false)] private UInt16 _lithiumBattery1DischargeEnd; // 0x109C Lithium battery 1 discharge end
|
||||||
|
[HoldingRegister<UInt16>(4253, writable: false)] private UInt16 _lithiumBattery2Full; // 0x109D LithiumBattery2 full
|
||||||
|
[HoldingRegister<UInt16>(4254, writable: false)] private UInt16 _lithiumBattery2DischargeEnd; // 0x109E Lithium battery 2 discharge end
|
||||||
|
[HoldingRegister<UInt16>(4255, writable: false)] private UInt16 _loadPowerOverload; // 0x109F Load power overload
|
||||||
|
[HoldingRegister<UInt16>(4256, writable: false)] private UInt16 _abnormalLeakageSelfCheck; // 0x10A0 Abnormal leakage self-check
|
||||||
|
[HoldingRegister<UInt16>(4257, writable: false)] private UInt16 _inverterOvertemperatureAlarm; // 0x10A1 Inverter overtemperature alarm
|
||||||
|
[HoldingRegister<UInt16>(4258, writable: false)] private UInt16 _inverterOvertemperature; // 0x10A2 Inverter overtemperature
|
||||||
|
[HoldingRegister<UInt16>(4259, writable: false)] private UInt16 _dcConverterOvertemperatureAlarm; // 0x10A3 DC converter overtemperature alarm
|
||||||
|
[HoldingRegister<UInt16>(4260, writable: false)] private UInt16 _parallelCommunicationAlarm; // 0x10A4 Parallel communication alarm
|
||||||
|
[HoldingRegister<UInt16>(4261, writable: false)] private UInt16 _systemDerating; // 0x10A5 System derating
|
||||||
|
[HoldingRegister<UInt16>(4262, writable: false)] private UInt16 _inverterRelayOpen; // 0x10A6 Inverter relay open
|
||||||
|
[HoldingRegister<UInt16>(4263, writable: false)] private UInt16 _inverterRelayShortCircuit; // 0x10A7 Inverter relay short circuit
|
||||||
|
[HoldingRegister<UInt16>(4264, writable: false)] private UInt16 _pvAccessMethodErrorAlarm; // 0x10A8 PV access method error alarm
|
||||||
|
[HoldingRegister<UInt16>(4265, writable: false)] private UInt16 _parallelModuleMissing; // 0x10A9 Parallel module missing
|
||||||
|
[HoldingRegister<UInt16>(4266, writable: false)] private UInt16 _duplicateMachineNumbersForParallelModules; // 0x10AA Duplicate machine numbers for parallel modules
|
||||||
|
[HoldingRegister<UInt16>(4267, writable: false)] private UInt16 _parameterConflictInParallelModule; // 0x10AB Parameter conflict in parallel module
|
||||||
|
[HoldingRegister<UInt16>(4268, writable: false)] private UInt16 _reservedAlarms4; // 0x10AC Reserved alarms4
|
||||||
|
[HoldingRegister<UInt16>(4269, writable: false)] private UInt16 _reverseMeterConnection; // 0x10AD Reverse meter connection
|
||||||
|
[HoldingRegister<UInt16>(4270, writable: false)] private UInt16 _inverterSealPulse; // 0x10AE Inverter Seal Pulse
|
||||||
|
[HoldingRegister<UInt16>(4271, writable: false)] private UInt16 _pv3NotConnected; // 0x10AF PV3 not connected
|
||||||
|
[HoldingRegister<UInt16>(4272, writable: false)] private UInt16 _pv3Overvoltage; // 0x10B0 PV3 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4273, writable: false)] private UInt16 _pv3AverageCurrentAnomaly; // 0x10B1 PV3 average current anomaly
|
||||||
|
[HoldingRegister<UInt16>(4274, writable: false)] private UInt16 _pv4NotConnected; // 0x10B2 PV4 not connected
|
||||||
|
[HoldingRegister<UInt16>(4275, writable: false)] private UInt16 _pv4Overvoltage; // 0x10B3 PV4 overvoltage
|
||||||
|
[HoldingRegister<UInt16>(4276, writable: false)] private UInt16 _pv4AverageCurrentAnomaly; // 0x10B4 PV4 average current anomaly
|
||||||
|
[HoldingRegister<UInt16>(4277, writable: false)] private UInt16 _pv3PowerTubeFailure; // 0x10B5 PV3 power tube failure
|
||||||
|
[HoldingRegister<UInt16>(4278, writable: false)] private UInt16 _pv4PowerTubeFailure; // 0x10B6 PV4 power tube failure
|
||||||
|
[HoldingRegister<UInt16>(4279, writable: false)] private UInt16 _pv3SoftStartFailure; // 0x10B7 PV3 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4280, writable: false)] private UInt16 _pv4SoftStartFailure; // 0x10B8 PV4 soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4281, writable: false)] private UInt16 _pv3OverloadTimeout; // 0x10B9 PV3 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4282, writable: false)] private UInt16 _pv4OverloadTimeout; // 0x10BA PV4 overload timeout
|
||||||
|
[HoldingRegister<UInt16>(4283, writable: false)] private UInt16 _pv3ReverseConnection; // 0x10BB PV3 reverse connection
|
||||||
|
[HoldingRegister<UInt16>(4284, writable: false)] private UInt16 _pv4ReverseConnection; // 0x10BC PV4 reverse connection
|
||||||
|
[HoldingRegister<UInt16>(4285, writable: false)] private UInt16 _abnormalDieselGeneratorVoltage; // 0x10BD Abnormal diesel generator voltage
|
||||||
|
[HoldingRegister<UInt16>(4286, writable: false)] private UInt16 _abnormalDieselGeneratorFrequency; // 0x10BE Abnormal diesel generator frequency
|
||||||
|
[HoldingRegister<UInt16>(4287, writable: false)] private UInt16 _dieselGeneratorVoltageReverseSequence; // 0x10BF Diesel generator voltage reverse sequence
|
||||||
|
[HoldingRegister<UInt16>(4288, writable: false)] private UInt16 _dieselGeneratorVoltageOutOfPhase; // 0x10C0 Diesel generator voltage out of phase
|
||||||
|
[HoldingRegister<UInt16>(4289, writable: false)] private UInt16 _leadBatteryTemperatureAbnormality; // 0x10C1 Lead battery temperature abnormality
|
||||||
|
[HoldingRegister<UInt16>(4290, writable: false)] private UInt16 _batteryAccessMethodError; // 0x10C2 Battery access method error
|
||||||
|
[HoldingRegister<UInt16>(4291, writable: false)] private UInt16 _reservedAlarms5; // 0x10C3 Reserved alarms5
|
||||||
|
[HoldingRegister<UInt16>(4292, writable: false)] private UInt16 _battery1BackupProhibited; // 0x10C4 Battery 1 backup prohibited
|
||||||
|
[HoldingRegister<UInt16>(4293, writable: false)] private UInt16 _battery2BackupProhibited; // 0x10C5 Battery 2 backup prohibited
|
||||||
|
[HoldingRegister<UInt16>(4294, writable: false)] private UInt16 _abnormalGridCurrent; // 0x10C6 Abnormal grid current
|
||||||
|
[HoldingRegister<UInt16>(4295, writable: false)] private UInt16 _generatorOverload; // 0x10C7 Generator overload
|
||||||
|
[HoldingRegister<UInt16>(4296, writable: false)] private UInt16 _busSoftStartFailure; // 0x10C8 Bus soft start failure
|
||||||
|
[HoldingRegister<UInt16>(4297, writable: false)] private UInt16 _openCircuitOfPowerGridRelay; // 0x10C9 Open circuit of power grid relay
|
||||||
|
[HoldingRegister<UInt16>(4298, writable: false)] private UInt16 _shortCircuitOfPowerGridRelay; // 0x10CA Short circuit of power grid relay
|
||||||
|
[HoldingRegister<UInt16>(4299, writable: false)] private UInt16 _generatorRelayOpenCircuit; // 0x10CB Generator relay open circuit
|
||||||
|
[HoldingRegister<UInt16>(4300, writable: false)] private UInt16 _generatorRelayShortCircuit; // 0x10CC Generator relay short circuit
|
||||||
|
[HoldingRegister<UInt16>(4301, writable: false)] private UInt16 _insufficientPhotovoltaicPower; // 0x10CD Insufficient photovoltaic power
|
||||||
|
[HoldingRegister<UInt16>(4302, writable: false)] private UInt16 _photovoltaic1Overcurrent; // 0x10CE Photovoltaic 1 overcurrent*/
|
||||||
|
|
||||||
|
/****************************** Holding registers ****************************/
|
||||||
// Voltages
|
// Voltages
|
||||||
[HoldingRegister<UInt32>(4096)] public UInt32 _gridAPhaseVoltage; // 0x1000
|
[HoldingRegister<UInt32>(4096)] public UInt32 _gridAPhaseVoltage; // 0x1000
|
||||||
[HoldingRegister<UInt32>(4098)] private UInt32 _grid_B_Phase_Voltage; // 0x1002
|
[HoldingRegister<UInt32>(4098)] private UInt32 _grid_B_Phase_Voltage; // 0x1002
|
||||||
|
|
@ -206,6 +377,7 @@ public partial class SinexcelRecord
|
||||||
[HoldingRegister<UInt32>(4740)] private UInt32 _pv4Current; // 0x1284
|
[HoldingRegister<UInt32>(4740)] private UInt32 _pv4Current; // 0x1284
|
||||||
[HoldingRegister<UInt32>(4742)] private UInt32 _pv4Power; // 0x1286
|
[HoldingRegister<UInt32>(4742)] private UInt32 _pv4Power; // 0x1286
|
||||||
[HoldingRegister<UInt32>(4744)] private UInt32 _generatorTotalPower; // 0x128C
|
[HoldingRegister<UInt32>(4744)] private UInt32 _generatorTotalPower; // 0x128C
|
||||||
|
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Manufacturer Information & Software Versions
|
// Manufacturer Information & Software Versions
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
|
|
@ -226,15 +398,15 @@ public partial class SinexcelRecord
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// System configuration / operation registers
|
// System configuration / operation registers
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
//[HoldingRegister<UInt32>(12294, writable: true)] private UInt32 _threePhaseWireSystem; // 0x3006
|
[HoldingRegister<UInt32>(12294, writable: true)] private UInt32 _threePhaseWireSystem; // 0x3006
|
||||||
//[HoldingRegister<UInt32>(12296, writable: true)] private UInt32 _remainnotused; // 0x3008
|
//[HoldingRegister<UInt32>(12296, writable: true)] private UInt32 _remainnotused; // 0x3008
|
||||||
//[HoldingRegister<UInt32>(12298, writable: true)] private UInt32 _inputFrequencyClass; // 0x300A
|
[HoldingRegister<UInt32>(12298, writable: true)] private UInt32 _inputFrequencyClass; // 0x300A
|
||||||
[HoldingRegister<UInt32>(12300, writable: true)] private UInt32 _workingMode; // 0x300C
|
[HoldingRegister<UInt32>(12300, writable: true)] private UInt32 _workingMode; // 0x300C
|
||||||
//[HoldingRegister<UInt32>(12302, writable: true)] private UInt32 _methodSwitchMode; // 0x300E
|
[HoldingRegister<UInt32>(12302, writable: true)] private UInt32 _methodSwitchMode; // 0x300E
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Inverter Control and Protection Configuration
|
// Inverter Control and Protection Configuration
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
//[HoldingRegister<UInt32>(12304, writable: true)] private UInt32 _enableOnGridUnbalanceCompensation; // 0x3010
|
[HoldingRegister<UInt32>(12304, writable: true)] private UInt32 _enableOnGridUnbalanceCompensation; // 0x3010
|
||||||
//[HoldingRegister<UInt32>(12306, writable: true)] private UInt32 _temperatureDrop; // 0x3012
|
//[HoldingRegister<UInt32>(12306, writable: true)] private UInt32 _temperatureDrop; // 0x3012
|
||||||
//[HoldingRegister<UInt32>(12308, writable: true)] private UInt32 _hvrt; // 0x3014
|
//[HoldingRegister<UInt32>(12308, writable: true)] private UInt32 _hvrt; // 0x3014
|
||||||
//[HoldingRegister<UInt32>(12310, writable: true)] private UInt32 _lvrt; // 0x3016
|
//[HoldingRegister<UInt32>(12310, writable: true)] private UInt32 _lvrt; // 0x3016
|
||||||
|
|
@ -243,53 +415,60 @@ public partial class SinexcelRecord
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// Battery & PV Configuration
|
// Battery & PV Configuration
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
//[HoldingRegister<UInt32>(12336, writable: true)] private UInt32 _batteryAccessMethod; // 0x3030
|
[HoldingRegister<UInt32>(12336, writable: true)] private UInt32 _batteryAccessMethod; // 0x3030
|
||||||
//[HoldingRegister<UInt32>(12338, writable: true)] private UInt32 _meterAccessEnable; // 0x3032
|
[HoldingRegister<UInt32>(12338, writable: true)] private UInt32 _meterAccessEnable; // 0x3032
|
||||||
//[HoldingRegister<UInt32>(12340, writable: true)] private UInt32 _enableBattery1; // 0x3034
|
[HoldingRegister<UInt32>(12340, writable: true)] private UInt32 _enableBattery1; // 0x3034
|
||||||
//[HoldingRegister<UInt32>(12342, writable: true)] private UInt32 _enableBattery2; // 0x3036
|
[HoldingRegister<UInt32>(12342, writable: true)] private UInt32 _enableBattery2; // 0x3036
|
||||||
//[HoldingRegister<UInt32>(12344, writable: true)] private UInt32 _enablePv1; // 0x3038
|
[HoldingRegister<UInt32>(12344, writable: true)] private UInt32 _enablePv1; // 0x3038
|
||||||
//[HoldingRegister<UInt32>(12346, writable: true)] private UInt32 _enablePv2; // 0x303A
|
[HoldingRegister<UInt32>(12346, writable: true)] private UInt32 _enablePv2; // 0x303A
|
||||||
//[HoldingRegister<UInt32>(12348, writable: true)] private UInt32 _batteryType; // 0x303C
|
[HoldingRegister<UInt32>(12348, writable: true)] private UInt32 _batteryType; // 0x303C
|
||||||
//[HoldingRegister<UInt32>(12350, writable: true)] private UInt32 _batteryCapacity1; // 0x303E
|
[HoldingRegister<UInt32>(12350, writable: true)] private UInt32 _batteryCapacity1; // 0x303E
|
||||||
//[HoldingRegister<UInt32>(12352, writable: true)] private UInt32 _maxChargingCurrentBattery1; // 0x3040
|
[HoldingRegister<UInt32>(12352, writable: true)] private UInt32 _maxChargingCurrentBattery1; // 0x3040
|
||||||
//[HoldingRegister<UInt32>(12354, writable: true)] private UInt32 _maxDischargingCurrentBattery1; // 0x3042
|
[HoldingRegister<UInt32>(12354, writable: true)] private UInt32 _maxDischargingCurrentBattery1; // 0x3042
|
||||||
//[HoldingRegister<UInt32>(12362, writable: true)] private UInt32 _ratedBatteryVoltage1; // 0x304A
|
[HoldingRegister<UInt32>(12356, writable: true)] private UInt32 _ratedBatteryVoltage1; // 0x304A
|
||||||
//[HoldingRegister<UInt32>(12358, writable: true)] private UInt32 _minSocBattery1; // 0x3046
|
[HoldingRegister<UInt32>(12358, writable: true)] private UInt32 _minSocBattery1; // 0x3046
|
||||||
//[HoldingRegister<UInt32>(12360, writable: true)] private UInt32 _setValueBattery1; // 0x3048
|
[HoldingRegister<UInt32>(12360, writable: true)] private UInt32 _setValueBattery1; // 0x3048
|
||||||
//[HoldingRegister<UInt32>(12364, writable: true)] private UInt32 _activeChargeDischarge; // 0x304A
|
//[HoldingRegister<UInt32>(12362, writable: true)] private UInt32 _activeChargeDischarge; // 0x304A // this
|
||||||
//[HoldingRegister<UInt32>(12366, writable: true)] private UInt32 _activeChargeDischargePower; // 0x304C
|
[HoldingRegister<UInt32>(12364, writable: true)] private UInt32 _activeChargeDischargePower; // 0x304C
|
||||||
//[HoldingRegister<UInt32>(12380, writable: true)] private UInt32 _enableIslandProtection; // 0x305C
|
[HoldingRegister<UInt32>(12380, writable: true)] private UInt32 _enableIslandProtection; // 0x305C
|
||||||
//[HoldingRegister<UInt32>(12382, writable: true)] private UInt32 _pvAccessMode; // 0x305E
|
[HoldingRegister<UInt32>(12382, writable: true)] private UInt32 _pvAccessMode; // 0x305E
|
||||||
|
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// System & Battery-2 Configuration
|
// System & Battery-2 Configuration
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
//[HoldingRegister<UInt32>(12384, writable: true)] private UInt32 _outputVoltageAdjustmentFactor; // 0x3060
|
[HoldingRegister<UInt32>(12384, writable: true)] private UInt32 _outputVoltageAdjustmentFactor; // 0x3060
|
||||||
//[HoldingRegister<UInt32>(12386, writable: true)] private UInt32 _setValueBatteryUndervoltage1; // 0x3062
|
[HoldingRegister<UInt32>(12386, writable: true)] private UInt32 _setValueBatteryUndervoltage1; // 0x3062
|
||||||
//[HoldingRegister<UInt32>(12388, writable: true)] private UInt32 _inverterPowerLimit; // 0x3064
|
[HoldingRegister<UInt32>(12388, writable: true)] private UInt32 _inverterPowerLimit; // 0x3064
|
||||||
//[HoldingRegister<UInt32>(12400, writable: true)] private UInt32 _battery2Capacity; // 0x30B0
|
[HoldingRegister<UInt32>(12400, writable: true)] private UInt32 _battery2Capacity; // 0x30B0
|
||||||
//[HoldingRegister<UInt32>(12402, writable: true)] private UInt32 _maxChargingCurrentBattery2; // 0x30B2
|
[HoldingRegister<UInt32>(12402, writable: true)] private UInt32 _maxChargingCurrentBattery2; // 0x30B2
|
||||||
//[HoldingRegister<UInt32>(12404, writable: true)] private UInt32 _maxDischargingCurrentBattery2; // 0x30B4
|
[HoldingRegister<UInt32>(12404, writable: true)] private UInt32 _maxDischargingCurrentBattery2; // 0x30B4
|
||||||
//[HoldingRegister<UInt32>(12406, writable: true)] private UInt32 _battery2RatedVoltage; // 0x30B6
|
[HoldingRegister<UInt32>(12406, writable: true)] private UInt32 _battery2RatedVoltage; // 0x30B6
|
||||||
//[HoldingRegister<UInt32>(12408, writable: true)] private UInt32 _battery2MinSoc; // 0x30B8
|
[HoldingRegister<UInt32>(12408, writable: true)] private UInt32 _battery2MinSoc; // 0x30B8
|
||||||
//[HoldingRegister<UInt32>(12410, writable: true)] private UInt32 _battery2OverVoltageSetting; // 0x30BA
|
[HoldingRegister<UInt32>(12410, writable: true)] private UInt32 _battery2OverVoltageSetting; // 0x30BA
|
||||||
//[HoldingRegister<UInt32>(12412, writable: true)] private UInt32 _battery2UnderVoltageSetpoint; // 0x30BC
|
[HoldingRegister<UInt32>(12412, writable: true)] private UInt32 _battery2UnderVoltageSetpoint; // 0x30BC
|
||||||
//
|
//
|
||||||
//[HoldingRegister<UInt32>(12414, writable: true)] private UInt32 _singleOrParallelMachine; // 0x30BE
|
[HoldingRegister<UInt32>(12414, writable: true)] private UInt32 _singleOrParallelMachine; // 0x30BE
|
||||||
//[HoldingRegister<UInt32>(12416, writable: true)] private UInt32 _numberOfSystemModules; // 0x30C0
|
[HoldingRegister<UInt32>(12416, writable: true)] private UInt32 _numberOfSystemModules; // 0x30C0
|
||||||
//[HoldingRegister<UInt32>(12418, writable: true)] private UInt32 _parallelModuleMachineNumber; // 0x30C2
|
[HoldingRegister<UInt32>(12418, writable: true)] private UInt32 _parallelModuleMachineNumber; // 0x30C2
|
||||||
//[HoldingRegister<UInt32>(12420, writable: true)] private UInt32 _accreditedCountries; // 0x30C4
|
[HoldingRegister<UInt32>(12420, writable: true)] private UInt32 _accreditedCountries; // 0x30C4
|
||||||
|
|
||||||
|
[HoldingRegister<UInt32>(12618, writable: true)] private UInt32 _battery1BackupSOC; // 0x314A
|
||||||
|
[HoldingRegister<UInt32>(12620, writable: true)] private UInt32 _battery2BackupSOC; // 0x314C
|
||||||
|
|
||||||
|
[HoldingRegister<UInt32>(12746, writable: true)] private UInt32 _enableGridExport; // 0x314A
|
||||||
|
[HoldingRegister<UInt32>(12748, writable: true)] private UInt32 _powerGridExportLimit; // 0x314C
|
||||||
|
|
||||||
|
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
// System Control & Diagnostic Registers
|
// System Control & Diagnostic Registers
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
[HoldingRegister<UInt32>(15426, writable: true)] private UInt32 _powerOn; // 0x3C42
|
[HoldingRegister<UInt32>(15426, writable: true)] private UInt32 _powerOn; // 0x3C42
|
||||||
[HoldingRegister<UInt32>(15428, writable: true)] private UInt32 _powerOff; // 0x3C44
|
[HoldingRegister<UInt32>(15428, writable: true)] private UInt32 _powerOff; // 0x3C44
|
||||||
//[HoldingRegister<UInt32>(15430, writable: true)] private UInt32 _faultClearing; // 0x3C46
|
[HoldingRegister<UInt32>(15430, writable: true)] private UInt32 _faultClearing; // 0x3C46
|
||||||
//[HoldingRegister<UInt32>(15518, writable: true)] private UInt32 _meterReverseManualDetection; // 0x3C9E
|
[HoldingRegister<UInt32>(15518, writable: true)] private UInt32 _meterReverseManualDetection; // 0x3C9E
|
||||||
//[HoldingRegister<UInt32>(15520, writable: true)] private UInt32 _batteryVoltageRating; // 0x3CA0
|
//[HoldingRegister<UInt32>(15520, writable: true)] private UInt32 _batteryVoltageRating; // 0x3CA0 This causing the inverter to switch off, please consider testing it first before uncommenting
|
||||||
//[HoldingRegister<UInt32>(15524, writable: true)] private UInt32 _battery1Activation; // 0x3CA4
|
[HoldingRegister<UInt32>(15522, writable: true)] private UInt32 _battery1Activation; // 0x3CA2
|
||||||
//[HoldingRegister<UInt32>(15526, writable: true)] private UInt32 _battery2Activation; // 0x3CA6
|
[HoldingRegister<UInt32>(15524, writable: true)] private UInt32 _battery2Activation; // 0x3CA4
|
||||||
|
|
||||||
|
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
|
|
@ -337,8 +516,6 @@ public partial class SinexcelRecord
|
||||||
// Time-based scheduling (period 1) configuration
|
// Time-based scheduling (period 1) configuration
|
||||||
// ───────────────────────────────────────────────
|
// ───────────────────────────────────────────────
|
||||||
|
|
||||||
//[HoldingRegister<UInt32>(49328, writable: true)] private UInt32 _Timed_Charge_and_Discharge_Off; // 0xC0B4, bit flags Sun–Sat
|
|
||||||
//[HoldingRegister<UInt32>(49330, writable: true)] private UInt32 _Other_time_period_mode; // 0xC0B4, bit flags Sun–Sat
|
|
||||||
[HoldingRegister<UInt32>(49332, writable: true)] private UInt32 _repetitiveWeeks; // 0xC0B4, bit flags Sun–Sat
|
[HoldingRegister<UInt32>(49332, writable: true)] private UInt32 _repetitiveWeeks; // 0xC0B4, bit flags Sun–Sat
|
||||||
[HoldingRegister<UInt32>(49334, writable: true)] private UInt32 _effectiveStartDate; // 0xC0B6, UNIX timestamp (UTC+offset)
|
[HoldingRegister<UInt32>(49334, writable: true)] private UInt32 _effectiveStartDate; // 0xC0B6, UNIX timestamp (UTC+offset)
|
||||||
[HoldingRegister<UInt32>(49336, writable: true)] private UInt32 _effectiveEndDate; // 0xC0B8, UNIX timestamp (UTC+offset)
|
[HoldingRegister<UInt32>(49336, writable: true)] private UInt32 _effectiveEndDate; // 0xC0B8, UNIX timestamp (UTC+offset)
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,8 @@ export interface JSONRecordData {
|
||||||
Battery2Soh:number;
|
Battery2Soh:number;
|
||||||
PvPower:number;
|
PvPower:number;
|
||||||
ConsumptionPower:number;
|
ConsumptionPower:number;
|
||||||
|
WorkingMode?:string;
|
||||||
|
OperatingMode?:string;
|
||||||
};
|
};
|
||||||
|
|
||||||
AcDcGrowatt: {
|
AcDcGrowatt: {
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,9 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="location" defaultMessage="Location" />
|
<FormattedMessage id="location" defaultMessage="Location" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<FormattedMessage id="installationSN" defaultMessage="Installation SN" />
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<FormattedMessage id="region" defaultMessage="Region" />
|
<FormattedMessage id="region" defaultMessage="Region" />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
@ -156,6 +159,19 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
fontWeight="bold"
|
||||||
|
color="text.primary"
|
||||||
|
gutterBottom
|
||||||
|
noWrap
|
||||||
|
sx={{ marginTop: '10px', fontSize: 'small' }}
|
||||||
|
>
|
||||||
|
{installation.serialNumber}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,11 @@ function SodioHomeInstallation(props: singleInstallationProps) {
|
||||||
fontSize: '14px'
|
fontSize: '14px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{values.Config.OperatingPriority}
|
{props.current_installation.device === 4
|
||||||
|
? values.InverterRecord?.WorkingMode
|
||||||
|
: props.current_installation.device === 3
|
||||||
|
? values.InverterRecord?.OperatingMode
|
||||||
|
: values.Config.OperatingPriority}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue