Update Status to inherit from Status Api

This commit is contained in:
atef 2023-02-23 13:45:09 +01:00
parent 5affecda55
commit 175f11fccf
41 changed files with 917 additions and 715 deletions

View File

@ -26,7 +26,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VenusLogger", "app/VenusLog
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meiringen", "app/Meiringen/Meiringen.csproj", "{4C816420-FD19-47BF-87FE-599210CA8384}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeterDriver", "app\EmuMeterDriver\EmuMeterDriver.csproj", "{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeterDriver", "app/EmuMeterDriver/EmuMeterDriver.csproj", "{F65F33B0-3522-4008-8D1E-47EF8E4C7AC7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BmsTunnel", "app/BmsTunnel/BmsTunnel.csproj", "{40B45363-BE34-420B-8F87-775EE6EE3513}"
EndProject
@ -34,21 +34,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{145597B4-3E3
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3", "lib\S3\S3.csproj", "{C3639841-13F4-4F24-99C6-7D965593BF89}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S3", "lib/S3/S3.csproj", "{C3639841-13F4-4F24-99C6-7D965593BF89}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", "{D846B46B-46FF-4EF7-9B9D-DDBEF9533C56}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deprecated", "deprecated", "{46DE03C4-52D1-47AA-8E60-8BB15361D723}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsController", "app\CsController\CsController.csproj", "{72DBBE42-A09F-43C0-9613-331039857056}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsController", "app/CsController/CsController.csproj", "{72DBBE42-A09F-43C0-9613-331039857056}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBatteryDbus", "app\TestBatteryDbus\TestBatteryDbus.csproj", "{CB226D69-DD28-4AAF-8BBE-2488E37A0B8D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaliMax", "app/SaliMax/SaliMax.csproj", "{25073794-D859-4824-9984-194C7E928496}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaliMax", "app\SaliMax\SaliMax.csproj", "{25073794-D859-4824-9984-194C7E928496}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuiFeeder", "app/GuiFeeder/GuiFeeder.csproj", "{5B953EC4-51F3-4A0A-ADF5-BAA3D1570CB2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuiFeeder", "app\GuiFeeder\GuiFeeder.csproj", "{5B953EC4-51F3-4A0A-ADF5-BAA3D1570CB2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusApi", "lib\StatusApi\StatusApi.csproj", "{9D17E78C-8A70-43DB-A619-DC12D20D023D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatusApi", "lib/StatusApi/StatusApi.csproj", "{9D17E78C-8A70-43DB-A619-DC12D20D023D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Devices", "Devices", "{4931A385-24DC-4E78-BFF4-356F8D6D5183}"
EndProject
@ -58,38 +56,36 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Victron", "Victron", "{BD8C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Trumpf", "Trumpf", "{DDDBEFD0-5DEA-4C7C-A9F2-FDB4636CF092}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvert", "lib\Devices\Trumpf\TruConvert\TruConvert.csproj", "{EF46CF7B-823E-4CB7-966F-EDDC144C7954}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvert", "lib/Devices/Trumpf/TruConvert/TruConvert.csproj", "{EF46CF7B-823E-4CB7-966F-EDDC144C7954}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvertAc", "lib\Devices\Trumpf\TruConvertAc\TruConvertAc.csproj", "{1F4B445E-459E-44CD-813E-6D725EBB81E8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvertAc", "lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj", "{1F4B445E-459E-44CD-813E-6D725EBB81E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvertDc", "lib\Devices\Trumpf\TruConvertDc\TruConvertDc.csproj", "{F6F29829-C31A-4994-A698-E441BEA631C6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TruConvertDc", "lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj", "{F6F29829-C31A-4994-A698-E441BEA631C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBus", "lib\Protocols\DBus\DBus.csproj", "{8C3C620A-087D-4DD6-B493-A47FC643F8DC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBus", "lib/Protocols/DBus/DBus.csproj", "{8C3C620A-087D-4DD6-B493-A47FC643F8DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus", "lib\Protocols\Modbus\Modbus.csproj", "{E4AE6A33-0DEB-48EB-9D57-C0C7C63FC267}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Modbus", "lib/Protocols/Modbus/Modbus.csproj", "{E4AE6A33-0DEB-48EB-9D57-C0C7C63FC267}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VeDBus", "lib\Victron\VeDBus\VeDBus.csproj", "{50B26E29-1B99-4D07-BCA5-359CD550BBAA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VeDBus", "lib/Victron/VeDBus/VeDBus.csproj", "{50B26E29-1B99-4D07-BCA5-359CD550BBAA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VictronVRM", "lib\Victron\VictronVRM\VictronVRM.csproj", "{FE05DF69-B5C7-4C2E-8FB9-7776441A7622}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VictronVRM", "lib/Victron/VictronVRM/VictronVRM.csproj", "{FE05DF69-B5C7-4C2E-8FB9-7776441A7622}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ampt", "lib\Devices\AMPT\Ampt.csproj", "{77AF3A64-2878-4150-BCD0-F16530783165}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ampt", "lib/Devices/AMPT/Ampt.csproj", "{77AF3A64-2878-4150-BCD0-F16530783165}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Battery48TL", "lib\Devices\Battery48TL\Battery48TL.csproj", "{1C3F443A-B339-4B08-80E6-8A84817FFEC9}"
ProjectSection(ProjectDependencies) = postProject
{89A3E29C-4E57-47FE-A800-12AC68418264} = {89A3E29C-4E57-47FE-A800-12AC68418264}
EndProjectSection
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Battery48TL", "lib/Devices/Battery48TL/Battery48TL.csproj", "{1C3F443A-B339-4B08-80E6-8A84817FFEC9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeter", "lib\Devices\EmuMeter\EmuMeter.csproj", "{152A4168-F612-493C-BBEA-8EB26E6E2D34}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmuMeter", "lib/Devices/EmuMeter/EmuMeter.csproj", "{152A4168-F612-493C-BBEA-8EB26E6E2D34}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "lib\Utils\Utils.csproj", "{89A3E29C-4E57-47FE-A800-12AC68418264}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "lib/Utils/Utils.csproj", "{89A3E29C-4E57-47FE-A800-12AC68418264}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6060", "lib\Devices\Adam6060\Adam6060.csproj", "{4AFDB799-E6A4-4DCA-8B6D-8C0F98398461}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adam6060", "lib/Devices/Adam6060/Adam6060.csproj", "{4AFDB799-E6A4-4DCA-8B6D-8C0F98398461}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Channels", "lib\Channels\Channels.csproj", "{AF7E8DCA-8D48-498E-AB3D-208061B244DC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Channels", "lib/Channels/Channels.csproj", "{AF7E8DCA-8D48-498E-AB3D-208061B244DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend", "app\Backend\Backend.csproj", "{A56F58C2-B265-435B-A985-53B4D6F49B1A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend", "app/Backend/Backend.csproj", "{A56F58C2-B265-435B-A985-53B4D6F49B1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FossilTui", "app/FossilTui/FossilTui.csproj", "{C40264BB-C834-4C48-9B3F-6BEF8F37C0ED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FossilTui", "app\FossilTui\FossilTui.csproj", "{C40264BB-C834-4C48-9B3F-6BEF8F37C0ED}"
EndProject
Global

View File

@ -6,19 +6,15 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\DBus\DBus.csproj" />
<ProjectReference Include="..\..\lib\Devices\EmuMeter\EmuMeter.csproj" />
<ProjectReference Include="..\..\lib\EmuMeter\EmuMeter.csproj" />
<ProjectReference Include="..\..\lib\Modbus\Modbus.csproj" />
<ProjectReference Include="..\..\lib\Protocols\DBus\DBus.csproj" />
<ProjectReference Include="..\..\lib\Protocols\Modbus\Modbus.csproj" />
<ProjectReference Include="..\..\lib\Utils\Utils.csproj" />
<ProjectReference Include="..\..\lib\VeDBus\VeDBus.csproj" />
<ProjectReference Include="..\..\lib\Victron\VeDBus\VeDBus.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.5.0" />
<PackageReference Include="CliWrap" Version="3.6.0" />
</ItemGroup>
</Project>

View File

@ -15,12 +15,16 @@
<ProjectReference Include="../../lib/Devices/Trumpf/TruConvert/TruConvert.csproj" />
<ProjectReference Include="../../lib/StatusApi/StatusApi.csproj" />
<ProjectReference Include="../../lib/Utils/Utils.csproj" />
<PackageReference Include="CliWrap" Version="3.5.0" />
<PackageReference Include="CliWrap" Version="3.6.0" />
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2" />
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
<ProjectReference Include="..\..\lib\Time\Time.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="src\StatusLog" />
</ItemGroup>
</Project>

View File

@ -3,7 +3,6 @@
dotnet_version='net6.0'
set -e
echo -e "\n============================ Build ============================\n"
@ -11,21 +10,28 @@ echo -e "\n============================ Build ============================\n"
dotnet publish \
./SaliMax.csproj \
-c Release \
-r linux-arm
-r linux-x64
echo -e "\n============================ Deploy ============================\n"
rsync -v \
./bin/Release/$dotnet_version/linux-arm/publish/* \
debian@10.2.1.87:~/salimax
./bin/Release/$dotnet_version/linux-x64/publish/* \
ie-entwicklung@10.2.3.49:~/salimax
echo -e "\n============================ Run ============================\n"
# debian@10.2.1.87:~/salimax
echo -e "\n============================ Restart Salimax sevice ============================\n"
ssh -tt \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=2 \
debian@10.2.1.87 \
'~/salimax/SaliMax' \
2>/dev/null
ie-entwicklung@10.2.3.49 \
sudo systemctl restart salimax.service
echo -e "\n============================ Print service output ============================\n"
ssh -tt \
ie-entwicklung@10.2.3.49 \
journalctl -f -u salimax.service

View File

@ -13,14 +13,14 @@ public static class Control
public static Decimal ControlInverterPower(this StatusRecord status, Decimal targetInverterPower)
{
var s = status.InverterStatus!;
var totalInverterAcPower = s.PowerAcL1 + s.PowerAcL2 + s.PowerAcL3;
var totalInverterAcPower = s.Ac.ActivePower;
return ControlPower(totalInverterAcPower, targetInverterPower,status.SalimaxConfig!.PConstant);
}
public static Decimal ControlBatteryPower(this StatusRecord status, Decimal targetBatteryPower)
public static Decimal ControlBatteryPower(this StatusRecord status, Decimal targetBatteryPower, UInt16 i = 0) //this will use the avg batteries
{
return ControlPower(status.BatteryStatus!.Power, targetBatteryPower, status.SalimaxConfig!.PConstant);
return ControlPower(status.BatteriesStatus![i].Dc.Power, targetBatteryPower, status.SalimaxConfig!.PConstant);
}
public static Decimal ControlLowBatterySoc(this StatusRecord status)
@ -31,14 +31,14 @@ public static class Control
public static Decimal LowerLimit(params Decimal[] deltas) => deltas.Max();
public static Decimal UpperLimit(params Decimal[] deltas) => deltas.Min();
private static Decimal HoldMinSocCurve(StatusRecord s)
private static Decimal HoldMinSocCurve(StatusRecord s, UInt16 i = 0)
{
// TODO: explain LowSOC curve
var a = -2 * s.SalimaxConfig!.SelfDischargePower / s.SalimaxConfig.HoldSocZone;
var b = -a * (s.SalimaxConfig.MinSoc + s.SalimaxConfig.HoldSocZone);
return s.BatteryStatus!.Soc * a + b;
return s.BatteriesStatus![i].Soc * a + b; //this will use the avg batteries
}
private static Decimal ControlPower(Decimal measurement, Decimal target, Decimal p)

View File

@ -18,7 +18,7 @@ public static class Controller
private static readonly TimeSpan CommunicationTimeout = TimeSpan.FromSeconds(10);
private static readonly Int16 MaxmimumAllowedBatteryTemp = 315;
public static readonly Int16 MaxmimumAllowedBatteryTemp = 315;
private static UInt16 _numberOfInverters;
@ -71,7 +71,7 @@ public static class Controller
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 9,
{
SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 10,
{
@ -79,11 +79,11 @@ public static class Controller
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 11,
{
SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 12,
{
SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Open,
SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Open, SaliMaxRelayStatus.K3: Closed,
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 13,
{
@ -91,7 +91,7 @@ public static class Controller
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 14,
{
SaliMaxRelayStatus.K1: Closed, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Open,
SaliMaxRelayStatus.K1: Open, SaliMaxRelayStatus.K2: Closed, SaliMaxRelayStatus.K3: Closed,
InverterStatus.AcDcActiveGridType: AcDcGridType.GridTied400V50Hz
} => 15,
{
@ -140,7 +140,10 @@ public static class Controller
public static ControlRecord SaliMaxControl(StatusRecord statusRecord)
{
var resetInverterAlarm = CheckInverterAlarms(statusRecord);
var currentSaliMaxState = GetSaliMaxState(statusRecord);
UInt16 acSlaveId = 1;
var resetInverterAlarm = CheckInverterAlarms(statusRecord, currentSaliMaxState).WriteLine(" reset Alarm");
var resetDcAlarm = CheckDcDcAlarms(statusRecord);
var lastEocTime = GetLastEocTime(statusRecord);
@ -152,32 +155,48 @@ public static class Controller
var noGridMeter = statusRecord.GridMeterStatus == null;
var saliMaxConfig = statusRecord.SalimaxConfig with { LastEoc = lastEocTime };
var currentSaliMaxState = GetSaliMaxState(statusRecord);
ExplainState(currentSaliMaxState);
const RelayState k2Relay = Closed;
var acPowerStageEnable = StateConfig.AcPowerStageEnableStates.Contains(currentSaliMaxState) || currentSaliMaxState == 8;
var acPowerStageEnable = StateConfig.AcPowerStageEnableStates.Contains(currentSaliMaxState); //this is logical incorrect, find better way
var dcPowerStageEnable = statusRecord.BatteryStatus is not null; // this is to check
var dcPowerStageEnable = statusRecord.BatteriesStatus is not null; // TODO this is to check, Can be the batteries Status be null?
var salimaxRelay = statusRecord.SaliMaxRelayStatus! with { K2 = k2Relay }; // to check
var salimaxRelay = statusRecord.SaliMaxRelayStatus! with { K2 = k2Relay }; // to check // this is must be control
if (statusRecord.BatteryStatus == null)
if (resetInverterAlarm)
{
acPowerStageEnable = !resetInverterAlarm ;
acSlaveId = 0;
}
if (resetDcAlarm)
{
dcPowerStageEnable = !resetDcAlarm ;
}
acSlaveId.WriteLine(" AcSlave @");
if (statusRecord.BatteriesStatus == null)
{
Console.WriteLine(" No batteries");
return new ControlRecord
{
AcControlRecord = Defaults.TruConvertAcControl with
{
SignedPowerNominalValue = 0,
PowerStageEnable = acPowerStageEnable,
CommunicationTimeout = CommunicationTimeout,
SlaveAddress = acSlaveId,
ResetsAlarmAndWarning = resetInverterAlarm
},
DcControlRecord = Defaults.TruConvertDcControl with
{
PowerStageEnable = dcPowerStageEnable,
ResetsAlarmAndWarning = resetDcAlarm
PowerStageEnable = dcPowerStageEnable,
ResetsAlarmAndWarning = resetDcAlarm,
TimeoutForCommunication = CommunicationTimeout
},
SalimaxConfig = saliMaxConfig, // must create a control of each
SalimaxRelays = salimaxRelay, // must create a control of each
@ -201,6 +220,38 @@ public static class Controller
throw new NotImplementedException();
}
var newPowerSetPoint = CalculateNewPowerSetPoint(statusRecord);
////////////////////////// Control Record //////////////////////////
var acControlRecord = Defaults.TruConvertAcControl with
{
PowerStageEnable = acPowerStageEnable,
CommunicationTimeout = CommunicationTimeout,
SignedPowerNominalValue = newPowerSetPoint,
SlaveAddress = acSlaveId,
ResetsAlarmAndWarning = resetInverterAlarm
};
var dcControlRecord = Defaults.TruConvertDcControl with
{
PowerStageEnable = dcPowerStageEnable,
ResetsAlarmAndWarning = resetDcAlarm,
TimeoutForCommunication = CommunicationTimeout
};
return new ControlRecord
{
AcControlRecord = acControlRecord,
DcControlRecord = dcControlRecord,
SalimaxConfig = saliMaxConfig,
SalimaxRelays = salimaxRelay
};
}
private static Decimal CalculateNewPowerSetPoint(StatusRecord statusRecord)
{
var currentPowerSetPoint = statusRecord.InverterStatus!.AcSignedPowerValue;
var limitReason = "no limit";
@ -212,7 +263,7 @@ public static class Controller
goal = "Calibration Charge";
delta = statusRecord.ControlInverterPower(statusRecord.SalimaxConfig.MaxInverterPower);
}
else if (statusRecord.BatteryStatus!.Soc < statusRecord.SalimaxConfig.MinSoc)
else if (statusRecord.AvgBatteriesStatus!.Soc < statusRecord.SalimaxConfig.MinSoc) // TODO
{
goal = $"reach min SOC (Min soc: {statusRecord.SalimaxConfig.MinSoc})";
delta = statusRecord.ControlInverterPower(statusRecord.SalimaxConfig
@ -234,7 +285,7 @@ public static class Controller
delta = inverterAc2DcLimitPower;
}
var batteryChargingLimitPower = statusRecord.ControlBatteryPower(statusRecord.BatteryStatus!.MaxChargingPower);
var batteryChargingLimitPower = statusRecord.ControlBatteryPower(statusRecord.BatteriesStatus[0]!.MaxChargingPower);
if (delta > batteryChargingLimitPower)
{
@ -253,12 +304,12 @@ public static class Controller
}
var batteryDischargingLimitPower =
statusRecord.ControlBatteryPower(statusRecord.BatteryStatus!.MaxDischargingPower);
statusRecord.ControlBatteryPower(statusRecord.BatteriesStatus[0]!.MaxDischargingPower); // TODO change to avg battery
if (delta < batteryDischargingLimitPower)
{
limitReason =
$"limited by max battery discharging power: {statusRecord.BatteryStatus!.MaxDischargingPower}";
$"limited by max battery discharging power: {statusRecord.BatteriesStatus[0]!.MaxDischargingPower}";// TODO change to avg battery
delta = batteryDischargingLimitPower;
}
@ -271,13 +322,13 @@ public static class Controller
delta = keepMinSocLimitDelta;
}
/* if (statusRecord.BatteryStatus.BatteryTemperature >= 300) must not reduce the delta
{
var softLandingFactor = (MaxmimumAllowedBatteryTemp - statusRecord.BatteryStatus.BatteryTemperature) / 15; //starting softlanding from 300 degree
limitReason =
$"limiting discharging power in order to stay keep the battery temp below 315°: {statusRecord.BatteryStatus.BatteryTemperature}°" + " Softlanding factor: " + softLandingFactor;
delta *= softLandingFactor;
}*/
// if (statusRecord.BatteriesStatus[0]!.Temperature >= 300) //must not reduce the delta
// {
// var softLandingFactor = (MaxmimumAllowedBatteryTemp - statusRecord.BatteriesStatus[0]!.Temperature) / 15; //starting softlanding from 300 degree
// limitReason =
// $"limiting discharging power in order to stay keep the battery temp below 315°: {statusRecord.BatteriesStatus[0]!.Temperature}°" + " Softlanding factor: " + softLandingFactor;
// delta *= softLandingFactor;
// }
var newPowerSetPoint =
DistributePower(currentPowerSetPoint + delta, statusRecord.SalimaxConfig.MaxInverterPower);
@ -289,32 +340,7 @@ public static class Controller
limitReason.WriteLine(" Limit reason");
delta.WriteLine(" Delta");
// "============".WriteLine();
////////////////////////// Control Record //////////////////////////
var acControlRecord = Defaults.TruConvertAcControl with
{
PowerStageEnable = acPowerStageEnable,
CommunicationTimeout = CommunicationTimeout,
SignedPowerNominalValue = newPowerSetPoint,
ResetsAlarmAndWarning = resetInverterAlarm
};
var dcControlRecord = Defaults.TruConvertDcControl with
{
PowerStageEnable = dcPowerStageEnable,
ResetsAlarmAndWarning = resetDcAlarm,
TimeoutForCommunication = CommunicationTimeout
};
return new ControlRecord
{
AcControlRecord = acControlRecord,
DcControlRecord = dcControlRecord,
SalimaxConfig = saliMaxConfig,
SalimaxRelays = salimaxRelay
};
return newPowerSetPoint;
}
private static State TargetState(State currentState)
@ -423,7 +449,7 @@ public static class Controller
private static UnixTime GetLastEocTime(StatusRecord statusRecord)
{
if (statusRecord.BatteryStatus?.Soc >= 100)
if (statusRecord.BatteriesStatus[0]!.Soc >= 100 && statusRecord.BatteriesStatus[1]!.Soc >= 100 )
{
Console.WriteLine("battery has reached EOC");
File.AppendAllTextAsync(Config.LogSalimaxLog, String.Join(Environment.NewLine, UnixTime.Now + "battery has reached EOC"));
@ -442,6 +468,7 @@ public static class Controller
private static Boolean CheckDcDcAlarms(StatusRecord s)
{
s.DcDcStatus?.Alarms.Count.WriteLine(" Dc Alarm count");
if ( s.DcDcStatus?.Alarms.Count > 0 &&
s.DcDcStatus?.PowerOperation == false)
{
@ -452,14 +479,15 @@ public static class Controller
return false;
}
private static Boolean CheckInverterAlarms(StatusRecord s)
private static Boolean CheckInverterAlarms(StatusRecord s, UInt16 state)
{
if ( s.InverterStatus?.Alarms.Count > 0 &&
s.InverterStatus?.PowerOperation == false)
s.InverterStatus?.Alarms.Count.WriteLine(" Ac Alarm count");
if ( s.InverterStatus?.Alarms.Count > 0 )
{
File.AppendAllTextAsync(Config.LogSalimaxLog, UnixTime.Now + " " + s.InverterStatus.Alarms);
File.AppendAllTextAsync(Config.LogSalimaxLog, UnixTime.Now + " " + s.InverterStatus.Alarms[0]); // Todo write every alarm in he alarm list
return true;
}
return false;
}

View File

@ -5,14 +5,26 @@ public enum State : Int16
{
State1 = 1,
State2 = 2,
State3 = 3,
State4 = 4,
State5 = 5,
State6 = 6,
State7 = 7,
State8 = 8,
State9 = 9,
State10 = 10,
State11 = 11,
State12 = 12,
State13 = 13,
State14 = 14,
State15 = 15,
State16 = 16,
State17 = 17,
State18 = 18,
State19 = 19,
State20 = 20,
State21 = 21,
State22 = 22,
State23 = 23,
State24 = 24
}

View File

@ -3,5 +3,5 @@ namespace InnovEnergy.SaliMax.Controller;
public static class StateConfig
{
public static readonly IReadOnlyList<Int32> AcPowerStageEnableStates = new[] { 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
public static readonly IReadOnlyList<Int32> AcPowerStageEnableStates = new[] {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
}

View File

@ -10,13 +10,13 @@ namespace InnovEnergy.SaliMax.Controller;
public record StatusRecord
{
public TruConvertAcStatus? InverterStatus { get; init; }
public TruConvertDcStatus? DcDcStatus { get; init; }
public Battery48TLStatus? BatteryStatus { get; init; }
public EmuMeterStatus? GridMeterStatus { get; init; }
public SaliMaxRelayStatus? SaliMaxRelayStatus { get; init; }
public AmptStatus? AmptStatus { get; init; }
public EmuMeterStatus? AcInToAcOutMeterStatus { get; init; }
public SalimaxConfig SalimaxConfig { get; init; } = null!;
public TruConvertAcStatus? InverterStatus { get; init; }
public TruConvertDcStatus? DcDcStatus { get; init; }
public Battery48TLStatus[]? BatteriesStatus { get; set; } = Array.Empty<Battery48TLStatus>(); // TODO remove static
public AvgBatteriesStatus? AvgBatteriesStatus { get; init; }
public EmuMeterStatus? GridMeterStatus { get; init; }
public SaliMaxRelayStatus? SaliMaxRelayStatus { get; init; }
public AmptStatus? AmptStatus { get; init; }
public EmuMeterStatus? AcInToAcOutMeterStatus { get; init; }
public SalimaxConfig SalimaxConfig { get; init; } = null!;
}

View File

@ -11,15 +11,16 @@ public static class Ampt
if (s is null)
return null;
// TODO return one AMPT device to sum all the other
return DeviceType
.PvOnDc
.CreateDevice("AMPT")
.AddProp("Current 1", s.Current1)
.AddProp("Current 2", s.Current2)
.AddProp("Voltage 1", s.Voltage1)
.AddProp("Voltage 2", s.Voltage2)
.AddProp("Power 1", s.Current1 * s.Voltage1)
.AddProp("Power 2", s.Current2 * s.Voltage2);
// .AddDcConnection(s.AvgCurrent.Round3(), s.AvgVolatge.Round3());
.AddProp("Current 1", s.Devices[0].Dc.Current)
.AddProp("Current 2", s.Devices[1].Dc.Current)
.AddProp("Voltage 1", s.Devices[0].Dc.Voltage)
.AddProp("Voltage 2", s.Devices[1].Dc.Voltage)
.AddProp("Power 1", s.Devices[0].Dc.Current * s.Devices[0].Dc.Voltage)
.AddProp("Power 2", s.Devices[1].Dc.Current * s.Devices[1].Dc.Voltage);
}
}

View File

@ -14,13 +14,13 @@ public static class Battery48Tl
return DeviceType
.Battery
.CreateDevice("48TL Battery")
.AddDc48Connection(s.Current.Round3(),s.Voltage.Round3())
.AddDc48Connection(s.Dc.Current.Round3(),s.Dc.Voltage.Round3())
.AddAlarms(s.Alarms)
.AddWarnings(s.Warnings)
.AddProp("Soc", s.Soc.Round3())
.AddProp("HeaterOn", s.HeaterOn)
.AddProp("EocReached", s.EocReached)
.AddProp("BatteryCold", s.BatteryCold)
.AddProp("Temperature", s.BatteryTemperature);
.AddProp("Temperature", s.Temperature);
}
}

View File

@ -17,16 +17,16 @@ public static class EmuMeter
//
var l1 = CreateAcPhase(s.CurrentL1, s.VoltageL1N, ACos(s.PowerFactorL1));
var l2 = CreateAcPhase(s.CurrentL2, s.VoltageL2N, ACos(s.PowerFactorL2));
var l3 = CreateAcPhase(s.CurrentL3, s.VoltageL3N, ACos(s.PowerFactorL3));
var l1 = CreateAcPhase(s.Ac.L1.Current, s.Ac.L1.Voltage, ACos(s.Ac.L1.PowerFactor));
var l2 = CreateAcPhase(s.Ac.L2.Current, s.Ac.L2.Voltage, ACos(s.Ac.L2.PowerFactor));
var l3 = CreateAcPhase(s.Ac.L3.Current, s.Ac.L3.Voltage, ACos(s.Ac.L3.PowerFactor));
var ac = new JsonObject
{
["L1"] = l1,
["L2"] = l2,
["L3"] = l3,
["Frequency"] = s.Frequency
["Frequency"] = s.Ac.Frequency
};
var status = new JsonObject
@ -39,8 +39,8 @@ public static class EmuMeter
private static IEnumerable<JsonObject> GetAcPhases(this EmuMeterStatus s)
{
yield return CreateAcPhase(s.CurrentL1.Round3(),s.VoltageL1N.Round3(),s.PowerFactorL1.Apply(ACos).Round3());
yield return CreateAcPhase(s.CurrentL2.Round3(),s.VoltageL2N.Round3(),s.PowerFactorL2.Apply(ACos).Round3());
yield return CreateAcPhase(s.CurrentL3.Round3(),s.VoltageL3N.Round3(),s.PowerFactorL3.Apply(ACos).Round3());
yield return CreateAcPhase(s.Ac.L1.Current.Round3(),s.Ac.L1.Voltage.Round3(),s.Ac.L1.PowerFactor.Apply(ACos).Round3());
yield return CreateAcPhase(s.Ac.L2.Current.Round3(),s.Ac.L2.Voltage.Round3(),s.Ac.L2.PowerFactor.Apply(ACos).Round3());
yield return CreateAcPhase(s.Ac.L3.Current.Round3(),s.Ac.L3.Voltage.Round3(),s.Ac.L3.PowerFactor.Apply(ACos).Round3());
}
}

View File

@ -24,15 +24,16 @@ public static class Salimax
private static IEnumerable<JsonNode?> GetDevices(StatusRecord s)
{
yield return s.InverterStatus.Log("1234");
yield return s.InverterStatus.Log();
yield return s.DcDcStatus.Log("3214");
yield return s.GridMeterStatus.Log(DeviceType.Grid);
yield return s.AcInToAcOutMeterStatus.Log(DeviceType.AcInToAcOut);
yield return s.GridMeterStatus.Log(DeviceType.Grid , "123");
yield return s.AcInToAcOutMeterStatus.Log(DeviceType.AcInToAcOut, "123");
yield return s.AmptStatus.Log();
yield return s.BatteryStatus.Log();
yield return s.BatteriesStatus![0].Log();
yield return s.BatteriesStatus[1].Log();
}
public static JsonArray CreateTopology()
private static JsonArray CreateTopology()
{
var acInBusJson = JsonUtil.CreateBus
(

View File

@ -9,13 +9,12 @@ namespace InnovEnergy.SaliMax.Log;
public static class TruConvertAc
{
// TODO: remove serialNb arg, embed TruConvertDcStatus
public static JsonObject? Log(this TruConvertAcStatus? s, String serialNb)
public static JsonObject? Log(this TruConvertAcStatus? s)
{
if (s is null)
return null;
var dcPower = s.PowerAcL1 + s.PowerAcL2 + s.PowerAcL3;
var dcPower = s.Ac.ActivePower;
var dcVoltage = s.ActualDcLinkVoltageLowerHalfExt + s.ActualDcLinkVoltageUpperHalfExt;
var dcCurrent = dcVoltage != 0m
? dcPower / dcVoltage
@ -25,16 +24,16 @@ public static class TruConvertAc
// TODO: acos quadrant
// TODO: total AC power
var l1 = CreateAcPhase(s.PhaseCurrentL1, s.GridVoltageL1, ACos(s.CosPhiL1));
var l2 = CreateAcPhase(s.PhaseCurrentL2, s.GridVoltageL2, ACos(s.CosPhiL2));
var l3 = CreateAcPhase(s.PhaseCurrentL3, s.GridVoltageL3, ACos(s.CosPhiL3));
var l1 = CreateAcPhase(s.Ac.L1.Current.Round3(), s.Ac.L1.Voltage.Round3(), s.Ac.L1.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
var l2 = CreateAcPhase(s.Ac.L2.Current.Round3(), s.Ac.L2.Voltage.Round3(), s.Ac.L1.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
var l3 = CreateAcPhase(s.Ac.L3.Current.Round3(), s.Ac.L3.Voltage.Round3(), s.Ac.L1.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
var ac = new JsonObject
{
["L1"] = l1,
["L2"] = l2,
["L3"] = l3,
["Frequency"] = s.GridFrequency
["Frequency"] = s.Ac.Frequency
};
var dc = CreateDcPhase(dcCurrent, dcVoltage);
@ -47,20 +46,20 @@ public static class TruConvertAc
["Alarms"] = s.Alarms.ToJsonArray() ,
};
return new JsonObject { [$"TruConvertAc {serialNb}"] = status };
return new JsonObject { [$"TruConvertAc {s.SerialNumber}"] = status };
}
private static IEnumerable<JsonObject> GetAcPhases(this TruConvertAcStatus s)
{
// Math.Acos return "NaN" if the cos phi < -1 or > 1
// Decimal.Acos throw an exception
yield return JsonUtil.CreateAcPhase(s.PhaseCurrentL1.Round3(), s.GridVoltageL1.Round3(),
s.CosPhiL1.Clamp(-1m, 1m).Apply(ACos).Round3());
yield return JsonUtil.CreateAcPhase(s.Ac.L1.Current.Round3(), s.Ac.L1.Voltage.Round3(),
s.Ac.L1.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
yield return JsonUtil.CreateAcPhase(s.PhaseCurrentL2.Round3(), s.GridVoltageL2.Round3(),
s.CosPhiL2.Clamp(-1m, 1m).Apply(ACos).Round3());
yield return JsonUtil.CreateAcPhase(s.Ac.L2.Current.Round3(), s.Ac.L2.Voltage.Round3(),
s.Ac.L2.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
yield return JsonUtil.CreateAcPhase(s.PhaseCurrentL3.Round3(), s.GridVoltageL3.Round3(),
s.CosPhiL3.Clamp(-1m, 1m).Apply(ACos).Round3());
yield return JsonUtil.CreateAcPhase(s.Ac.L3.Current.Round3(), s.Ac.L3.Voltage.Round3(),
s.Ac.L3.PowerFactor.Clamp(-1m, 1m).Apply(ACos).Round3());
}
}

View File

@ -14,30 +14,19 @@ public static class TruConvertDc
if (s is null)
return null;
var dcCurrent = s.DcLinkVoltage != 0m
? s.DcPower / s.DcLinkVoltage
: 0m;
var dcCurrent = s.Dc.Current;
return new JO
{
{
$"TruConvertDc {serialNb}", new JO
{
{ "Dc" , CreateDcPhase(dcCurrent, s.DcLinkVoltage) },
{ "Dc" , CreateDcPhase(dcCurrent, s.Dc.Voltage) },
{ "Dc48" , CreateDcPhase(s.BatteryCurrent, s.BatteryVoltage) },
{ "Warnings", s.Warnings.ToJsonArray() },
{ "Alarms" , s.Alarms.ToJsonArray() },
}
}
};
// return DeviceType
// .DcDc
// .CreateDevice("TruConvertDc")
// //.AddDcConnection(Decimal.Round(current, 3), s.DcLinkVoltage)
// .AddDc48Connection(Decimal.Round(s.BatteryCurrent, 3), Decimal.Round(s.BatteryVoltage, 3))
// .AddWarnings(s.Warnings)
// .AddAlarms(s.Alarms)
// .AddProp("DC Power", s.DcPower);
}
}

View File

@ -1,3 +1,6 @@
#undef BatteriesAllowed
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
@ -14,9 +17,12 @@ using InnovEnergy.SaliMax.Log;
using InnovEnergy.SaliMax.SaliMaxRelays;
using InnovEnergy.SaliMax.SystemConfig;
using InnovEnergy.Time.Unix;
using Utils = InnovEnergy.Lib.StatusApi.Utils;
#pragma warning disable IL2026
namespace InnovEnergy.SaliMax;
internal static class Program
@ -31,7 +37,8 @@ internal static class Program
}
catch (Exception e)
{
await File.AppendAllTextAsync(Config.LogSalimaxLog, String.Join(Environment.NewLine, UnixTime.Now + " \n" + e));
await File.AppendAllTextAsync(Config.LogSalimaxLog,
String.Join(Environment.NewLine, UnixTime.Now + " \n" + e));
throw;
}
}
@ -51,33 +58,58 @@ internal static class Program
};
#if DEBUG
var inverterDevice = new TruConvertAcDevice("127.0.0.1", 5001);
var dcDcDevice = new TruConvertDcDevice("127.0.0.1", 5002);
var gridMeterDevice = new EmuMeterDevice("127.0.0.1", 5003);
var saliMaxRelaysDevice = new SaliMaxRelaysDevice("127.0.0.1", 5004);
var amptDevice = new AmptCommunicationUnit("127.0.0.1", 5005);
var inverterDevice = new TruConvertAcDevice("127.0.0.1", 5001);
var dcDcDevice = new TruConvertDcDevice("127.0.0.1", 5002);
var gridMeterDevice = new EmuMeterDevice("127.0.0.1", 5003);
var saliMaxRelaysDevice = new SaliMaxRelaysDevice("127.0.0.1", 5004);
var amptDevice = new AmptCommunicationUnit("127.0.0.1", 5005);
var acInToAcOutMeterDevice = new EmuMeterDevice("127.0.0.1", 5003); // TODO: use real device
var battery48TlDevice = Battery48TlDevice.Fake();
var salimaxConfig = new SalimaxConfig();
#else
var battery48TlDevice = new Battery48TlDevice("/dev/ttyUSB0", 3);
var inverterDevice = new TruConvertAcDevice("192.168.1.2");
var dcDcDevice = new TruConvertDcDevice("192.168.1.3");
var gridMeterDevice = new EmuMeterDevice("192.168.1.241");
var acInToAcOutMeterDevice = new EmuMeterDevice("192.168.1.241"); // TODO: use real device
var amptDevice = new AmptCommunicationUnit("192.168.1.249");
var saliMaxRelaysDevice = new SaliMaxRelaysDevice("192.168.1.242");
var secondBattery48TlDevice = Battery48TlDevice.Fake();
var firstBattery48TlDevice =Battery48TlDevice.Fake();;
var salimaxConfig = new SalimaxConfig();
#else
#if BatteriesAllowed
var firstBattery48TlDevice = new Battery48TlDevice("/dev/ttyUSB0", 2);
var secondBattery48TlDevice = new Battery48TlDevice("/dev/ttyUSB0", 3);
#endif
var inverterDevice = new TruConvertAcDevice("192.168.1.2");
var dcDcDevice = new TruConvertDcDevice("192.168.1.3");
var gridMeterDevice = new EmuMeterDevice("192.168.1.241");
var acInToAcOutMeterDevice = new EmuMeterDevice("192.168.1.241"); // TODO: use real device
var amptDevice = new AmptCommunicationUnit("192.168.1.249");
var saliMaxRelaysDevice = new SaliMaxRelaysDevice("192.168.1.242");
var salimaxConfig = new SalimaxConfig();
#endif
// This is will be always add manually ? or do we need to read devices automatically in a range of IP @
#if BatteriesAllowed
var battery48TlDevices = new[] { firstBattery48TlDevice, secondBattery48TlDevice };
#endif
var dcDcDevices = new[] { dcDcDevice };
var inverterDevices = new[] { inverterDevice};
StatusRecord ReadStatus()
{
#if BatteriesAllowed
var battery48TlStatusArray = battery48TlDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
#endif
// var dcDcStatusArray = dcDcDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
// var inverterStatusArray = inverterDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
return new StatusRecord
{
InverterStatus = inverterDevice.ReadStatus(),
DcDcStatus = dcDcDevice.ReadStatus(),
BatteryStatus = battery48TlDevice.ReadStatus(),
#if BatteriesAllowed
BatteriesStatus = battery48TlStatusArray,
AvgBatteriesStatus = AvgBatteriesStatus.ReadBatteriesStatus(battery48TlStatusArray),
#else
BatteriesStatus = null,
AvgBatteriesStatus = null,
#endif
AcInToAcOutMeterStatus = acInToAcOutMeterDevice.ReadStatus(),
GridMeterStatus = gridMeterDevice.ReadStatus(),
SaliMaxRelayStatus = saliMaxRelaysDevice.ReadStatus(),
@ -87,10 +119,11 @@ internal static class Program
}
var startTime = UnixTime.Now;
var startTime = UnixTime.Now;
const Int32 delayTime = 10;
ReleaseWriteTopology(startTime);
await UploadTopology(s3Config, Salimax.TopologyToLog(startTime), startTime);
DebugWriteTopology(startTime);
Console.WriteLine("press ctrl-C to stop");
@ -104,16 +137,17 @@ internal static class Program
await Task.Delay(delayTime);
t = UnixTime.Now;
}
var status = ReadStatus();
//var jsonLog = status.ToLog(t);
//await UploadTimeSeries(s3Config, jsonLog, t);
var status = ReadStatus();
#if BatteriesAllowed
var jsonLog = status.ToLog(t);
await UploadTimeSeries(s3Config, jsonLog, t);
var controlRecord = Controller.Controller.SaliMaxControl(status);
Controller.Controller.WriteControlRecord(controlRecord, inverterDevice, dcDcDevice, saliMaxRelaysDevice);
// JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue);
//ReleaseWriteLog(jsonLog, t);
//JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue);
#endif
PrintTopology(status);
while (UnixTime.Now == t)
@ -127,42 +161,41 @@ internal static class Program
{
const String chargingSeparator = ">>>>>>>>>>";
const String dischargingSeparator = "<<<<<<<<<";
const Int32 height = 25;
const Int32 height = 25;
var boxSize = chargingSeparator.Length / 2;
var pwr = s.InverterStatus!.Ac.ActivePower;
var pvPower = (s.AmptStatus!.Devices[0].Dc.Voltage * s.AmptStatus.Devices[0].Dc.Current + s.AmptStatus!.Devices[1].Dc.Voltage * s.AmptStatus.Devices[1].Dc.Current).Round0(); // TODO using one Ampt
var loadPower = Utils.Round3((s.GridMeterStatus!.ActivePowerL123 + pwr)); // it's a + because the pwr is inverted
var pwr = s.InverterStatus!.PowerAcL1 + s.InverterStatus.PowerAcL2 + s.InverterStatus.PowerAcL3;
var pvPower = (s.AmptStatus!.Voltage1 * s.AmptStatus.Current1 + s.AmptStatus!.Voltage2 * s.AmptStatus.Current2).Round0();
var loadPower = (s.GridMeterStatus!.ActivePowerL123 + pwr).Round3(); // it's a + because the pwr is inverted
var gridSeparator = s.GridMeterStatus!.ActivePowerL123 > 0 ? chargingSeparator : dischargingSeparator;
var gridSeparator = s.GridMeterStatus!.ActivePowerL123 > 0 ? chargingSeparator : dischargingSeparator;
var inverterSeparator = -pwr > 0 ? chargingSeparator : dischargingSeparator;
var dcSeparator = -s.DcDcStatus!.DcPower > 0 ? chargingSeparator : dischargingSeparator;
var batterySeparator = s.BatteryStatus!.Power > 0 ? chargingSeparator : dischargingSeparator;
var dcSeparator = -s.DcDcStatus!.Dc.Power > 0 ? chargingSeparator : dischargingSeparator;
#if BatteriesAllowed
var battery1Separator = s.BatteriesStatus[0]!.Power > 0 ? chargingSeparator : dischargingSeparator;
var battery2Separator = s.BatteriesStatus[1]!.Power > 0 ? chargingSeparator : dischargingSeparator;
#endif
////////////////// Grid //////////////////////
var boxGrid = AsciiArt.CreateBox
////////////////// Grid //////////////////////
var boxGrid = AsciiArt.CreateBox
(
"Grid",
s.GridMeterStatus.VoltageL1N.V(),
s.GridMeterStatus.VoltageL2N.V(),
s.GridMeterStatus.VoltageL3N.V()
s.GridMeterStatus.Ac.L1.Voltage.V(),
s.GridMeterStatus.Ac.L2.Voltage.V(),
s.GridMeterStatus.Ac.L3.Voltage.V()
).AlignCenterVertical(height);
//var gridBox = CreateRect(0, boxGrid).AlignBottom(height);
var gridAcBusArrow = AsciiArt.CreateHorizontalArrow(s.GridMeterStatus!.ActivePowerL123.Round0(), gridSeparator).AlignCenterVertical(height);
var gridAcBusArrow = AsciiArt.CreateHorizontalArrow(s.GridMeterStatus!.ActivePowerL123.Round0(), gridSeparator)
.AlignCenterVertical(height);
////////////////// Ac Bus //////////////////////
var boxAcBus = AsciiArt.CreateBox
var boxAcBus = AsciiArt.CreateBox
(
"AC Bus",
s.InverterStatus.GridVoltageL1.V(),
s.InverterStatus.GridVoltageL2.V(),
s.InverterStatus.GridVoltageL3.V()
s.InverterStatus.Ac.L1.Voltage.V(),
s.InverterStatus.Ac.L2.Voltage.V(),
s.InverterStatus.Ac.L3.Voltage.V()
);
var boxLoad = AsciiArt.CreateBox
@ -172,107 +205,147 @@ internal static class Program
""
);
var loadRect = CreateRect(loadPower, boxAcBus, boxLoad).AlignBottom(height);
var loadRect = CreateRect(boxAcBus, boxLoad, loadPower).AlignBottom(height);
var acBusInvertArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator).AlignCenterVertical(height);
var acBusInvertArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
.AlignCenterVertical(height);
//////////////////// Inverter /////////////////////////
var inverterBox = AsciiArt.CreateBox
(
"",
"Inverter",
""
).AlignCenterVertical(height);
(
"",
"Inverter",
""
).AlignCenterVertical(height);
var inverterArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator).AlignCenterVertical(height);
var inverterArrow = AsciiArt.CreateHorizontalArrow(-pwr.Round0(), inverterSeparator)
.AlignCenterVertical(height);
//////////////////// DC Bus /////////////////////////
var dcBusBox = AsciiArt.CreateBox
(
"DC Bus",
(s.InverterStatus.ActualDcLinkVoltageLowerHalfExt + s.InverterStatus.ActualDcLinkVoltageUpperHalfExt).V(),
""
);
var dcBusBox = AsciiArt.CreateBox
(
"DC Bus",
(s.InverterStatus.ActualDcLinkVoltageLowerHalfExt + s.InverterStatus.ActualDcLinkVoltageUpperHalfExt).V(),
""
);
var pvBox = AsciiArt.CreateBox
(
"",
"MPPT",
""
);
(
"MPPT",
((s.AmptStatus!.Devices[0].Dc.Voltage + s.AmptStatus!.Devices[1].Dc.Voltage) / 2).Round0().V(),
""
);
var pvRect = CreateRect(pvPower, pvBox, dcBusBox).AlignTop(height);
var pvRect = CreateRect(pvBox, dcBusBox, pvPower).AlignTop(height);
var dcBusArrow = AsciiArt.CreateHorizontalArrow(-s.DcDcStatus!.DcPower.Round0(), dcSeparator).AlignCenterVertical(height);
var dcBusArrow = AsciiArt.CreateHorizontalArrow(-s.DcDcStatus!.Dc.Power, dcSeparator)
.AlignCenterVertical(height);
//////////////////// Dc/Dc /////////////////////////
var dcBox = AsciiArt.CreateBox
(
"",
"Dc/Dc",
""
).AlignCenterVertical(height);
var dcArrow = AsciiArt.CreateHorizontalArrow(s.BatteryStatus!.Power.Round0(), batterySeparator).AlignCenterVertical(height);
//////////////////// Battery /////////////////////////
var batteryBox = AsciiArt.CreateBox
(
"Battery",
s.BatteryStatus!.Voltage.V(),
""
).AlignCenterVertical(height);
var batteryDegree = AsciiArt.CreateTransitionPadLeft(s.BatteryStatus!.Soc.Percent(), s.BatteryStatus!.BatteryTemperature.Celsius()).AlignCenterVertical(height);
// var loadLength = boxSize * 6;
// var mpptLength = boxSize * 14;
//
// var mpptBox = AsciiArt.CreateVerticalPad(pvPower.W(),(Int16)mpptLength, true, "MPPT") + "\n";
// var loadBox = AsciiArt.CreateVerticalPad(loadPower.W(), (Int16)loadLength, false, "Load");
(
"Dc/Dc",
s.DcDcStatus.BatteryVoltage.V(),
""
).AlignCenterVertical(height);
#if BatteriesAllowed
var dcArrow1 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[0]!.Power.Round0(), battery1Separator);
var dcArrow2 = AsciiArt.CreateHorizontalArrow(s.BatteriesStatus[1]!.Power.Round0(), battery2Separator);
#else
var dcArrow1 ="";
var dcArrow2 = "";
var dcArrowRect = CreateRect(dcArrow1, dcArrow2).AlignCenterVertical(height);
#endif
var topology = boxGrid.SideBySideWith(gridAcBusArrow,"")
.SideBySideWith(loadRect,"")
.SideBySideWith(acBusInvertArrow,"")
.SideBySideWith(inverterBox,"")
.SideBySideWith(inverterArrow,"")
.SideBySideWith(pvRect,"")
.SideBySideWith(dcBusArrow,"")
.SideBySideWith(dcBox,"")
.SideBySideWith(dcArrow,"")
.SideBySideWith(batteryBox,"")
.SideBySideWith(batteryDegree, "") + "\n";
#if BatteriesAllowed
// var topology = mpptBox + boxSide11 + loadBox;
//////////////////// Batteries /////////////////////////
var battery1Box = AsciiArt.CreateBox
(
"Battery 1",
s.BatteriesStatus[0].Voltage.V(),
s.BatteriesStatus[0].Soc.Percent(),
s.BatteriesStatus[0].Temperature.Celsius()
);
var battery2Box = AsciiArt.CreateBox
(
"Battery 2",
s.BatteriesStatus[1].Voltage.V(),
s.BatteriesStatus[1].Soc.Percent(),
s.BatteriesStatus[1].Temperature.Celsius()
);
var batteryRect = CreateRect(battery1Box, battery2Box).AlignCenterVertical(height);
var avgBatteryBox = AsciiArt.CreateBox
(
"Batteries",
s.AvgBatteriesStatus!.Voltage.V(),
s.AvgBatteriesStatus.Soc.Percent(),
s.AvgBatteriesStatus.Temperature.Celsius()
).AlignCenterVertical(height);
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
.SideBySideWith(loadRect, "")
.SideBySideWith(acBusInvertArrow, "")
.SideBySideWith(inverterBox, "")
.SideBySideWith(inverterArrow, "")
.SideBySideWith(pvRect, "")
.SideBySideWith(dcBusArrow, "")
.SideBySideWith(dcBox, "")
.SideBySideWith(dcArrowRect, "")
.SideBySideWith(batteryRect, "")
.SideBySideWith(avgBatteryBox, "")+ "\n";
#else
var topology = boxGrid.SideBySideWith(gridAcBusArrow, "")
.SideBySideWith(loadRect, "")
.SideBySideWith(acBusInvertArrow, "")
.SideBySideWith(inverterBox, "")
.SideBySideWith(inverterArrow, "")
.SideBySideWith(pvRect, "")
.SideBySideWith(dcBusArrow, "")
.SideBySideWith(dcBox, "")+ "\n";
#endif
Console.WriteLine(topology);
// Console.WriteLine(loadRect);
// Console.WriteLine(pvRect);
}
private static String CreateRect(Decimal loadPower, String boxAcBus, String boxLoad = "")
private static String CreateRect(String boxTop, String boxBottom, Decimal power)
{
var loadAcBusArrow = AsciiArt.CreateVerticalArrow(loadPower);
var boxes = new[] { boxAcBus, loadAcBusArrow, boxLoad };
var powerArrow = AsciiArt.CreateVerticalArrow(power);
var boxes = new[] { boxTop, powerArrow, boxBottom };
var maxWidth = boxes.Max(l => l.Width());
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
return rect;
}
private static String CreateRect(String boxTop, String boxBottom)
{
var boxes = new[] { boxTop, boxBottom };
var maxWidth = boxes.Max(l => l.Width());
var rect = boxes.Select(l => l.AlignCenterHorizontal(maxWidth)).JoinLines();
return rect;
}
// to delete not used anymore
[Conditional("RELEASE")]
private static void ReleaseWriteLog(JsonObject jsonLog, UnixTime timestamp)
{
WriteToFile(jsonLog, "/home/debian/DataSaliMax/" + timestamp);
// WriteToFile(jsonLog, "/home/debian/DataSaliMax/" + timestamp); // this is was for beaglebone TODO
}
[Conditional("RELEASE")]
private static void ReleaseWriteTopology(UnixTime timestamp)
// [Conditional("RELEASE")]
private static JsonObject ReleaseWriteTopology(UnixTime timestamp)
{
var topologyJson = Salimax.TopologyToLog(timestamp);
WriteToFile(topologyJson, "/home/debian/DataSaliMax/topology" + timestamp);
// WriteToFile(topologyJson, "/home/debian/DataSaliMax/topology" + timestamp); // this is was for beaglebone
return topologyJson;
}
[Conditional("DEBUG")]
@ -318,4 +391,19 @@ internal static class Program
Console.WriteLine(error);
}
}
private static async Task UploadTopology(S3Config config, JsonObject json, UnixTime unixTime)
{
var payload = JsonSerializer.Serialize(json, JsonOptions);
var s3Path = "topology" + unixTime.Ticks + ".json";
var request = config.CreatePutRequest(s3Path);
var response = await request.PutAsync(new StringContent(payload));
if (response.StatusCode != 200)
{
Console.WriteLine("ERROR: PUT");
var error = response.GetStringAsync();
Console.WriteLine(error);
}
}
}

View File

@ -2,9 +2,6 @@ namespace InnovEnergy.SaliMax.SaliMaxRelays;
public record SaliMaxRelayStatus
{
internal const UInt16 DigitalInputsStartRegister = 1;
internal const UInt16 NbDigitalInputs = 6;
public RelayState K1 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00002
public RelayState K2 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00003
public RelayState K3 { get; init; } = RelayState.Closed; // Address on Adam(0X) 00004

View File

@ -2,5 +2,5 @@ namespace InnovEnergy.SaliMax.SystemConfig;
public static class Config
{
public const String LogSalimaxLog = "/home/debian/Salimax.log";
public const String LogSalimaxLog = "/home/ie-entwicklung/Salimax.log"; // todo remove ie-entwicklung
}

View File

@ -13,7 +13,7 @@ public static class Defaults
Subnet = default, //= 0x FFFFFF00;
Gateway = default, //= 0x C0A80102;
ResetParamToDefault = false, // Coil
CommunicationTimeout = default,
CommunicationTimeout = TimeSpan.FromSeconds(10),
FactoryResetParameters = false,
ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.AcDcAndDcDc,
UpdateSwTrigger = 0,
@ -25,7 +25,7 @@ public static class Defaults
// IlBuildnumber = 0,
PowerStageEnable = true,
SetValueConfig = SymmetricAcOperationMode.Symmetric, // Asymmetric = 0, // this is can not be seen in UI
ResetsAlarmAndWarning = false,
ResetsAlarmAndWarning = true,
PreChargeDcLinkConfig = PreChargeDcLinkConfig.Internal, // 1 = internal
PowerFactorConvention = PowerFactorConvention.Producer, // 0 = producer
SlaveAddress = 1,
@ -66,9 +66,9 @@ public static class Defaults
GridFormingMode = 0, // 0 = not grid-forming (grid-tied) ,1 = grid-forming TODO enum
//remove DC stuff from AC
DcLinkRefVoltage = 850,
DcLinkMinVoltage = 830,
DcLinkMaxVoltage = 870,
DcLinkRefVoltage = 800,
DcLinkMinVoltage = 780,
DcLinkMaxVoltage = 820,
DcVoltageRefUs = 900,
DcMinVoltageUs = 880,
DcMaxVoltageUs = 920,
@ -88,7 +88,7 @@ public static class Defaults
Subnet = default,
Gateway = default,
ResetParamToDefault = false ,
TimeoutForCommunication = default ,
TimeoutForCommunication = TimeSpan.FromSeconds(10) ,
RestartFlag = false ,
ConnectedSystemConfig = Lib.Devices.Trumpf.TruConvert.SystemConfig.DcDcOnly,
UpdateSwTrigger = 0,
@ -98,7 +98,7 @@ public static class Defaults
SerialNumberDcDc = 0,
MaterialNumberDcDc = 0,
PowerStageEnable = true,
ResetsAlarmAndWarning = false,
ResetsAlarmAndWarning = false,
SlaveAddress = 1,
SubSlaveAddress = 0,
ModbusSlaveId = false,
@ -112,7 +112,7 @@ public static class Defaults
BatteryCurrentSet = 0,
DynamicCurrentPerMillisecond = 2,
DcLinkControlMode = 1,
ReferenceVoltage = 850,
ReferenceVoltage = 800,
UpperVoltageWindow = 40,
LowerVoltageWindow = 40,
VoltageDeadBand = 0,

View File

@ -1,6 +1,7 @@
using DecimalMath;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.StatusApi.Connections;
namespace InnovEnergy.Lib.Devices.Ampt;
@ -48,25 +49,22 @@ public class AmptCommunicationUnit
.ToList();
var amptSt = new AmptStatus
{
Sid = r.GetUInt32(1),
IdSunSpec = r.GetUInt16(3),
Manufacturer = r.GetString(5, 16),
Model = r.GetString(21, 16),
Version = r.GetString(45, 8),
SerialNumber = r.GetString(53, 16),
DeviceAddress = r.GetInt16(69),
IdVendor = r.GetUInt16(71),
Devices = devices,
Current1 = r.GetInt16(90) * currentFactor,
Current2 = r.GetInt16(106) * currentFactor,
Voltage1 = r.GetUInt32(91) * voltageFactor,
Voltage2 = r.GetUInt32(107) * voltageFactor,
(
Sid : r.GetUInt32(1),
IdSunSpec : r.GetUInt16(3),
Manufacturer : r.GetString(5, 16),
Model : r.GetString(21, 16),
Version : r.GetString(45, 8),
SerialNumber : r.GetString(53, 16),
DeviceAddress : r.GetInt16(69),
IdVendor : r.GetUInt16(71),
Devices : devices
// devices.d Current1 = r.GetInt16(90) * currentFactor,
// Current2 = r.GetInt16(106) * currentFactor,
// Voltage1 = r.GetUInt32(91) * voltageFactor,
// Voltage2 = r.GetUInt32(107) * voltageFactor
//AvgVolatge = ReadDevicesVoltage(nbrOfDevices),
//AvgCurrent = ReadDevicesCurrent(nbrOfDevices)
};
);
return amptSt;
@ -104,26 +102,30 @@ public class AmptCommunicationUnit
var b = (UInt16)(FirstDeviceOffset + deviceNumber * RegistersPerDevice); // base address
return new AmptDeviceStatus
{
DeviceId = r.GetInt16 (b) ,
Timestamp = r.GetUInt32((UInt16)(b + 3)),
Current = r.GetUInt16((UInt16)(b + 5)) * currentFactor,
Voltage = r.GetUInt32((UInt16)(b + 6)) * voltageFactor,
ProductionToday = r.GetUInt32((UInt16)(b + 12))* energyFactor,
Strings = new AmptStringStatus[]
(
Dc : new DcConnection
(
Voltage:r.GetUInt32((UInt16)(b + 6)) * voltageFactor,
Current:r.GetUInt16((UInt16)(b + 5)) * currentFactor
),
DeviceId : r.GetInt16 (b) ,
Timestamp : r.GetUInt32((UInt16)(b + 3)),
ProductionToday : r.GetUInt32((UInt16)(b + 12))* energyFactor,
Strings : new []
{
new()
{
Voltage = r.GetUInt32((UInt16)(b + 8)) * voltageFactor,
Current = r.GetUInt16((UInt16)(b + 14)) * currentFactor,
},
new()
{
Voltage = r.GetUInt32((UInt16)(b + 9)) * voltageFactor,
Current = r.GetUInt16((UInt16)(b + 15)) * currentFactor,
}
new DcConnection
(
Voltage : r.GetUInt32((UInt16)(b + 8)) * voltageFactor,
Current : r.GetUInt16((UInt16)(b + 14)) * currentFactor
),
new DcConnection
(
Voltage : r.GetUInt32((UInt16)(b + 9)) * voltageFactor,
Current : r.GetUInt16((UInt16)(b + 15)) * currentFactor
)
}
};
);
}
}
}

View File

@ -1,14 +1,15 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Devices;
namespace InnovEnergy.Lib.Devices.Ampt;
public record AmptDeviceStatus
{
internal const UInt16 NbrOfStrings = 2;
public Int16 DeviceId { get; init; } // The string number
public UInt32 Timestamp { get; init; } // The UTC timestamp of the measurements
public Decimal Current { get; init; } // String output current in mA
public Decimal Voltage { get; init; } // String output voltage in mV
public Decimal ProductionToday { get; init; } // converted to kW in AmptCU class
public IReadOnlyList<AmptStringStatus> Strings { get; init; } = null!;
}
(
DcConnection Dc,
// UInt16 NbrOfStrings,
Int16 DeviceId, // The string number
UInt32 Timestamp, // The UTC timestamp of the measurements
Decimal ProductionToday, // converted to kW in AmptCU class
IReadOnlyList<DcConnection> Strings
): Mppt(Dc, Strings)
{}

View File

@ -1,25 +1,27 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Devices;
namespace InnovEnergy.Lib.Devices.Ampt;
public record AmptStatus
(
UInt32 Sid, // A well-known value 0x53756e53, uniquely identifies this as a SunSpec Modbus Map
UInt16 IdSunSpec, // A well-known value 1, uniquely identifies this as a SunSpec Common Model
// UInt16 L, // Well-known # of 16-bit registers to follow : 66
String? Manufacturer, // A well-known value registered with SunSpec for compliance: "Ampt"
String? Model, // Manufacturer specific value "Communication Unit"
String? Version, // Software Version
String? SerialNumber, // Manufacturer specific value
Int16 DeviceAddress, // Modbus Device ID
UInt16 IdVendor, // Ampt SunSpec Vendor Code 64050
// Decimal Current1,
// Decimal Current2,
// Decimal Voltage1,
// Decimal Voltage2,
IReadOnlyList<AmptDeviceStatus> Devices
//internal const UInt16 StartRegister = 1;
//internal const UInt16 TotalNbOfRegister = 116;
)
{
internal const UInt16 StartRegister = 1;
internal const UInt16 TotalNbOfRegister = 116;
public UInt32 Sid { get; init; } // A well-known value 0x53756e53, uniquely identifies this as a SunSpec Modbus Map
public UInt16 IdSunSpec { get; init; } // A well-known value 1, uniquely identifies this as a SunSpec Common Model
//public UInt16 L { get; init; } // Well-known # of 16-bit registers to follow : 66
public String? Manufacturer { get; init; } // A well-known value registered with SunSpec for compliance: "Ampt"
public String? Model { get; init; } // Manufacturer specific value "Communication Unit"
public String? Version { get; init; } // Software Version
public String? SerialNumber { get; init; } // Manufacturer specific value
public Int16 DeviceAddress { get; init; } // Modbus Device ID
public UInt16 IdVendor { get; init; } // Ampt SunSpec Vendor Code 64050
public Decimal AvgVolatge { get; init; }
public Decimal AvgCurrent { get; init; }
public Decimal Current1 { get; init; }
public Decimal Current2 { get; init; }
public Decimal Voltage1 { get; init; }
public Decimal Voltage2 { get; init; }
public IReadOnlyList<AmptDeviceStatus> Devices { get; init; } = null!;
}

View File

@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="../../Utils/Utils.csproj" />
<ProjectReference Include="../../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../../StatusApi/StatusApi.csproj" />
</ItemGroup>
</Project>

View File

@ -1,5 +1,7 @@
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
using InnovEnergy.Lib.StatusApi.Connections;
namespace InnovEnergy.Lib.Devices.Battery48TL;
@ -33,6 +35,7 @@ public class Battery48TlDevice
{
if (Modbus is null) // TODO : remove fake
{
Console.WriteLine("Battery is null");
return null;
}
@ -41,13 +44,101 @@ public class Battery48TlDevice
try
{
var registers = Modbus.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters);
return new Battery48TLStatus(registers);
return TryReadStatus(registers);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.Message + " Battery ");
Modbus.CloseConnection();
return null;
}
}
private Battery48TLStatus? TryReadStatus(ModbusRegisters data)
{
var soc = data.ParseDecimal(register: 1054, scaleFactor: 0.1m);
var eocReached = data.ParseEocReached();
var warnings = new List<String>();
if (data.ParseBool(1006, 1)) warnings.Add("TaM1: BMS temperature high");
if (data.ParseBool(1006, 4)) warnings.Add("TbM1: Battery temperature high");
if (data.ParseBool(1006, 6)) warnings.Add("VBm1: Bus voltage low");
if (data.ParseBool(1006, 8)) warnings.Add("VBM1: Bus voltage high");
if (data.ParseBool(1006, 10)) warnings.Add("IDM1: Discharge current high");
if (data.ParseBool(1006, 24)) warnings.Add("vsM1: String voltage high");
if (data.ParseBool(1006, 26)) warnings.Add("iCM1: Charge current high");
if (data.ParseBool(1006, 28)) warnings.Add("iDM1: Discharge current high");
if (data.ParseBool(1006, 30)) warnings.Add("MID1: String voltages unbalanced");
if (data.ParseBool(1006, 32)) warnings.Add("BLPW: Not enough charging power on bus");
if (data.ParseBool(1006, 35)) warnings.Add("Ah_W: String SOC low");
if (data.ParseBool(1006, 38)) warnings.Add("MPMM: Midpoint wiring problem");
if (data.ParseBool(1006, 39)) warnings.Add("TCMM:");
if (data.ParseBool(1006, 40)) warnings.Add("TCdi: Temperature difference between strings high");
if (data.ParseBool(1006, 41)) warnings.Add("WMTO:");
if (data.ParseBool(1006, 44)) warnings.Add("bit44:");
if (data.ParseBool(1006, 46)) warnings.Add("CELL1:");
var alarms = new List<String>();
if (data.ParseBool(1010, 0)) alarms.Add("Tam : BMS temperature too low");
if (data.ParseBool(1010, 2)) alarms.Add("TaM2 : BMS temperature too high");
if (data.ParseBool(1010, 3)) alarms.Add("Tbm : Battery temperature too low");
if (data.ParseBool(1010, 5)) alarms.Add("TbM2 : Battery temperature too high");
if (data.ParseBool(1010, 7)) alarms.Add("VBm2 : Bus voltage too low");
if (data.ParseBool(1010, 9)) alarms.Add("VBM2 : Bus voltage too high");
if (data.ParseBool(1010, 11)) alarms.Add("IDM2 : Discharge current too high");
if (data.ParseBool(1010, 12)) alarms.Add("ISOB : Electrical insulation failure");
if (data.ParseBool(1010, 13)) alarms.Add("MSWE : Main switch failure");
if (data.ParseBool(1010, 14)) alarms.Add("FUSE : Main fuse blown");
if (data.ParseBool(1010, 15)) alarms.Add("HTRE : Battery failed to warm up");
if (data.ParseBool(1010, 16)) alarms.Add("TCPE : Temperature sensor failure");
if (data.ParseBool(1010, 17)) alarms.Add("STRE :");
if (data.ParseBool(1010, 18)) alarms.Add("CME : Current sensor failure");
if (data.ParseBool(1010, 19)) alarms.Add("HWFL : BMS hardware failure");
if (data.ParseBool(1010, 20)) alarms.Add("HWEM : Hardware protection tripped");
if (data.ParseBool(1010, 21)) alarms.Add("ThM : Heatsink temperature too high");
if (data.ParseBool(1010, 22)) alarms.Add("vsm1 : String voltage too low");
if (data.ParseBool(1010, 23)) alarms.Add("vsm2 : Low string voltage failure");
if (data.ParseBool(1010, 25)) alarms.Add("vsM2 : String voltage too high");
if (data.ParseBool(1010, 27)) alarms.Add("iCM2 : Charge current too high");
if (data.ParseBool(1010, 29)) alarms.Add("iDM2 : Discharge current too high");
if (data.ParseBool(1010, 31)) alarms.Add("MID2 : String voltage unbalance too high");
if (data.ParseBool(1010, 33)) alarms.Add("CCBF : Internal charger hardware failure");
if (data.ParseBool(1010, 34)) alarms.Add("AhFL :");
if (data.ParseBool(1010, 36)) alarms.Add("TbCM :");
if (data.ParseBool(1010, 37)) alarms.Add("BRNF :");
if (data.ParseBool(1010, 42)) alarms.Add("HTFS : If Heaters Fuse Blown");
if (data.ParseBool(1010, 43)) alarms.Add("DATA : Parameters out of range");
if (data.ParseBool(1010, 45)) alarms.Add("CELL2:");
return new Battery48TLStatus(
Dc: new DcConnection
(
Voltage : data.ReadVoltage(),
Current : data.ReadCurrent()),
Soc : !eocReached && soc >= 100m ? 99.9m : soc,
Temperature : data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400),
BusVoltage : data.ParseDecimal(register: 1002, scaleFactor: 0.01m),
GreenLed : data.ParseLedState(register: 1005, led: LedColor.Green),
AmberLed : data.ParseLedState(register: 1006, led: LedColor.Amber),
BlueLed : data.ParseLedState(register: 1005, led: LedColor.Blue),
RedLed : data.ParseLedState(register: 1005, led: LedColor.Red),
Warnings : warnings,
Alarms : alarms,
MainSwitchClosed : data.ParseBool(baseRegister: 1014, bit: 0),
AlarmOutActive : data.ParseBool(baseRegister: 1014, bit: 1),
InternalFanActive : data.ParseBool(baseRegister: 1014, bit: 2),
VoltMeasurementAllowed: data.ParseBool(baseRegister: 1014, bit: 3),
AuxRelay : data.ParseBool(baseRegister: 1014, bit: 4),
RemoteState : data.ParseBool(baseRegister: 1014, bit: 5),
HeaterOn : data.ParseBool(baseRegister: 1014, bit: 6),
EocReached : eocReached,
BatteryCold : data.ParseBatteryCold(),
MaxChargingPower : data.CalcMaxChargePower(),
MaxDischargingPower : data.CalcMaxDischargePower()
);
}
}

View File

@ -1,118 +1,39 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Devices;
namespace InnovEnergy.Lib.Devices.Battery48TL;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Battery48TLStatus
public record Battery48TLStatus
(
DcConnection Dc,
Decimal Soc,
Decimal Temperature,
//Decimal Current,
//Decimal Voltage,
Decimal BusVoltage,
LedState GreenLed,
LedState AmberLed,
LedState BlueLed,
LedState RedLed,
IReadOnlyList<String> Warnings,
IReadOnlyList<String> Alarms,
Boolean MainSwitchClosed,
Boolean AlarmOutActive,
Boolean InternalFanActive,
Boolean VoltMeasurementAllowed,
Boolean AuxRelay,
Boolean RemoteState,
Boolean HeaterOn,
Boolean EocReached,
Boolean BatteryCold,
Decimal MaxChargingPower,
Decimal MaxDischargingPower
)
: Battery(Dc, Soc, Temperature)
{
public Decimal Soc { get; }
public Decimal Current { get; }
public Decimal Voltage { get; }
public Decimal Power => Current * Voltage; // TODO do we have to invert the sign here?
public Decimal BusVoltage { get; }
public Decimal BatteryTemperature { get; }
public LedState GreenLed { get; }
public LedState AmberLed { get; }
public LedState BlueLed { get; }
public LedState RedLed { get; }
public IReadOnlyList<String> Warnings { get; }
public IReadOnlyList<String> Alarms { get; }
public Boolean MainSwitchClosed { get; }
public Boolean AlarmOutActive { get; }
public Boolean InternalFanActive { get; }
public Boolean VoltMeasurementAllowed { get; }
public Boolean AuxRelay { get; }
public Boolean RemoteState { get; }
public Boolean HeaterOn { get; }
public Boolean EocReached { get; }
public Boolean BatteryCold { get; }
public Decimal MaxChargingPower { get; }
public Decimal MaxDischargingPower { get; }
public Battery48TLStatus(ModbusRegisters data)
{
var soc = data.ParseDecimal(register: 1054, scaleFactor: 0.1m);
var eocReached = data.ParseEocReached();
var warnings = new List<String>();
if (data.ParseBool(1006, 1)) warnings.Add("TaM1: BMS temperature high");
if (data.ParseBool(1006, 4)) warnings.Add("TbM1: Battery temperature high");
if (data.ParseBool(1006, 6)) warnings.Add("VBm1: Bus voltage low");
if (data.ParseBool(1006, 8)) warnings.Add("VBM1: Bus voltage high");
if (data.ParseBool(1006, 10)) warnings.Add("IDM1: Discharge current high");
if (data.ParseBool(1006, 24)) warnings.Add("vsM1: String voltage high");
if (data.ParseBool(1006, 26)) warnings.Add("iCM1: Charge current high");
if (data.ParseBool(1006, 28)) warnings.Add("iDM1: Discharge current high");
if (data.ParseBool(1006, 30)) warnings.Add("MID1: String voltages unbalanced");
if (data.ParseBool(1006, 32)) warnings.Add("BLPW: Not enough charging power on bus");
if (data.ParseBool(1006, 35)) warnings.Add("Ah_W: String SOC low");
if (data.ParseBool(1006, 38)) warnings.Add("MPMM: Midpoint wiring problem");
if (data.ParseBool(1006, 39)) warnings.Add("TCMM:");
if (data.ParseBool(1006, 40)) warnings.Add("TCdi: Temperature difference between strings high");
if (data.ParseBool(1006, 41)) warnings.Add("WMTO:");
if (data.ParseBool(1006, 44)) warnings.Add("bit44:");
if (data.ParseBool(1006, 46)) warnings.Add("CELL1:");
var alarms = new List<String>();
if (data.ParseBool(1010, 0)) alarms.Add("Tam : BMS temperature too low");
if (data.ParseBool(1010, 2)) alarms.Add("TaM2 : BMS temperature too high");
if (data.ParseBool(1010, 3)) alarms.Add("Tbm : Battery temperature too low");
if (data.ParseBool(1010, 5)) alarms.Add("TbM2 : Battery temperature too high");
if (data.ParseBool(1010, 7)) alarms.Add("VBm2 : Bus voltage too low");
if (data.ParseBool(1010, 9)) alarms.Add("VBM2 : Bus voltage too high");
if (data.ParseBool(1010, 11)) alarms.Add("IDM2 : Discharge current too high");
if (data.ParseBool(1010, 12)) alarms.Add("ISOB : Electrical insulation failure");
if (data.ParseBool(1010, 13)) alarms.Add("MSWE : Main switch failure");
if (data.ParseBool(1010, 14)) alarms.Add("FUSE : Main fuse blown");
if (data.ParseBool(1010, 15)) alarms.Add("HTRE : Battery failed to warm up");
if (data.ParseBool(1010, 16)) alarms.Add("TCPE : Temperature sensor failure");
if (data.ParseBool(1010, 17)) alarms.Add("STRE :");
if (data.ParseBool(1010, 18)) alarms.Add("CME : Current sensor failure");
if (data.ParseBool(1010, 19)) alarms.Add("HWFL : BMS hardware failure");
if (data.ParseBool(1010, 20)) alarms.Add("HWEM : Hardware protection tripped");
if (data.ParseBool(1010, 21)) alarms.Add("ThM : Heatsink temperature too high");
if (data.ParseBool(1010, 22)) alarms.Add("vsm1 : String voltage too low");
if (data.ParseBool(1010, 23)) alarms.Add("vsm2 : Low string voltage failure");
if (data.ParseBool(1010, 25)) alarms.Add("vsM2 : String voltage too high");
if (data.ParseBool(1010, 27)) alarms.Add("iCM2 : Charge current too high");
if (data.ParseBool(1010, 29)) alarms.Add("iDM2 : Discharge current too high");
if (data.ParseBool(1010, 31)) alarms.Add("MID2 : String voltage unbalance too high");
if (data.ParseBool(1010, 33)) alarms.Add("CCBF : Internal charger hardware failure");
if (data.ParseBool(1010, 34)) alarms.Add("AhFL :");
if (data.ParseBool(1010, 36)) alarms.Add("TbCM :");
if (data.ParseBool(1010, 37)) alarms.Add("BRNF :");
if (data.ParseBool(1010, 42)) alarms.Add("HTFS : If Heaters Fuse Blown");
if (data.ParseBool(1010, 43)) alarms.Add("DATA : Parameters out of range");
if (data.ParseBool(1010, 45)) alarms.Add("CELL2:");
Voltage = data.ReadVoltage();
Current = data.ReadCurrent();
Soc = !eocReached && soc >= 100m ? 99.9m : soc;
BusVoltage = data.ParseDecimal(register: 1002, scaleFactor: 0.01m);
BatteryTemperature = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
GreenLed = data.ParseLedState(register: 1005, led: LedColor.Green);
AmberLed = data.ParseLedState(register: 1006, led: LedColor.Amber);
BlueLed = data.ParseLedState(register: 1005, led: LedColor.Blue);
RedLed = data.ParseLedState(register: 1005, led: LedColor.Red);
Warnings = warnings;
Alarms = alarms;
MainSwitchClosed = data.ParseBool(baseRegister: 1014, bit: 0);
AlarmOutActive = data.ParseBool(baseRegister: 1014, bit: 1);
InternalFanActive = data.ParseBool(baseRegister: 1014, bit: 2);
VoltMeasurementAllowed = data.ParseBool(baseRegister: 1014, bit: 3);
AuxRelay = data.ParseBool(baseRegister: 1014, bit: 4);
RemoteState = data.ParseBool(baseRegister: 1014, bit: 5);
HeaterOn = data.ParseBool(baseRegister: 1014, bit: 6);
EocReached = eocReached;
BatteryCold = data.ParseBatteryCold();
MaxChargingPower = data.CalcMaxChargePower();
MaxDischargingPower = data.CalcMaxDischargePower();
}
}

View File

@ -111,6 +111,22 @@ public static class BatteryDataParser
return pLimit;
}
private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint)
{
// const Int32 holdZone = 300;
// const Int32 maxAllowedTemp = 315;
var kp = 0.05m;
var error = setpoint - power;
var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m));
return controlOutput;
// var a = holdZone - maxAllowedTemp;
// var b = -a * maxAllowedTemp;
}
internal static Decimal CalcMaxChargePower(this ModbusRegisters data)
{
var v = ReadVoltage(data);
@ -133,13 +149,15 @@ public static class BatteryDataParser
{
var v = ReadVoltage(data);
var i = ReadCurrent(data);
var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
var pLimits = new[]
{
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin),
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax),
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin),
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax)
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax),
// CalcPowerLimitImposedByTempLimit(t,315,300)
};
var pLimit = pLimits.Max();

View File

@ -29,7 +29,4 @@ public static class Constants
public const Decimal RIntMin = RStringMin / NumberOfStrings;
public const Decimal RIntMax = RStringMax / NumberOfStrings;
public const Decimal IMax = NumberOfStrings * IMaxPerString;
}

View File

@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="../../Utils/Utils.csproj" />
<ProjectReference Include="../../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../../StatusApi/StatusApi.csproj" />
</ItemGroup>
</Project>

View File

@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="../../../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../../../StatusApi/StatusApi.csproj" />
<ProjectReference Include="../TruConvert/TruConvert.csproj" />
</ItemGroup>

View File

@ -31,7 +31,7 @@ public record TruConvertAcControl
public AcErrorPolicy ErrorHandlingPolicy { get; init;} = 0;
public AcDcGridType GridType { get; init;} = 0;
public UInt16 SubSlaveAddress { get; init;} = 0;
public Boolean UseModbusSlaveIdForAddressing { get; init;} = false;
public Boolean UseModbusSlaveIdForAddressing { get; init;} = false;
public UInt16 SubSlaveErrorPolicy { get; init;} = 0; // must be an enum
public Decimal SignedPowerNominalValue { get; init;} = 0; // resolution 0.001 and Unit kva,
public Decimal SignedPowerSetValueL1 { get; init;} = 0; // resolution 0.001 and Unit kva,
@ -63,9 +63,9 @@ public record TruConvertAcControl
public Decimal MaxPeakCurrentVoltageControlL2 { get; init;} = 0; // resolution 0.01
public Decimal MaxPeakCurrentVoltageControlL3 { get; init;} = 0; // resolution 0.01
public UInt16 GridFormingMode { get; init;} = 1; // 0 = not grid-forming (grid-tied) ,1 = grid-forming
public UInt16 DcLinkRefVoltage { get; init;} = 850;
public UInt16 DcLinkMinVoltage { get; init;} = 830;
public UInt16 DcLinkMaxVoltage { get; init;} = 870;
public UInt16 DcLinkRefVoltage { get; init;} = 800;
public UInt16 DcLinkMinVoltage { get; init;} = 780;
public UInt16 DcLinkMaxVoltage { get; init;} = 820;
public UInt16 DcVoltageRefUs { get; init;} = 900;
public UInt16 DcMinVoltageUs { get; init;} = 880;
public UInt16 DcMaxVoltageUs { get; init;} = 920;

View File

@ -2,7 +2,10 @@ using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Phases;
using InnovEnergy.Lib.Utils;
using static DecimalMath.DecimalEx;
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters;
@ -39,6 +42,8 @@ public class TruConvertAcDevice
WriteRegs(CommunicationTimeout, c.CommunicationTimeout.TotalSeconds.ConvertTo<UInt16>());
WriteRegs(ConnectedSystemConfig, c.ConnectedSystemConfig);
WriteCoils(PowerStageConfig, c.PowerStageEnable,
c.SetValueConfig.ConvertTo<Boolean>(),
c.ResetsAlarmAndWarning);
@ -92,6 +97,8 @@ public class TruConvertAcDevice
private void WriteRegs (UInt16 a, params UInt16[] regs) => ModbusTcpClient.WriteRegisters(a, regs);
private void WriteCoils(UInt16 a, params Boolean[] coils) => ModbusTcpClient.WriteMultipleCoils(a, coils);
private static Decimal GetPhi(Decimal cosPhi) => cosPhi.Clamp(-1m, 1m).Apply(ACos);
public TruConvertAcStatus? ReadStatus()
{
try
@ -112,115 +119,171 @@ public class TruConvertAcDevice
{
// Console.WriteLine("Reading Ac Device");
var acActualMain = ModbusTcpClient.ReadInputRegisters(5001, 3);
var acActualAcDc = ModbusTcpClient.ReadInputRegisters(5021, 9);
var acActualAcDc2 = ModbusTcpClient.ReadInputRegisters(5031, 1);
var acActualAcDc3 = ModbusTcpClient.ReadInputRegisters(5131, 6);
var acActualMeasurement = ModbusTcpClient.ReadInputRegisters(5141, 3);
var acActualMeasurement1 = ModbusTcpClient.ReadInputRegisters(5151, 3);
var acActualMeasurement2 = ModbusTcpClient.ReadInputRegisters(5161, 3);
var acActualMeasurement3 = ModbusTcpClient.ReadInputRegisters(5171, 3);
var acActualMeasurement4 = ModbusTcpClient.ReadInputRegisters(5187, 2);
var acActualMeasurement5 = ModbusTcpClient.ReadInputRegisters(5189, 2);
var acActualMeasurement6 = ModbusTcpClient.ReadInputRegisters(5191, 2);
var acActualMeasurement7 = ModbusTcpClient.ReadInputRegisters(5201, 1);
var acActualMeasurement8 = ModbusTcpClient.ReadInputRegisters(5211, 4);
var acActualMeasurement9 = ModbusTcpClient.ReadInputRegisters(5221, 2);
var acActualTemp = ModbusTcpClient.ReadInputRegisters(5501, 1);
var acWarningValues = ModbusTcpClient.ReadInputRegisters(2402, 22);
var acAlarmValues = ModbusTcpClient.ReadInputRegisters(2809, 22);
var acSetValues = ModbusTcpClient.ReadInputRegisters(4196, 1);
var acSerialNumber = ModbusTcpClient.ReadInputRegisters(2009, 2);
var acActualMain = ModbusTcpClient.ReadInputRegisters(5001, 3);
var acActualAcDc = ModbusTcpClient.ReadInputRegisters(5021, 9);
var acActualAcDc2 = ModbusTcpClient.ReadInputRegisters(5031, 1);
var acActualAcDc3 = ModbusTcpClient.ReadInputRegisters(5131, 6);
var acActualMeasurement = ModbusTcpClient.ReadInputRegisters(5141, 3);
var acActualMeasurement1 = ModbusTcpClient.ReadInputRegisters(5151, 3);
var acActualMeasurement2 = ModbusTcpClient.ReadInputRegisters(5161, 3);
var acActualMeasurement3 = ModbusTcpClient.ReadInputRegisters(5171, 3);
var acActualMeasurement4 = ModbusTcpClient.ReadInputRegisters(5187, 2);
var acActualMeasurement5 = ModbusTcpClient.ReadInputRegisters(5189, 2);
var acActualMeasurement6 = ModbusTcpClient.ReadInputRegisters(5191, 2);
var acActualMeasurement7 = ModbusTcpClient.ReadInputRegisters(5201, 1);
var acActualMeasurement8 = ModbusTcpClient.ReadInputRegisters(5211, 4);
var acActualMeasurement9 = ModbusTcpClient.ReadInputRegisters(5221, 2);
var acActualTemp = ModbusTcpClient.ReadInputRegisters(5501, 1);
var acWarningValues = ModbusTcpClient.ReadInputRegisters(2402, 22);
var acAlarmValues = ModbusTcpClient.ReadInputRegisters(2809, 22);
var acSetValues = ModbusTcpClient.ReadInputRegisters(4196, 1);
var warnings = Enumerable
.Range(2404, 20)
.Select(n => acWarningValues.GetUInt16((UInt16)n).ConvertTo<WarningMessage>())
.ToArray();
.Range(2404, 20)
.Select(n => acWarningValues.GetUInt16((UInt16)n).ConvertTo<WarningMessage>())
.ToArray();
var alarms = Enumerable
.Range(2811, 20)
.Select(n => acAlarmValues.GetUInt16((UInt16)n).ConvertTo<AlarmMessage>())
.Where(m => m != AlarmMessage.NoAlarm)
.ToArray();
.Range(2811, 20)
.Select(n => acAlarmValues.GetUInt16((UInt16)n).ConvertTo<AlarmMessage>())
.Where(m => m != AlarmMessage.NoAlarm)
.ToArray();
var dcPower = acActualMeasurement.GetInt16(5141) * 1m + acActualMeasurement.GetInt16(5142) * 1m + acActualMeasurement.GetInt16(5143) * 1m;
var dcVoltage = acActualMeasurement8.GetUInt16(5214) + acActualMeasurement8.GetUInt16(5213);
var dcCurrent = dcVoltage != 0m
? dcPower / dcVoltage
: 0m;
// //acActualMeasurement
// PowerAcL1 = acActualMeasurement.GetInt16(5141) * 1m, // in Watt
// PowerAcL2 = acActualMeasurement.GetInt16(5142) * 1m, // in Watt
// PowerAcL3 = acActualMeasurement.GetInt16(5143) * 1m, // in Watt
//
//acActualMeasurement1
// PhaseCurrentL1 = acActualMeasurement1.GetUInt16(5151) * 0.01m,
// PhaseCurrentL2 = acActualMeasurement1.GetUInt16(5152) * 0.01m,
// PhaseCurrentL3 = acActualMeasurement1.GetUInt16(5153) * 0.01m,
//acActualMeasurement2
// GridVoltageL1 = acActualMeasurement2.GetUInt16(5161) * 0.1m,
// GridVoltageL2 = acActualMeasurement2.GetUInt16(5162) * 0.1m,
// GridVoltageL3 = acActualMeasurement2.GetUInt16(5163) * 0.1m,
//acActualMeasurement3
// CosPhiL1 = acActualMeasurement3.GetInt16(5171) * 0.01m,
// CosPhiL2 = acActualMeasurement3.GetInt16(5172) * 0.01m,
// CosPhiL3 = acActualMeasurement3.GetInt16(5173) * 0.01m,
// //acActualMeasurement4
// SumPowerL1 = acActualMeasurement4.GetUInt32(5187) * 1m, // in Watt
// //acActualMeasurement5
// SumPowerL2 = acActualMeasurement5.GetUInt32(5189) * 1m, // in Watt
// //acActualMeasurement6
// SumPowerL3 = acActualMeasurement6.GetUInt32(5191) * 1m, // in Watt
// //acActualMeasurement9
// GridFrequency = acActualMeasurement7.GetInt16(5201) * 0.01m,
//acActualMeasurement11
// VoltageIntNtoPE = acActualMeasurement9.GetInt16(5221) * 0.1m,
// VoltageExtNtoPE = acActualMeasurement9.GetInt16(5222) * 0.1m,
//
// ApparentPowerAcL1 = acActualAcDc3.GetUInt16(5131) * 1m, // in VA
// ApparentPowerAcL2 = acActualAcDc3.GetUInt16(5132) * 1m, // in VA
// ApparentPowerAcL3 = acActualAcDc3.GetUInt16(5133) * 1m, // in VA
var apparentPowerAcL1 = acActualAcDc3.GetUInt16(5131) * 1m;
var apparentPowerAcL2 = acActualAcDc3.GetUInt16(5132) * 1m;
var apparentPowerAcL3 = acActualAcDc3.GetUInt16(5133) * 1m;
var powerAcL1 = acActualMeasurement.GetInt16(5141) * 1m; // in Watt
var powerAcL2 = acActualMeasurement.GetInt16(5142) * 1m; // in Watt
var powerAcL3 = acActualMeasurement.GetInt16(5143) * 1m; // in Watt
var phaseCurrentL1 = acActualMeasurement1.GetUInt16(5151) * 0.01m;
var phaseCurrentL2 = acActualMeasurement1.GetUInt16(5152) * 0.01m;
var phaseCurrentL3 = acActualMeasurement1.GetUInt16(5153) * 0.01m;
var gridVoltageL1 = acActualMeasurement2.GetUInt16(5161) * 0.1m;
var gridVoltageL2 = acActualMeasurement2.GetUInt16(5162) * 0.1m;
var gridVoltageL3 = acActualMeasurement2.GetUInt16(5163) * 0.1m;
var gridFrequency = acActualMeasurement7.GetInt16(5201) * 0.01m;
return new TruConvertAcStatus
{
(
Ac: new ThreePhaseAcConnection
(
AcPhase.FromActiveReactive(
activePower: powerAcL1,
apparentPower: apparentPowerAcL1,
voltage: gridVoltageL1,
current: phaseCurrentL1),
AcPhase.FromActiveReactive(
activePower: powerAcL2,
apparentPower: apparentPowerAcL2,
voltage: gridVoltageL2,
current: phaseCurrentL2),
AcPhase.FromActiveReactive(
activePower: powerAcL3,
apparentPower: apparentPowerAcL3,
voltage: gridVoltageL3,
current: phaseCurrentL3),
gridFrequency // Gird Frequency
),
Dc: new DcConnection
(
dcVoltage,
dcCurrent
),
SerialNumber : acSerialNumber.GetInt32(2009).ToString(),
// acActualMainValues
MainState = acActualMain.GetInt16(5001).ConvertTo<MainState>(),
NumberOfConnectedSlaves = acActualMain.GetUInt16(5002),
NumberOfConnectedSubSlaves = acActualMain.GetUInt16(5003),
MainState : acActualMain.GetInt16(5001).ConvertTo<MainState>(),
NumberOfConnectedSlaves : acActualMain.GetUInt16(5002),
NumberOfConnectedSubSlaves : acActualMain.GetUInt16(5003),
//acActualAcDc
AcDcNominalGridFrequency = acActualAcDc.GetUInt16(5021) * 0.1m,
AcDcNominalGridVoltage = acActualAcDc.GetUInt16(5022),
AcDcActNominalPower = acActualAcDc.GetUInt16(5023),
AcDcActiveGridType = acActualAcDc.GetUInt16(5024).ConvertTo<AcDcGridType>(),
AcDcPowerLimitingStatusAct = acActualAcDc.GetUInt16(5025),
AcDcDcVoltageReference = acActualAcDc.GetUInt16(5026), // DC link reference
AcDcDcLinkVoltageMinAct = acActualAcDc.GetUInt16(5027), // DC link min voltage
AcDcDcLinkVoltageMaxAct = acActualAcDc.GetUInt16(5028), // DC link max voltage
AcDcDcLinkChargedMinVoltage = acActualAcDc.GetUInt16(5029) * 0.01m,
AcDcNominalGridFrequency : acActualAcDc.GetUInt16(5021) * 0.1m,
AcDcNominalGridVoltage : acActualAcDc.GetUInt16(5022),
AcDcActNominalPower : acActualAcDc.GetUInt16(5023),
AcDcActiveGridType : acActualAcDc.GetUInt16(5024).ConvertTo<AcDcGridType>(),
AcDcPowerLimitingStatusAct : acActualAcDc.GetUInt16(5025),
AcDcDcVoltageReference : acActualAcDc.GetUInt16(5026), // DC link reference
AcDcDcLinkVoltageMinAct : acActualAcDc.GetUInt16(5027), // DC link min voltage
AcDcDcLinkVoltageMaxAct : acActualAcDc.GetUInt16(5028), // DC link max voltage
AcDcDcLinkChargedMinVoltage : acActualAcDc.GetUInt16(5029) * 0.01m,
//ac Actual AcDc 2
AcDcStmActCustomer = acActualAcDc2.GetUInt16(5031), //need to check
ApparentPowerAcL1 = acActualAcDc3.GetUInt16(5131) * 1m, // in VA
ApparentPowerAcL2 = acActualAcDc3.GetUInt16(5132) * 1m, // in VA
ApparentPowerAcL3 = acActualAcDc3.GetUInt16(5133) * 1m, // in VA
AcDcOverloadIntegratorStatusL1 = acActualAcDc3.GetUInt16(5134) * 0.1m,
AcDcOverloadIntegratorStatusL2 = acActualAcDc3.GetUInt16(5135) * 0.1m,
AcDcOverloadIntegratorStatusL3 = acActualAcDc3.GetUInt16(5136) * 0.1m,
//acActualMeasurement
PowerAcL1 = acActualMeasurement.GetInt16(5141) * 1m, // in Watt
PowerAcL2 = acActualMeasurement.GetInt16(5142) * 1m, // in Watt
PowerAcL3 = acActualMeasurement.GetInt16(5143) * 1m, // in Watt
//acActualMeasurement1
PhaseCurrentL1 = acActualMeasurement1.GetUInt16(5151) * 0.01m,
PhaseCurrentL2 = acActualMeasurement1.GetUInt16(5152) * 0.01m,
PhaseCurrentL3 = acActualMeasurement1.GetUInt16(5153) * 0.01m,
//acActualMeasurement2
GridVoltageL1 = acActualMeasurement2.GetUInt16(5161) * 0.1m,
GridVoltageL2 = acActualMeasurement2.GetUInt16(5162) * 0.1m,
GridVoltageL3 = acActualMeasurement2.GetUInt16(5163) * 0.1m,
//acActualMeasurement3
CosPhiL1 = acActualMeasurement3.GetInt16(5171) * 0.01m,
CosPhiL2 = acActualMeasurement3.GetInt16(5172) * 0.01m,
CosPhiL3 = acActualMeasurement3.GetInt16(5173) * 0.01m,
//acActualMeasurement4
SumPowerL1 = acActualMeasurement4.GetUInt32(5187) * 1m, // in Watt
//acActualMeasurement5
SumPowerL2 = acActualMeasurement5.GetUInt32(5189) * 1m, // in Watt
//acActualMeasurement6
SumPowerL3 = acActualMeasurement6.GetUInt32(5191) * 1m, // in Watt
//acActualMeasurement9
GridFrequency = acActualMeasurement7.GetInt16(5201) * 0.01m,
AcDcStmActCustomer : acActualAcDc2.GetUInt16(5031), //need to check
AcDcOverloadIntegratorStatusL1 : acActualAcDc3.GetUInt16(5134) * 0.1m,
AcDcOverloadIntegratorStatusL2 : acActualAcDc3.GetUInt16(5135) * 0.1m,
AcDcOverloadIntegratorStatusL3 : acActualAcDc3.GetUInt16(5136) * 0.1m,
AcSignedPowerValue : acSetValues.GetInt16(4196) * -1.0m, // this is also used for control
//acActualMeasurement10
ActualDcLinkVoltageUpperHalf = acActualMeasurement8.GetUInt16(5211),
ActualDcLinkVoltageLowerHalf = acActualMeasurement8.GetUInt16(5212),
ActualDcLinkVoltageUpperHalfExt = acActualMeasurement8.GetUInt16(5213),
ActualDcLinkVoltageLowerHalfExt = acActualMeasurement8.GetUInt16(5214),
//acActualMeasurement11
VoltageIntNtoPE = acActualMeasurement9.GetInt16(5221) * 0.1m,
VoltageExtNtoPE = acActualMeasurement9.GetInt16(5222) * 0.1m,
ActualDcLinkVoltageUpperHalf : acActualMeasurement8.GetUInt16(5211),
ActualDcLinkVoltageLowerHalf : acActualMeasurement8.GetUInt16(5212),
ActualDcLinkVoltageUpperHalfExt : acActualMeasurement8.GetUInt16(5213),
ActualDcLinkVoltageLowerHalfExt : acActualMeasurement8.GetUInt16(5214),
VoltageIntNtoPe : acActualMeasurement9.GetInt16(5221) * 0.1m,
VoltageExtNtoPe : acActualMeasurement9.GetInt16(5222) * 0.1m,
//acActualTemp
InletAirTemperature = acActualTemp.GetInt16(5501) * 0.1m,
InletAirTemperature : acActualTemp.GetInt16(5501) * 0.1m,
Warnings = warnings,
Alarms = alarms,
// acSetValues
AcSignedPowerValue = acSetValues.GetInt16(4196) * -1.0m, // this is also used for control
};
Warnings : warnings,
Alarms : alarms
);
}
}

View File

@ -1,60 +1,43 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Devices;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using AlarmMessages = IReadOnlyList<AlarmMessage>;
using WarningMessages = IReadOnlyList<WarningMessage>;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public record TruConvertAcStatus
(
ThreePhaseAcConnection Ac,
DcConnection Dc,
String SerialNumber,
MainState MainState,
UInt16 NumberOfConnectedSlaves,
UInt16 NumberOfConnectedSubSlaves,
Decimal AcDcNominalGridFrequency,
Decimal AcDcNominalGridVoltage,
Decimal AcDcActNominalPower,
AcDcGridType AcDcActiveGridType,
Decimal AcDcPowerLimitingStatusAct,
Decimal AcDcDcVoltageReference,
Decimal AcDcDcLinkVoltageMinAct,
Decimal AcDcDcLinkVoltageMaxAct,
Decimal AcDcDcLinkChargedMinVoltage,
Decimal AcDcStmActCustomer,
Decimal AcDcOverloadIntegratorStatusL1,
Decimal AcDcOverloadIntegratorStatusL2,
Decimal AcDcOverloadIntegratorStatusL3,
Decimal AcSignedPowerValue,
Decimal ActualDcLinkVoltageUpperHalf,
Decimal ActualDcLinkVoltageLowerHalf,
Decimal ActualDcLinkVoltageUpperHalfExt,
Decimal ActualDcLinkVoltageLowerHalfExt,
Decimal VoltageIntNtoPe,
Decimal VoltageExtNtoPe,
Decimal InletAirTemperature,
WarningMessages Warnings,
AlarmMessages Alarms
) : ThreePhaseInverter(Ac, Dc)
{
public MainState MainState { get; init; } // 5000
public UInt16 NumberOfConnectedSlaves { get; init; } // 5001
public UInt16 NumberOfConnectedSubSlaves { get; init; }
public Decimal AcDcNominalGridFrequency { get; init; }
public Decimal AcDcNominalGridVoltage { get; init; }
public Decimal AcDcActNominalPower { get; init; }
public AcDcGridType AcDcActiveGridType { get; init; }
public Decimal AcDcPowerLimitingStatusAct { get; init; }
public Decimal AcDcDcVoltageReference { get; init; }
public Decimal AcDcDcLinkVoltageMinAct { get; init; }
public Decimal AcDcDcLinkVoltageMaxAct { get; init; }
public Decimal AcDcDcLinkChargedMinVoltage { get; init; }
public Decimal AcDcStmActCustomer { get; init; }
public Decimal ApparentPowerAcL1 { get; init; }
public Decimal ApparentPowerAcL2 { get; init; }
public Decimal ApparentPowerAcL3 { get; init; }
public Decimal AcDcOverloadIntegratorStatusL1 { get; init; }
public Decimal AcDcOverloadIntegratorStatusL2 { get; init; }
public Decimal AcDcOverloadIntegratorStatusL3 { get; init; }
public Decimal PowerAcL1 { get; init; }
public Decimal PowerAcL2 { get; init; }
public Decimal PowerAcL3 { get; init; }
public Decimal PhaseCurrentL1 { get; init; }
public Decimal PhaseCurrentL2 { get; init; }
public Decimal PhaseCurrentL3 { get; init; }
public Decimal GridVoltageL1 { get; init; }
public Decimal GridVoltageL2 { get; init; }
public Decimal GridVoltageL3 { get; init; }
public Decimal CosPhiL1 { get; init; }
public Decimal CosPhiL2 { get; init; }
public Decimal CosPhiL3 { get; init; }
public Decimal SumPowerL1 { get; init; }
public Decimal SumPowerL2 { get; init; }
public Decimal SumPowerL3 { get; init; }
public Decimal GridFrequency { get; init; }
public Decimal ActualDcLinkVoltageUpperHalf { get; init; }
public Decimal ActualDcLinkVoltageLowerHalf { get; init; }
public Decimal ActualDcLinkVoltageUpperHalfExt { get; init; }
public Decimal ActualDcLinkVoltageLowerHalfExt { get; init; }
public Decimal VoltageIntNtoPE { get; init; }
public Decimal VoltageExtNtoPE { get; init; }
public Decimal InletAirTemperature { get; init; }
public WarningMessages Warnings { get; init; } = Array.Empty<WarningMessage>();
public AlarmMessages Alarms { get; init; } = Array.Empty<AlarmMessage>();
public Boolean PowerOperation { get; init; }
public Decimal AcSignedPowerValue { get; init; }// this is in control but we also need to read data
//public Int16 AcSignedPowerValue2 { get; init; } = 0; // For Test
}

View File

@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="../../../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../TruConvert/TruConvert.csproj" />
<ProjectReference Include="../../../StatusApi/StatusApi.csproj" />
</ItemGroup>

View File

@ -12,7 +12,6 @@ public record TruConvertDcControl
public Boolean ResetParamToDefault { get; init;} = false ; // Coil
public TimeSpan TimeoutForCommunication { get; init;} = DefaultCommunicationTimeOut;
public Boolean RestartFlag { get; init;} = false ; // Coil
public SystemConfig ConnectedSystemConfig { get; init;} = SystemConfig.NoConfig ;
public UInt16 UpdateSwTrigger { get; init;} = 0 ;
public UInt16 AutomaticSwUpdate { get; init;} = 0 ;
@ -36,9 +35,9 @@ public record TruConvertDcControl
public Decimal BatteryCurrentSet { get; init;} = 0; // resolution 1.0
public Decimal DynamicCurrentPerMillisecond { get; init;} = 0; // resolution : 0.01
public Decimal DcLinkControlMode { get; init;} = 0; // Parameter aktiviert/deaktiviert "DC link voltage droop mode"
public Decimal ReferenceVoltage { get; init;} = 0; // resolution : 0.1
public Decimal UpperVoltageWindow { get; init;} = 0; // resolution : 0.1
public Decimal LowerVoltageWindow { get; init;} = 0; // resolution : 0.1
public Decimal ReferenceVoltage { get; init;} = 800; // resolution : 0.1
public Decimal UpperVoltageWindow { get; init;} = 40; // resolution : 0.1
public Decimal LowerVoltageWindow { get; init;} = 40; // resolution : 0.1
public Decimal VoltageDeadBand { get; init;} = 0; // resolution : 0.1
private static readonly TimeSpan DefaultCommunicationTimeOut = TimeSpan.FromMinutes(10);

View File

@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.DcControlRegisters;
@ -42,6 +43,7 @@ public class TruConvertDcDevice
*/
// TODO starting from 4000, evaluate what/if needs updating below 4000
WriteRegs(TimeoutCommunication, c.TimeoutForCommunication.TotalSeconds.ConvertTo<UInt16>());
WriteCoils(PowerStageOperation, c.PowerStageEnable);
WriteCoils(ResetsAlarmAndWarning, c.ResetsAlarmAndWarning);
@ -103,18 +105,14 @@ public class TruConvertDcDevice
private TruConvertDcStatus TryReadStatus()
{
// Console.WriteLine("Reading DC Device");
var dcPrValMain = ModbusTcpClient.ReadInputRegisters(5001, 3);
var dcBatteryValue = ModbusTcpClient.ReadInputRegisters(5101, 1);
var dcBatteryValue2 = ModbusTcpClient.ReadInputRegisters(5111, 1);
var dcBatteryValue3 = ModbusTcpClient.ReadInputRegisters(5114, 2);
var dcBatteryValue4 = ModbusTcpClient.ReadInputRegisters(5121, 1);
var dcPrValDcDc = ModbusTcpClient.ReadInputRegisters(5124, 1);
var dcPrValDcDc2 = ModbusTcpClient.ReadInputRegisters(5127, 2);
var dcTempValue = ModbusTcpClient.ReadInputRegisters(5511, 1);
var dcWarningValues = ModbusTcpClient.ReadInputRegisters(2404, 20);
var dcAlarmValues = ModbusTcpClient.ReadInputRegisters(2811, 20);
var dcSetValues = ModbusTcpClient.ReadInputRegisters(4001, 1);
@ -133,37 +131,34 @@ public class TruConvertDcDevice
var dcCurrentLimitState = GetFlags(dcPrValDcDc.GetUInt16(5124)).ToArray();
var dcLinkVoltage = dcPrValDcDc2.GetUInt16(5128);
var dcPower = dcBatteryValue4.GetInt16(5121) * 1m;
var dcCurrent = dcLinkVoltage != 0m ? dcPower / dcLinkVoltage : 0m;
return new TruConvertDcStatus
{
// dcPrValMain
MainState = (MainState) dcPrValMain.GetInt16(5001),
NumberOfConnectedSlaves = dcPrValMain.GetUInt16(5002),
NumberOfConnectedSubSlaves = dcPrValMain.GetUInt16(5003),
(
// dcBatteryValue
BatteryVoltage = dcBatteryValue.GetUInt16(5101) * 0.1m,
BatteryCurrent = dcBatteryValue2.GetInt16(5111),
Dc: new DcConnection
(
dcLinkVoltage,
dcCurrent
),
TotalDcPower = dcBatteryValue3.GetInt32(5114) * 1m, // Resolution is 0.001 (kW) in Tru convert DC doc, but we want it in W
DcPower = dcBatteryValue4.GetInt16(5121) * 1m, // Resolution is 0.001 (kW) in Tru convert DC doc, but we want it in W
// dcPrValDcDc
StatusOfCurrentLimiting = dcCurrentLimitState,
// dcPrValDcDc2
OverloadCapacity = dcPrValDcDc2.GetUInt16(5127) * 0.1m,
DcLinkVoltage = dcPrValDcDc2.GetUInt16(5128),
//dcTempValue
DcDcInletTemperature = dcTempValue.GetInt16(5511),
Warnings = warnings,
Alarms = alarms,
// dcSetValues
PowerOperation = dcSetValues.GetBoolean(4001)
};
MainState : (MainState)dcPrValMain.GetInt16(5001),
NumberOfConnectedSlaves : dcPrValMain.GetUInt16(5002),
NumberOfConnectedSubSlaves : dcPrValMain.GetUInt16(5003),
BatteryVoltage : dcBatteryValue.GetUInt16(5101) * 0.1m,
BatteryCurrent : dcBatteryValue2.GetInt16(5111),
TotalDcPower : dcBatteryValue3.GetInt32(5114) * 1m, // Resolution is 0.001 (kW) in Tru convert DC doc, but we want it in W
StatusOfCurrentLimiting : dcCurrentLimitState,
OverloadCapacity : dcPrValDcDc2.GetUInt16(5127) * 0.1m,
DcDcInletTemperature : dcTempValue.GetInt16(5511),
Warnings : warnings,
Alarms : alarms,
PowerOperation : dcSetValues.GetBoolean(4001)
);
}
}

View File

@ -1,4 +1,6 @@
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Devices;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
@ -7,21 +9,19 @@ using WarningMessages = IReadOnlyList<WarningMessage>;
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
public record TruConvertDcStatus
{
public MainState MainState { get; init; }
public UInt16 NumberOfConnectedSlaves { get; init; }
public UInt16 NumberOfConnectedSubSlaves { get; init; }
public Decimal BatteryVoltage { get; init; }
public Decimal BatteryCurrent { get; init; }
public Decimal TotalDcPower { get; init; }
public Decimal DcPower { get; init; }
public DcCurrentLimitStates StatusOfCurrentLimiting { get; init; } = Array.Empty<DcCurrentLimitState>();
public Decimal OverloadCapacity { get; init; }
public Decimal DcLinkVoltage { get; init; }
public Decimal DcDcInletTemperature { get; init; }
public AlarmMessages Alarms { get; init; } = Array.Empty<AlarmMessage>();
public WarningMessages Warnings { get; init; } = Array.Empty<WarningMessage>();
public Boolean PowerOperation { get; init; }
}
(
DcConnection Dc,
MainState MainState,
UInt16 NumberOfConnectedSlaves,
UInt16 NumberOfConnectedSubSlaves,
Decimal BatteryVoltage,
Decimal BatteryCurrent,
Decimal TotalDcPower,
DcCurrentLimitStates StatusOfCurrentLimiting,
Decimal OverloadCapacity,
Decimal DcDcInletTemperature,
AlarmMessages Alarms,
WarningMessages Warnings,
Boolean PowerOperation
):DcDevice(Dc)
{}

View File

@ -1,9 +1,8 @@
using InnovEnergy.Lib.StatusApi.Phases;
namespace InnovEnergy.Lib.StatusApi.Connections;
public record DcConnection(Decimal Voltage, Decimal Current) : Phase(Voltage, Current)
{
public Decimal Power => Current * Voltage;
public Decimal Power => (Current * Voltage).Round3();
}

View File

@ -3,10 +3,10 @@ using InnovEnergy.Lib.StatusApi.Phases;
namespace InnovEnergy.Lib.StatusApi.Connections;
public record SinglePhaseAcConnection
(
Decimal Voltage,
Decimal Current,
Decimal Phi,
Decimal Frequency
)
: AcPhase(Voltage, Current, Phi);
(
Decimal Voltage,
Decimal Current,
Decimal Phi,
Decimal Frequency
);
//: AcPhase(Voltage, Current, Phi);

View File

@ -21,12 +21,12 @@ public record AcPhase
public static AcPhase FromActiveReactive
(
Decimal activePower,
Decimal reactivePower,
Decimal apparentPower,
Decimal voltage,
Decimal current
)
{
var apparentPower = Sqrt(activePower * activePower + reactivePower * reactivePower);
var reactivePower = Sqrt(Math.Abs(apparentPower * apparentPower - activePower * activePower ));
var phi = ATan2(reactivePower, activePower);
return new AcPhase
@ -39,11 +39,10 @@ public record AcPhase
ReactivePower: reactivePower,
PowerFactor: Cos(phi)
);
}
public static AcPhase FromVoltageCurrentPhi(Decimal voltage, Decimal current, Decimal phi)
public static AcPhase CreateInstance(Decimal voltage, Decimal current, Decimal phi)
{
var powerFactor = Cos(phi);

View File

@ -0,0 +1,11 @@
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
public static class Utils
{
public static Decimal Round3(this Decimal d)
{
return d.RoundToSignificantFigures(3);
}
}

View File

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.3.1" />
<PackageReference Include="CliWrap" Version="3.6.0" />
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2" />
<PackageReference Include="System.Reactive.Linq" Version="5.0.0" />
</ItemGroup>