This commit is contained in:
Sina Blattmann 2023-04-06 08:05:52 +02:00
commit d90547ecdb
85 changed files with 1693 additions and 791 deletions

View File

@ -156,8 +156,8 @@ public static class SessionMethods
.WithParent(sessionUser)
.Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password))
.Do(() => newUser.MustResetPassword = true)
.Apply(Db.Create)
&& Mailer.Mailer.SendVerificationMessage(newUser);
.Apply(Db.Create);
// && Mailer.Mailer.SendVerificationMessage(newUser);
//Send Email to new user to verify email and set password

View File

@ -182,9 +182,9 @@ public static class UserMethods
{
return other?.Type switch
{
"installation" => user.HasAccessTo(Db.GetInstallationById(other.ParentId)),
"user" => user.HasAccessTo(Db.GetUserById(other.ParentId)),
"folder" => user.HasAccessTo(Db.GetFolderById(other.ParentId)),
"Installation" => user.HasAccessTo(Db.GetFolderById(other.ParentId)),
"User" => user.HasAccessTo(Db.GetUserById(other.ParentId)),
"Folder" => user.HasAccessTo(Db.GetFolderById(other.ParentId)),
_ => false
};
}

View File

@ -16,20 +16,21 @@ public static partial class Db
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
public static TableQuery<Session> Sessions => Connection.Table<Session>();
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
public static TableQuery<DeletedFolder> DeletedFolders => Connection.Table<DeletedFolder>();
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
public static TableQuery<DeletedInstallation> DeletedInstallations => Connection.Table<DeletedInstallation>();
public static TableQuery<User> Users => Connection.Table<User>();
public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>();
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
public static TableQuery<Session> Sessions => Connection.Table<Session>();
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
public static TableQuery<User> Users => Connection.Table<User>();
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
public static TableQuery<DeletedInstallation> DeletedInstallations => Connection.Table<DeletedInstallation>();
public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>();
public static TableQuery<DeletedFolder> DeletedFolders => Connection.Table<DeletedFolder>();
public static void Init()
{
// used to force static constructor
}
@ -54,6 +55,7 @@ public static partial class Db
Observable.Interval(TimeSpan.FromDays(0.5))
.StartWith(0) // Do it right away (on startup)
.ObserveOn(TaskPoolScheduler.Default)
.SubscribeOn(TaskPoolScheduler.Default)
.SelectMany(Cleanup)
.Subscribe();
}

View File

@ -1,4 +1,5 @@
#!/bin/bash
## create a new VRM access token
## USAGE: ./newToken.sh -u Username -p Password -n UniqueTokenName

View File

@ -2,23 +2,23 @@
<Import Project="../InnovEnergy.App.props" />
<ItemGroup>
<ProjectReference Include="../../Lib/Devices/Adam6060/Adam6060.csproj"/>
<ProjectReference Include="../../Lib/Devices/AMPT/Ampt.csproj"/>
<ProjectReference Include="../../Lib/Devices/Battery48TL/Battery48TL.csproj"/>
<ProjectReference Include="../../Lib/Devices/EmuMeter/EmuMeter.csproj"/>
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj"/>
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj"/>
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvert/TruConvert.csproj"/>
<ProjectReference Include="../../Lib/StatusApi/StatusApi.csproj"/>
<ProjectReference Include="../../Lib/Utils/Utils.csproj"/>
<ProjectReference Include="../../Lib/Time/Time.csproj"/>
<ProjectReference Include="../../Lib/Devices/Adam6060/Adam6060.csproj" />
<ProjectReference Include="../../Lib/Devices/AMPT/Ampt.csproj" />
<ProjectReference Include="../../Lib/Devices/Battery48TL/Battery48TL.csproj" />
<ProjectReference Include="../../Lib/Devices/EmuMeter/EmuMeter.csproj" />
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertAc/TruConvertAc.csproj" />
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvertDc/TruConvertDc.csproj" />
<ProjectReference Include="../../Lib/Devices/Trumpf/TruConvert/TruConvert.csproj" />
<ProjectReference Include="../../Lib/StatusApi/StatusApi.csproj" />
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
<ProjectReference Include="../../Lib/Time/Time.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.0"/>
<PackageReference Include="Flurl.Http" Version="3.2.4"/>
<PackageReference Include="System.IO.Ports" Version="7.0.0"/>
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2"/>
<PackageReference Include="CliWrap" Version="3.6.0" />
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
<PackageReference Include="DecimalMath.DecimalEx" Version="1.0.2" />
</ItemGroup>

View File

@ -1,51 +1,53 @@
using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.StatusApi;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Composite;
using static InnovEnergy.Lib.Devices.Battery48TL.TemperatureState;
namespace InnovEnergy.App.SaliMax.Controller;
public class AvgBatteriesStatus
public static class AvgBatteriesStatus
{
public Decimal Soc { get; set; }
public Decimal Current { get; set; }
public Decimal Voltage { get; set; }
public Decimal Power { get; set; }
public Decimal BusVoltage { get; set; }
public Decimal BatteryTemperature { get; set; }
public IReadOnlyList<String> Warnings { get; set; }
public IReadOnlyList<String> Alarms { get; set; }
public Boolean HeaterOn { get; set; }
public Boolean EocReached { get; set; }
public Boolean BatteryCold { get; set; }
public Decimal MaxChargingPower { get; set; }
public Decimal MaxDischargingPower { get; set; }
public static AvgBatteriesStatus ReadBatteriesStatus(IReadOnlyList<Battery48TLStatus> batteriesStatus)
public static CombinedStatus<Battery48TLStatus>? Combine(this IReadOnlyList<Battery48TLStatus> stati)
{
var soc = batteriesStatus.Any() ? batteriesStatus.Average(b => b.Soc) : 0;
var current = batteriesStatus.Select(b => b.Dc.Current).Aggregate(0m,(a, b) => a + b);
var voltage = batteriesStatus.Any() ? batteriesStatus.Average(b => b.Dc.Voltage) : 0;
var power = batteriesStatus.Select(b => b.Dc.Power).Aggregate(0m,(a, b) => a + b);
var busVoltage = batteriesStatus.Any() ? batteriesStatus.Average(b => b.BusVoltage): 0;
var batteryTemperature = batteriesStatus.Any() ? batteriesStatus.Average(b => b.Temperature): 0;
var heaterOn = batteriesStatus.Any() && batteriesStatus.Select(b => b.HeaterOn).Aggregate((a, b) => a | b);
var eocReached = batteriesStatus.All(b => b.EocReached);
var batteryCold = batteriesStatus.Any(b => b.BatteryCold);
var maxChargingPower = batteriesStatus.Select(b => b.MaxChargingPower).Aggregate(0m, (a, b) => a + b);
var maxDischargingPower = batteriesStatus.Select(b => b.MaxDischargingPower).Aggregate(0m, (a, b) => a + b);
return new AvgBatteriesStatus
var combined = stati.Count == 0
? null
: new Battery48TLStatus
{
Soc = soc,
Current = current,
Voltage = voltage,
Power = power,
BusVoltage = busVoltage,
BatteryTemperature = batteryTemperature,
HeaterOn = heaterOn,
EocReached = eocReached,
BatteryCold = batteryCold,
MaxChargingPower = maxChargingPower,
MaxDischargingPower = maxDischargingPower
Soc = stati.Min(b => b.Soc),
Temperature = stati.Average(b => b.Temperature),
Dc = new DcBus
{
Voltage = stati.Average(b => b.Dc.Voltage),
Current = stati.Sum(b => b.Dc.Current),
},
Alarms = stati.SelectMany(b => b.Alarms).Distinct().ToList(),
Warnings = stati.SelectMany(b => b.Warnings).Distinct().ToList(),
MaxChargingPower = stati.Sum(b => b.MaxChargingPower),
MaxDischargingPower = stati.Sum(b => b.MaxDischargingPower),
Heating = stati.Any(b => b.Heating),
AmberLed = LedState.Off, // not used for combined battery
BlueLed = LedState.Off,
RedLed = LedState.Off,
GreenLed = LedState.Off,
CellsVoltage = stati.Average(b => b.CellsVoltage),
ConnectedToDc = stati.Any(b => b.ConnectedToDc),
TemperatureState = stati.Any(b => b.TemperatureState == OperatingTemperature) // TODO: revisit when we have the overheated state
? OperatingTemperature
: Cold,
};
return new CombinedStatus<Battery48TLStatus>
{
Combined = combined!,
Children = stati
};
}
}

View File

@ -5,18 +5,19 @@ using InnovEnergy.Lib.Devices.Battery48TL;
using InnovEnergy.Lib.Devices.EmuMeter;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using InnovEnergy.Lib.StatusApi;
namespace InnovEnergy.App.SaliMax.Controller;
public record StatusRecord
{
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!;
public TruConvertAcStatus? InverterStatus { get; init; }
public TruConvertDcStatus? DcDcStatus { get; init; }
public CombinedStatus<Battery48TLStatus>? BatteriesStatus { 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

@ -13,6 +13,7 @@ using InnovEnergy.Lib.Devices.EmuMeter;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Utils;
#pragma warning disable IL2026
@ -41,14 +42,26 @@ internal static class Program
{
Console.WriteLine("Starting SaliMax");
var batteryNodes = new Byte[] { 2, 3 };
var batteryTty = "/dev/ttyUSB0";
var relaysIp = "10.0.1.1";
var truConvertAcIp = "10.0.2.1";
var truConvertDcIp = "10.0.3.1";
var gridMeterIp = "10.0.4.1";
var internalMeter = "10.0.4.2";
var amptIp = "10.0.5.1";
var s3Config = new S3Config
{
Bucket = "saliomameiringen",
Region = "sos-ch-dk-2",
Provider = "exo.io",
Bucket = "saliomameiringen",
Region = "sos-ch-dk-2",
Provider = "exo.io",
ContentType = "text/plain; charset=utf-8",
Key = "EXO2bf0cbd97fbfa75aa36ed46f",
Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs"
Key = "EXO2bf0cbd97fbfa75aa36ed46f",
Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs"
};
#if DEBUG
@ -62,48 +75,42 @@ internal static class Program
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 batteries = batteryNodes.Select(n => new Battery48TlDevice(batteryTty, n)).ToList();
var inverterDevice = new TruConvertAcDevice(truConvertAcIp);
var dcDcDevice = new TruConvertDcDevice(truConvertDcIp);
var gridMeterDevice = new EmuMeterDevice(gridMeterIp);
var acInToAcOutMeterDevice = new EmuMeterDevice(internalMeter); // TODO: use real device
var amptDevice = new AmptCommunicationUnit(amptIp);
var saliMaxRelaysDevice = new SaliMaxRelaysDevice(relaysIp);
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 combinedBatteryStatus = batteries
.Select(b => b.ReadStatus())
.NotNull()
.ToList()
.Combine();
var battery48TlStatusArray = battery48TlDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
#endif
// var dcDcStatusArray = dcDcDevices.Select(b => b.ReadStatus()).NotNull().ToArray();
// 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(),
#if BatteriesAllowed
BatteriesStatus = battery48TlStatusArray,
AvgBatteriesStatus = AvgBatteriesStatus.ReadBatteriesStatus(battery48TlStatusArray),
#else
BatteriesStatus = null,
AvgBatteriesStatus = null,
#endif
BatteriesStatus = combinedBatteryStatus,
AcInToAcOutMeterStatus = acInToAcOutMeterDevice.ReadStatus(),
GridMeterStatus = gridMeterDevice.ReadStatus(),
SaliMaxRelayStatus = saliMaxRelaysDevice.ReadStatus(),

View File

@ -0,0 +1,43 @@
#!/bin/bash
host=ie-entwicklung@10.2.3.115
tunnel() {
name=$1
ip=$2
rPort=$3
lPort=$4
echo -n "localhost:$lPort $name "
ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null &
until nc -vz 127.0.0.1 $lPort 2> /dev/null
do
echo -n .
sleep 0.3
done
echo "ok"
}
echo ""
tunnel "Trumpf Inverter (http) " 10.0.2.1 80 8001
tunnel "Trumpf DCDC (http) " 10.0.3.1 80 8002
tunnel "Ext Emu Meter (http) " 10.0.4.1 80 8003
tunnel "Int Emu Meter (http) " 10.0.4.2 80 8004
tunnel "AMPT (http) " 10.0.5.1 8080 8005
tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001
tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002
tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003
tunnel "Int Emu Meter " 10.0.4.2 502 5004
tunnel "AMPT (modbus) " 10.0.5.1 502 5005
echo
echo "press any key to close the tunnels ..."
read -r -n 1 -s
kill $(jobs -p)
echo "done"

View File

@ -9,8 +9,12 @@
<TargetFramework>net6.0</TargetFramework>
<InvariantGlobalization>true</InvariantGlobalization>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
<RootNamespace>$(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("src/", "").Replace("/",".").Replace("\","."))</RootNamespace>
<RootNamespace>Please.reload.the.project.Rider.is.stupid</RootNamespace>
<Authors>$(Company) Team</Authors>
</PropertyGroup>
<PropertyGroup Condition="'$(SolutionDir)' != ''">
<RootNamespace>$(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("/",".").Replace("\","."))</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -60,20 +60,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Channels", "Lib/Channels/Ch
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend", "App/Backend/Backend.csproj", "{A56F58C2-B265-435B-A985-53B4D6F49B1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Units", "Lib/Units/Units.csproj", "{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Units", "Lib\Units\Units.csproj", "{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemControl", "Lib/Devices/Trumpf/SystemControl/SystemControl.csproj", "{B816BB44-E97E-4E02-B80A-BEDB5B923A96}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Meta", "Meta", "{AED84693-C389-44C9-B2C0-ACB560189CF2}"
ProjectSection(SolutionItems) = preProject
InnovEnergy.props = InnovEnergy.props
App\InnovEnergy.App.props = App\InnovEnergy.App.props
Lib\InnovEnergy.Lib.props = Lib\InnovEnergy.Lib.props
App/InnovEnergy.App.props = App/InnovEnergy.App.props
Lib/InnovEnergy.Lib.props = Lib/InnovEnergy.Lib.props
InnovEnergy.sln.DotSettings = InnovEnergy.sln.DotSettings
..\.gitignore = ..\.gitignore
../.gitignore = ../.gitignore
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -180,6 +180,10 @@ Global
{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7}.Release|Any CPU.Build.0 = Release|Any CPU
{B816BB44-E97E-4E02-B80A-BEDB5B923A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B816BB44-E97E-4E02-B80A-BEDB5B923A96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B816BB44-E97E-4E02-B80A-BEDB5B923A96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B816BB44-E97E-4E02-B80A-BEDB5B923A96}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CF4834CB-91B7-4172-AC13-ECDA8613CD17} = {145597B4-3E30-45E6-9F72-4DD43194539A}
@ -211,5 +215,6 @@ Global
{A56F58C2-B265-435B-A985-53B4D6F49B1A} = {145597B4-3E30-45E6-9F72-4DD43194539A}
{C04FB6DA-23C6-46BB-9B21-8F4FBA32FFF7} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{4A67D79F-F0C9-4BBC-9601-D5948E6C05D3} = {AD5B98A8-AB7F-4DA2-B66D-5B4E63E7D854}
{B816BB44-E97E-4E02-B80A-BEDB5B923A96} = {DDDBEFD0-5DEA-4C7C-A9F2-FDB4636CF092}
EndGlobalSection
EndGlobal

View File

@ -28,6 +28,8 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=MPPT/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=mppts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=nanopi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precharge/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precharges/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prosumer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Prosumers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=proxyport/@EntryIndexedValue">True</s:Boolean>
@ -38,6 +40,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Trumpf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ttyusb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tupled/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=unhashed/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=victronenergy/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View File

@ -1,5 +1,6 @@
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using static InnovEnergy.Lib.Devices.Battery48TL.Constants;
namespace InnovEnergy.Lib.Devices.Battery48TL;
@ -10,10 +11,10 @@ public class Battery48TlDevice
public Battery48TlDevice(String device, Byte nodeId)
{
var serialConnection = new ModbusSerialConnection(device,
Constants.BaudRate,
Constants.Parity,
Constants.DataBits,
Constants.StopBits,
BaudRate,
Parity,
DataBits,
StopBits,
Constants.Timeout);
Modbus = new ModbusRtuClient(serialConnection, nodeId);
@ -34,7 +35,7 @@ public class Battery48TlDevice
try
{
return Modbus
.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters)
.ReadInputRegisters(BaseAddress, NoOfRegisters)
.ParseBatteryStatus();
}
catch (Exception e)

View File

@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.StatusApi;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.Devices.Battery48TL;
@ -15,22 +14,17 @@ public record Battery48TLStatus : BatteryStatus
public Power MaxChargingPower { get; init; }
public Power MaxDischargingPower { get; init; }
public State GreenLed { get; init; }
public State AmberLed { get; init; }
public State BlueLed { get; init; }
public State RedLed { get; init; }
public LedState GreenLed { get; init; }
public LedState AmberLed { get; init; }
public LedState BlueLed { get; init; }
public LedState RedLed { get; init; }
public State Warnings { get; init; }
public State Alarms { get; init; }
public IReadOnlyList<String> Warnings { get; init; } = Array.Empty<String>();
public IReadOnlyList<String> Alarms { get; init; } = Array.Empty<String>();
public State MainSwitchState { get; init; } // connected to bus | disconnected from bus
public State HeaterState { get; init; } // heating | not heating
public State EocState { get; init; } // EOC reached | EOC not reached
public State TemperatureState { get; init; } // cold | operating temperature | overheated
public static T operator |(T left, T right) => OpParallel(left, right);
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public Boolean ConnectedToDc { get; init; }
public Boolean Heating { get; init; }
public TemperatureState TemperatureState { get; init; } // cold | operating temperature | overheated

View File

@ -24,7 +24,7 @@ public static class Constants
private const Decimal RStringMin = 0.125m;
private const Decimal RStringMax = 0.250m;
private const Decimal IMaxPerString = 20.0m;
private const UInt16 NumberOfStrings = 5;
private const UInt16 NumberOfStrings = 5;
public const Decimal RIntMin = RStringMin / NumberOfStrings;
public const Decimal RIntMax = RStringMax / NumberOfStrings;

View File

@ -1,8 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Battery48TL.LedState;
namespace InnovEnergy.Lib.Devices.Battery48TL;
@ -10,24 +10,44 @@ public static class ModbusParser
{
internal static Battery48TLStatus ParseBatteryStatus(this ModbusRegisters data)
{
var greenLed = data.ParseLedState(register: 1005, led: LedColor.Green);
var amberLed = data.ParseLedState(register: 1005, led: LedColor.Amber);
var blueLed = data.ParseLedState(register: 1005, led: LedColor.Blue);
var redLed = data.ParseLedState(register: 1005, led: LedColor.Red);
var soc = data.ParseSoc();
var eoc = greenLed is On
&& amberLed is Off
&& blueLed is Off;
var maxSoc = eoc ? 100m : 99.9m;
var batteryCold = greenLed >= BlinkingSlow
&& blueLed >= BlinkingSlow;
var temperatureState = batteryCold
? TemperatureState.Cold
: TemperatureState.OperatingTemperature; // TODO: overheated
return new Battery48TLStatus
{
Dc = data.ParseDcBus(),
Alarms = data.ParseAlarms().ToList(),
Warnings = data.ParseWarnings().ToList(),
Soc = data.ParseSoc(),
Temperature = data.ParseTemperature(),
GreenLed = data.ParseGreenLed(),
AmberLed = data.ParseAmberLed(),
BlueLed = data.ParseBlueLed(),
RedLed = data.ParseRedLed(),
MainSwitchState = data.ParseMainSwitchState(),
HeaterState = data.ParseHeaterState(),
EocState = data.ParseEocState(),
TemperatureState = data.ParseTemperatureState(),
Soc = Math.Min(soc, maxSoc),
Temperature = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400),
GreenLed = greenLed,
AmberLed = amberLed,
BlueLed = blueLed,
RedLed = redLed,
Heating = data.ParseBool(baseRegister: 1014, bit: 6),
ConnectedToDc = data.ParseBool(baseRegister: 1014, bit: 0),
TemperatureState = temperatureState,
MaxChargingPower = data.CalcMaxChargePower(),
MaxDischargingPower = data.CalcMaxDischargePower(),
CellsVoltage = data.ParseCellsVoltage(),
CellsVoltage = data.ParseDecimal(register: 1000, scaleFactor: 0.01m),
};
}
@ -47,11 +67,6 @@ public static class ModbusParser
return data.ParseDecimal(register: 1001, scaleFactor: 0.01m, offset: -10000);
}
internal static Decimal ParseCellsVoltage(this ModbusRegisters data)
{
return data.ParseDecimal(register: 1000, scaleFactor: 0.01m);
}
internal static Decimal ParseBusVoltage(this ModbusRegisters data)
{
return data.ParseDecimal(register: 1002, scaleFactor: 0.01m);
@ -74,57 +89,19 @@ public static class ModbusParser
return (hi, lo) switch
{
(false, false) => LedState.Off,
(false, true) => LedState.On,
(true, false) => LedState.BlinkingSlow,
(true, true) => LedState.BlinkingFast,
(false, false) => Off,
(false, true) => On,
(true, false) => BlinkingSlow,
(true, true) => BlinkingFast,
};
}
private static Boolean ParseEocReached(this ModbusRegisters data)
{
return ParseLedState(data, 1005, LedColor.Green) == LedState.On &&
ParseLedState(data, 1005, LedColor.Amber) == LedState.Off &&
ParseLedState(data, 1005, LedColor.Blue) == LedState.Off;
}
internal static State ParseTemperatureState(this ModbusRegisters data)
{
return data.ParseBatteryCold() ? "cold" : "operating temperature"; // TODO: overheated,
}
internal static Decimal ParseTemperature(this ModbusRegisters data)
{
return data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
}
internal static Decimal ParseSoc(this ModbusRegisters data)
{
return data.ParseDecimal(register: 1054, scaleFactor: 0.1m);
}
internal static State ParseEocState(this ModbusRegisters data)
{
return data.ParseEocReached() ? "EOC reached" : "EOC not reached";
}
internal static State ParseHeaterState(this ModbusRegisters data)
{
return data.ParseBool(baseRegister: 1014, bit: 6) ? "heating" : "not heating";
}
internal static State ParseMainSwitchState(this ModbusRegisters data)
{
return data.ParseBool(baseRegister: 1014, bit: 0) ? "connected to bus" : "disconnected from bus";
}
internal static Boolean ParseBatteryCold(this ModbusRegisters data)
{
return ParseLedState(data, 1005, LedColor.Green) >= LedState.BlinkingSlow &&
ParseLedState(data, 1005, LedColor.Blue) >= LedState.BlinkingSlow;
}
private static Decimal CalcPowerLimitImposedByVoltageLimit(Decimal v,Decimal i,Decimal vLimit,Decimal rInt)
{
var dv = vLimit - v;
@ -161,11 +138,12 @@ public static class ModbusParser
internal static Decimal CalcMaxChargePower(this ModbusRegisters data)
{
var v = ParseCellsVoltage(data);
var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m);
var i = ParseCurrent(data);
var pLimits = new[]
{
// TODO: review
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin),
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax),
CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin),
@ -177,23 +155,20 @@ public static class ModbusParser
return Math.Max(pLimit, 0);
}
internal static DcBus ParseDcBus(this ModbusRegisters data)
internal static DcBus ParseDcBus(this ModbusRegisters data) => new()
{
return new()
{
Current = data.ParseCurrent(),
Voltage = data.ParseBusVoltage(),
};
}
Current = data.ParseCurrent(),
Voltage = data.ParseBusVoltage(),
};
internal static Decimal CalcMaxDischargePower(this ModbusRegisters data)
{
var v = ParseCellsVoltage(data);
var v = data.ParseDecimal(register: 1000, scaleFactor: 0.01m);
var i = ParseCurrent(data);
var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
var pLimits = new[]
{
// TODO: review
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin),
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax),
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin),
@ -207,12 +182,6 @@ public static class ModbusParser
}
internal static LedState ParseGreenLed(this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Green);
internal static LedState ParseAmberLed(this ModbusRegisters data) => data.ParseLedState(register: 1006, led: LedColor.Amber);
internal static LedState ParseBlueLed (this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Blue);
internal static LedState ParseRedLed (this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Red);
[SuppressMessage("ReSharper", "StringLiteralTypo")]
internal static IEnumerable<String> ParseAlarms(this ModbusRegisters data)
{

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Battery48TL;
public enum TemperatureState
{
Cold = 0,
OperatingTemperature = 1,
Overheated = 2,
}

View File

@ -0,0 +1,18 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public enum AlarmMessage
{
NoAlarm = 0,
BmsCommunicationTimeoutHasOccured = 40302, // BMS communication timeout has occured.
Rs485CommunicationAlarm = 40303, // RS-485 communication alarm.
SoftwareVersionsOfSystemControlAndModulesDoNotMatch1 = 40412, // Software versions of system control and module(s) do not match.
SoftwareVersionsOfSystemControlAndModulesDoNotMatch2 = 40413, // Software versions of system control and module(s) do not match.
SoftwareVersionsOfSystemControlAndModulesDoNotMatch3 = 40414, // Software versions of system control and module(s) do not match.
SoftwareVersionsOfSystemControlAndModulesDoNotMatch4 = 40415, // Software versions of system control and module(s) do not match.
SoftwareVersionsOfSystemControlAndModulesDoNotMatch5 = 40416, // Software versions of system control and module(s) do not match.
NoSlaveModuleWasFoundPleaseCheckRs485Connection = 40304, // No slave module was found, please check RS-485 connection(s).
NumberOfOrCombinationOfConnectedSlaveTypesNotSupported = 40305, // Number of or combination of connected slave types not supported.
}

View File

@ -0,0 +1,84 @@
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
public record ControlRecord
{
private static readonly TimeSpan DefaultCommunicationTimeOut = TimeSpan.FromSeconds(10);
// TODO
// public UInt32 Date { get; init;}
// public UInt32 Time { get; init;}
// public UInt32 IpAddress { get; init;} //= 0x C0A80102;
// public UInt32 Subnet { get; init;} //= 0x FFFFFF00;
// public UInt32 Gateway { get; init;} //= 0x C0A80102;
// public Boolean ResetParamToDefault { get; init;} = false ; // Coil
// public TimeSpan CommunicationTimeout { get; init;} = DefaultCommunicationTimeOut;
// public Boolean FactoryResetParameters { get; init;} = false;
// public SystemConfig ConnectedSystemConfig { get; init;} = 0;
// public UInt16 UpdateSwTrigger { get; init;} = 0;
// public UInt16 AutomaticSwUpdate { get; init;} = 0;
// public UInt16 CustomerValuesSaveReset { get; init;} = 0;
// public UInt16 SerialNumberSystemControl { get; init;} = 0;
// public UInt16 SerialNumberAcDc { get; init;} = 0;
// public UInt16 IntegrationLevel { get; init;} = 0;
// public UInt16 IlBuildnumber { get; init;} = 0;
// public Boolean PowerStageEnable { get; init;} = true;
// public SymmetricAcOperationMode SetValueConfig { get; init;} = 0;
// public Boolean ResetsAlarmAndWarning { get; init;} = false;
// public PreChargeDcLinkConfig PreChargeDcLinkConfig { get; init;} = (PreChargeDcLinkConfig)0;
// public PowerFactorConvention PowerFactorConvention { get; init;} = 0; //0 = producer
// public UInt16 SlaveAddress { get; init;} = Slave.Broadcast;
// public ErrorPolicy ErrorHandlingPolicy { get; init;} = 0;
// public AcDcGridType GridType { get; init;} = 0;
// public UInt16 SubSlaveAddress { get; init;} = 0;
// 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,
// public Decimal SignedPowerSetValueL2 { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal SignedPowerSetValueL3 { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal PowerSetValue { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal PowerSetValueL1 { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal PowerSetValueL2 { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal PowerSetValueL3 { get; init;} = 0; // resolution 0.001 and Unit kva,
// public Decimal MaximumGridCurrentRmsL1 { get; init;} = 0; // resolution : 0.01
// public Decimal MaximumGridCurrentRmsL2 { get; init;} = 0; // resolution : 0.01
// public Decimal MaximumGridCurrentRmsL3 { get; init;} = 0; // resolution : 0.01
// public Decimal CosPhiSetValueL1 { get; init;} = 0; // resolution : 0.01
// public Decimal CosPhiSetValueL2 { get; init;} = 0; // resolution : 0.01
// public Decimal CosPhiSetValueL3 { get; init;} = 0; // resolution : 0.01
// public Boolean PhaseL1IsCapacitive { get; init;} = true; // True = Capacitive, false = Inductive
// public Boolean PhaseL2IsCapacitive { get; init;} = true; // True = Capacitive, false = Inductive
// public Boolean PhaseL3IsCapacitive { get; init;} = true; // True = Capacitive, false = Inductive
// public Boolean PhasesAreCapacitive { get; init;} = true; // True = Capacitive, false = Inductive
// public Double SetPointCosPhi { get; init;} = 0; // resolution 0.01
// public Double SetPointSinPhi { get; init;} = 0; // resolution 0.01
// public Double SetPointSinPhiL1 { get; init;} = 0; // resolution 0.01
// public Double SetPointSinPhiL2 { get; init;} = 0; // resolution 0.01
// public Double SetPointSinPhiL3 { get; init;} = 0; // resolution 0.01
// public Decimal FrequencyOffsetIm { get; init;} = 0; // resolution 0.01
// public UInt16 VoltageAdjustmentFactorIm { get; init;} = 0;
// public UInt16 PreChargeDcLinkVoltage { get; init;} = 0;
// public Decimal MaxPeakCurrentVoltageControlL1 { get; init;} = 0; // resolution 0.01
// 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;} = 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;
// // Need to discuss this with Ivo
// // public UInt16 FrequencySlopeIslandMode { get; init;} = 200; // resolution 0.01
// // public UInt16 VoltageSlopeIslandMode { get; init;} = 500; // resolution 0.01
// public UInt16 AcDcGcBypassMode { get; init;} = 0;
// public UInt16 AcDcGcPMaxThresholdPercent { get; init;} = 0; // resolution 0.01
// public UInt16 AcDcGcStartupRampEnable { get; init;} = 0;
// public DcStageConfiguration DcConfigModule { get; init;} = 0; // this must be an enum
// public UInt16 DcDcPowerDistribution { get; init;} = 0; // 0.1 resolution
// public AcDcDistributionMode AcDcDistributionMode { get; init;} = 0;
}

View File

@ -0,0 +1,7 @@
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
public enum ErrorPolicy : UInt16
{
Relaxed = 0, // 0 = relaxed (System keeps running even if some slaves are in error state.)
Strict = 1, // 1 = strict (System shuts down as soon as one component is in error state.)
}

View File

@ -0,0 +1,43 @@
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
using AlarmMessages = IReadOnlyList<AlarmMessage>;
using WarningMessages = IReadOnlyList<WarningMessage>;
// ReSharper disable UnusedAutoPropertyAccessor.Global
#pragma warning disable CS8618
public record StatusRecord
{
// TODO
// public MainState MainState { get; init; }
// public String SerialNumber { get; init; }
// public AcDcGridType GridType { get; init; }
// public WarningMessages Warnings { get; init; }
// public AlarmMessages Alarms { get; init; }
// public Decimal NumberOfConnectedSlaves { get; init; }
// public Decimal NumberOfConnectedSubSlaves { get; init; }
// public Frequency AcDcNominalGridFrequency { get; init; }
// public Voltage AcDcNominalGridVoltage { get; init; }
// public Power AcDcActNominalPower { get; init; }
// public Decimal AcDcPowerLimitingStatusAct { get; init; } // TODO: enum
// public Voltage AcDcDcVoltageReference { get; init; }
// public Voltage AcDcDcLinkVoltageMinAct { get; init; }
// public Voltage AcDcDcLinkVoltageMaxAct { get; init; }
// public Voltage AcDcDcLinkChargedMinVoltage { get; init; }
// public Decimal AcDcStmActCustomer { get; init; }
// public Decimal AcDcOverloadIntegratorStatusL1 { get; init; }
// public Decimal AcDcOverloadIntegratorStatusL2 { get; init; }
// public Decimal AcDcOverloadIntegratorStatusL3 { get; init; }
// public Power AcSignedPowerValue { get; init; }
// public Voltage ActualDcLinkVoltageUpperHalf { get; init; }
// public Voltage ActualDcLinkVoltageLowerHalf { get; init; }
// public Voltage ActualDcLinkVoltageUpperHalfExt { get; init; }
// public Voltage ActualDcLinkVoltageLowerHalfExt { get; init; }
// public Voltage VoltageIntNtoPe { get; init; }
// public Voltage VoltageExtNtoPe { get; init; }
// public Temperature InletAirTemperature { get; init; }
}

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
public enum SubSlavesErrorPolicy : UInt16
{
Strict = 0, // (AC-DC module switches to error state if at least one submodules is in error state
Relaxed = 1, // (AC-DC module switches to error state if all submodules are in error state.)
Off = 2, // (If possible AC-DC module continues operation even if all submodules are in error state.
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../../InnovEnergy.Lib.props" />
<ItemGroup>
<ProjectReference Include="..\..\..\Protocols\Modbus\Modbus.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,324 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.SystemControlRegisters;
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class SystemControlDevice
{
private ModbusTcpClient ModbusTcpClient { get; }
public SystemControlDevice(String hostname, UInt16 port = ModbusTcpClient.DefaultPort, Byte slaveAddress = 0)
{
var connection = new ModbusTcpConnection(hostname, port);
ModbusTcpClient = new ModbusTcpClient(connection, slaveAddress);
}
public void WriteControl(ControlRecord c)
{
/*
WriteRegs(AcControlRegisters.Date, new List<UInt16> { c.Date.ConvertTo<UInt16>()});
WriteRegs(AcControlRegisters.Time, new List<UInt16> { c.Time.ConvertTo<UInt16>()});
WriteRegs(AcControlRegisters.IpAddress, new List<UInt16> { c.IpAddress.ConvertTo<UInt16>()});
WriteRegs(AcControlRegisters.Subnet, new List<UInt16> { c.Subnet.ConvertTo<UInt16>()});
WriteRegs(AcControlRegisters.Gateway, new List<UInt16> { c.Gateway.ConvertTo<UInt16>()});
WriteCoils(AcControlRegisters.ResetParamToDefault, c.ResetParamToDefault);
WriteCoils(AcControlRegisters.FactoryResetParameters, c.FactoryResetParameters);
ModbusTcpClient.WriteRegisters(AcControlRegisters.UpdateSwTrigger, c.UpdateSwTrigger, c.AutomaticSwUpdate, c.CustomerValuesSaveReset);
*/
WriteRegs(CommunicationTimeout, c.CommunicationTimeout.TotalSeconds.ConvertTo<UInt16>());
WriteRegs(ConnectedSystemConfig, c.ConnectedSystemConfig);
WriteCoils(PowerStageConfig, c.PowerStageEnable,
c.SetValueConfig.ConvertTo<Boolean>(),
c.ResetsAlarmAndWarning);
WriteRegs(PreChargeDcLinkConfigR, c.PreChargeDcLinkConfig,
c.PowerFactorConvention, c.SlaveAddress,
c.ErrorHandlingPolicy,
c.GridType, c.SubSlaveAddress);
WriteCoils(ModbusSlaveId, c.UseModbusSlaveIdForAddressing);
WriteRegs(SubSlaveErrorPolicy, c.SubSlaveErrorPolicy);
WriteRegs(SignedPowerNominalValue, -1.0m, c.SignedPowerNominalValue);/*, c.SignedPowerSetValueL1,
c.SignedPowerSetValueL2, c.SignedPowerSetValueL3,
c.PowerSetValue, c.PowerSetValueL1,
c.PowerSetValueL2, c.PowerSetValuesL3);*/
WriteRegs(MaximumGridCurrentRmsL1, 0.01m, c.MaximumGridCurrentRmsL1, c.MaximumGridCurrentRmsL2,
c.MaximumGridCurrentRmsL3, c.CosPhiSetValueL1,
c.CosPhiSetValueL2, c.CosPhiSetValueL3);
WriteCoils(PhaseL1IsCapacitive, c.PhaseL1IsCapacitive,
c.PhaseL2IsCapacitive,
c.PhaseL3IsCapacitive,
c.PhasesAreCapacitive);
/* WriteRegs(SetPointCosPhi, 0.01m, c.SetPointCosPhi.ConvertTo<UInt16>(),
c.SetPointSinPhi.ConvertTo<UInt16>(),
c.SetPointSinPhiL1.ConvertTo<UInt16>(),
c.SetPointSinPhiL2.ConvertTo<UInt16>(),
c.SetPointSinPhiL3.ConvertTo<UInt16>(),
c.FrequencyOffsetIm);*/
WriteRegs(VoltageAdjustmentFactorIm, c.VoltageAdjustmentFactorIm);
WriteRegs(PreChargeDcLinkVoltage, c.PreChargeDcLinkVoltage);
WriteRegs(MaxPeakCurrentVoltageControlL1, 0.01m, c.MaxPeakCurrentVoltageControlL1,
c.MaxPeakCurrentVoltageControlL2,
c.MaxPeakCurrentVoltageControlL3);
WriteRegs(GridFormingMode, c.GridFormingMode, c.DcLinkRefVoltage,
c.DcLinkMinVoltage, c.DcLinkMaxVoltage,
c.DcVoltageRefUs, c.DcMinVoltageUs, c.DcMaxVoltageUs);
WriteRegs(AcDcGcBypassMode, c.AcDcGcBypassMode);
WriteRegs(AcDcGcPMaxThresholdPercent, 0.01m, c.AcDcGcPMaxThresholdPercent);
WriteRegs(AcDcGcStartupRampEnable, c.AcDcGcStartupRampEnable);
WriteRegs(DcConfigModule, c.DcConfigModule);
WriteRegs(DcDcPowerDistribution, 0.1m, c.DcDcPowerDistribution);
WriteRegs(SystemControlRegisters.AcDcDistributionMode, c.AcDcDistributionMode);
}
private void WriteRegs (UInt16 a, Decimal res = 1.0m, params Decimal[] regs) => ModbusTcpClient.WriteRegisters(a, regs.ToUInt16(res));
private void WriteRegs (UInt16 a, params IConvertible[] regs) => ModbusTcpClient.WriteRegisters(a, regs.Select(v => v.ConvertTo<UInt16>()).ToArray());
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 StatusRecord? ReadStatus()
{
try
{
return TryReadStatus();
}
catch (Exception e)
{
ModbusTcpClient.CloseConnection();
Console.WriteLine("Failed to read inverter status");
e.Message.WriteLine();
return null;
}
}
private StatusRecord TryReadStatus()
{
// Console.WriteLine("Reading Ac Device");
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();
var alarms = Enumerable
.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 StatusRecord
{
Ac = new Ac3Bus
{
Frequency = gridFrequency,
L1 = new AcPhase
{
Voltage = gridVoltageL1,
Current = phaseCurrentL1,
Phi = ACos(powerAcL1 / apparentPowerAcL1), // TODO: 2pi
},
L2 = new AcPhase
{
Voltage = gridVoltageL2,
Current = phaseCurrentL2,
Phi = ACos(powerAcL2 / apparentPowerAcL2), // TODO: 2pi
},
L3 = new AcPhase
{
Voltage = gridVoltageL3,
Current = phaseCurrentL3,
Phi = ACos(powerAcL3 / apparentPowerAcL3), // TODO: 2pi
}
},
Dc = new DcBus
{
Current = dcCurrent,
Voltage = dcVoltage,
},
MainState = acActualMain.GetInt16(5001).ConvertTo<MainState>(),
Alarms = alarms,
Warnings = warnings,
GridType = acActualAcDc.GetUInt16(5024).ConvertTo<AcDcGridType>(),
SerialNumber = acSerialNumber.GetInt32(2009).ToString(), // TODO: why tostring ?
NumberOfConnectedSlaves = acActualMain.GetUInt16(5002),
NumberOfConnectedSubSlaves = acActualMain.GetUInt16(5003),
AcDcNominalGridFrequency = acActualAcDc.GetUInt16(5021) * 0.1m,
AcDcNominalGridVoltage = acActualAcDc.GetUInt16(5022),
AcDcActNominalPower = acActualAcDc.GetUInt16(5023),
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,
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
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,
InletAirTemperature = acActualTemp.GetInt16(5501) * 0.1m,
};
// (
// Ac: new Ac3Bus
// (
// new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
// new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
// new AcPhase(gridVoltageL3,phaseCurrentL3, ACos(powerAcL3/apparentPowerAcL3)),
// 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),
//
// //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,
//
// //ac Actual AcDc 2
// 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),
//
// VoltageIntNtoPe : acActualMeasurement9.GetInt16(5221) * 0.1m,
// VoltageExtNtoPe : acActualMeasurement9.GetInt16(5222) * 0.1m,
// //acActualTemp
// InletAirTemperature : acActualTemp.GetInt16(5501) * 0.1m,
//
// Warnings : warnings,
// Alarms : alarms
// );
}
}

View File

@ -0,0 +1,83 @@
namespace InnovEnergy.Lib.Devices.Trumpf.SystemControl;
public static class SystemControlRegisters
{
// TODO
// public const UInt16 Date = 1001;
// public const UInt16 Time = 1003;
// public const UInt16 IpAddress = 1005;
// public const UInt16 Subnet = 1007;
// public const UInt16 Gateway = 1009;
// public const UInt16 ResetParamToDefault = 1011;
// public const UInt16 CommunicationTimeout = 1017;
// public const UInt16 RestartFlag = 1018;
// public const UInt16 ConnectedSystemConfig = 1019;
// public const UInt16 UpdateSwTrigger = 1027;
// public const UInt16 AutomaticSwUpdate = 1028;
// public const UInt16 CustomerValuesSaveReset = 1029;
// public const UInt16 SerialNumberSystemControl = 2001;
// public const UInt16 SerialNumberAcDc = 2009;
// public const UInt16 IntegrationLevel = 2051;
// public const UInt16 IlBuildnumber = 2052;
// public const UInt16 PowerStageConfig = 4001;
// public const UInt16 SetValueConfig = 4002;
// public const UInt16 ResetsAlarmAndWarning = 4003;
// public const UInt16 PreChargeDcLinkConfigR = 4006;
// public const UInt16 ReferenceFrameConvention = 4007;
// public const UInt16 SlaveAddress = 4008;
// public const UInt16 ErrorHandlingPolicy = 4009;
// public const UInt16 GridType = 4010;
// public const UInt16 SubSlaveAddress = 4011;
// public const UInt16 ModbusSlaveId = 4012;
// public const UInt16 SubSlaveErrorPolicy = 4013;
// public const UInt16 SignedPowerNominalValue = 4196;
// public const UInt16 SignedPowerSetValueL1 = 4197;
// public const UInt16 SignedPowerSetValueL2 = 4198;
// public const UInt16 SignedPowerSetValueL3 = 4199;
// public const UInt16 PowerSetValue = 4200;
// public const UInt16 PowerSetValueL1 = 4201;
// public const UInt16 PowerSetValueL2 = 4202;
// public const UInt16 PowerSetValueL3 = 4203;
// public const UInt16 MaximumGridCurrentRmsL1 = 4204;
// public const UInt16 MaximumGridCurrentRmsL2 = 4205;
// public const UInt16 MaximumGridCurrentRmsL3 = 4206;
// public const UInt16 CosPhiSetValueL1 = 4207;
// public const UInt16 CosPhiSetValueL2 = 4208;
// public const UInt16 CosPhiSetValueL3 = 4209;
// public const UInt16 PhaseL1IsCapacitive = 4214; // True = Capacitive, false = Inductive
// public const UInt16 PhaseL2IsCapacitive = 4215; // True = Capacitive, false = Inductive
// public const UInt16 PhaseL3IsCapacitive = 4216; // True = Capacitive, false = Inductive
// public const UInt16 PhasesAreCapacitive = 4217; // True = Capacitive, false = Inductive
// public const UInt16 SetPointCosPhi = 4218;
// public const UInt16 SetPointSinPhi = 4219;
// public const UInt16 SetPointSinPhiL1 = 4220;
// public const UInt16 SetPointSinPhiL2 = 4221;
// public const UInt16 SetPointSinPhiL3 = 4222;
// public const UInt16 FrequencyOffsetIm = 4223; //Im: Island mode
// public const UInt16 VoltageAdjustmentFactorIm = 4224; //Im: Island mode
// public const UInt16 PreChargeDcLinkVoltage = 4226;
// public const UInt16 MaxPeakCurrentVoltageControlL1 = 4227;
// public const UInt16 MaxPeakCurrentVoltageControlL2 = 4228;
// public const UInt16 MaxPeakCurrentVoltageControlL3 = 4229;
// public const UInt16 GridFormingMode = 4230;
// public const UInt16 DcLinkReferenceVoltage = 4231;
// public const UInt16 DcLinkMinVoltage = 4232;
// public const UInt16 DcLinkMaxVoltage = 4233;
// public const UInt16 AcDcDcVoltageRefUs = 4234;
// public const UInt16 AcDcMinDcLinkVoltageUs = 4235;
// public const UInt16 AcDcMaxDcLinkVoltageUs = 4236;
// // public const UInt16 FrequencySlopeIslandMode = 4237, // Function fN = f(active grid-power) of droop control
// // public const UInt16 VoltageSlopeIslandMode = 4238, // VN = f(reactive grid power) of droop control in island operation.
// public const UInt16 AcDcGcBypassMode = 4281;
// public const UInt16 AcDcGcPMaxThresholdPercent = 4282; // res
// public const UInt16 AcDcGcStartupRampEnable = 4283;
// public const UInt16 DcConfigModule = 4301; // 0 = DC module is off, battery voltage can be measured
// // 1 = DC module is active (DC link voltage control)
// // 2 = DC module is active(current source mode orin DC droop mode)
// public const UInt16 DcDcPowerDistribution = 4304;
// public const UInt16 AcDcDistributionMode = 4307;
}

View File

@ -2,9 +2,9 @@ namespace InnovEnergy.Lib.Devices.Trumpf.TruConvert;
public enum MainState : UInt16
{
PowerUp = 0,
Alarm = 1,
Idle = 2,
Operation = 3,
PowerUp = 0,
Alarm = 1,
Idle = 2,
Operation = 3,
Maintenance = 4,
}

View File

@ -1,62 +0,0 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
public enum AcDcGridType : UInt16
{
GridTied400V50Hz = 0, // grid-tied, 400V, 50Hz
GridTied480V60Hz = 1, // grid-tied, 480V, 60Hz
Island400V50Hz = 2, // island mode, 400V, 50Hz
Island480V60Hz = 3, // island mode, 480V, 60Hz
GridTied380V60Hz = 4, // grid-tied, 380V, 60Hz
}
public enum PreChargeDcLinkConfig : UInt16
{
External = 0, // device waits for external precharge of DC link
Internal = 1, // device precharges external DC link to necessary start-up voltage
InternalWithDc =
2, // behaviour similar to 1 with additional support of DC submodules (necessary for island operation)
InternalWithWait =
3 // device precharges external DC link to necessary start-up voltage and waits until PrechargeDcLinkConfig gets set back to 1 or 2
}
public enum AcErrorPolicy : UInt16
{
Relaxed = 0, // 0 = relaxed (System keeps running even if some slaves are in error state.)
Strict = 1, // 1 = strict (System shuts down as soon as one component is in error state.)
}
public enum SymmetricAcOperationMode : UInt16
{
//Configuration AC set values for phases L1 - L3
Asymmetric = 0,
Symmetric = 1,
}
public enum PowerFactorConvention : UInt16
{
Producer = 0, // 0 = producer reference frame
Consumer = 1, // 1 = consumer reference frame
}
public enum SubSlavesErrorPolicy : UInt16
{
Strict = 0, // (AC-DC module switches to error state if at least one submodules is in error state
Relaxed = 1, // (AC-DC module switches to error state if all submodules are in error state.)
Off = 2, // (If possible AC-DC module continues operation even if all submodules are in error state.
}
public enum DcStageConfiguration : UInt16
{
Off = 0, // DC module is off, power electronic circuit is deactivated, battery voltage can be measured.
Active = 1, // DC module is active and the power distribution according to parameters 4303 is active.
ActiveIndependent = 2, // DC module is active and operates independent from the AC module. The battery current set point from register 4500 is getting applied.
}
public enum AcDcDistributionMode : UInt16
{
Power = 0, // power distribution
Current = 1, // current distribution
Auto = 2, // auto
}

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum AcDcDistributionMode : UInt16
{
Power = 0, // power distribution
Current = 1, // current distribution
Auto = 2, // auto
}

View File

@ -0,0 +1,10 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum AcDcGridType : UInt16
{
GridTied400V50Hz = 0, // grid-tied, 400V, 50Hz
GridTied480V60Hz = 1, // grid-tied, 480V, 60Hz
Island400V50Hz = 2, // island mode, 400V, 50Hz
Island480V60Hz = 3, // island mode, 480V, 60Hz
GridTied380V60Hz = 4, // grid-tied, 380V, 60Hz
}

View File

@ -0,0 +1,7 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum AcErrorPolicy : UInt16
{
Relaxed = 0, // 0 = relaxed (System keeps running even if some slaves are in error state.)
Strict = 1, // 1 = strict (System shuts down as soon as one component is in error state.)
}

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum DcStageConfiguration : UInt16
{
Off = 0, // DC module is off, power electronic circuit is deactivated, battery voltage can be measured.
Active = 1, // DC module is active and the power distribution according to parameters 4303 is active.
ActiveIndependent = 2, // DC module is active and operates independent from the AC module. The battery current set point from register 4500 is getting applied.
}

View File

@ -0,0 +1,7 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum PowerFactorConvention : UInt16
{
Producer = 0, // 0 = producer reference frame
Consumer = 1, // 1 = consumer reference frame
}

View File

@ -0,0 +1,9 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum PreChargeDcLinkConfig : UInt16
{
External = 0, // device waits for external precharge of DC link
Internal = 1, // device precharges external DC link to necessary start-up voltage
InternalWithDc = 2, // behaviour similar to 1 with additional support of DC submodules (necessary for island operation)
InternalWithWait = 3 // device precharges external DC link to necessary start-up voltage and waits until PrechargeDcLinkConfig gets set back to 1 or 2
}

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum SubSlavesErrorPolicy : UInt16
{
Strict = 0, // (AC-DC module switches to error state if at least one submodules is in error state
Relaxed = 1, // (AC-DC module switches to error state if all submodules are in error state.)
Off = 2, // (If possible AC-DC module continues operation even if all submodules are in error state.
}

View File

@ -0,0 +1,8 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
public enum SymmetricAcOperationMode : UInt16
{
//Configuration AC set values for phases L1 - L3
Asymmetric = 0,
Symmetric = 1,
}

View File

@ -1,8 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../../InnovEnergy.Lib.props" />
<ItemGroup>
<ProjectReference Include="../../../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../../../StatusApi/StatusApi.csproj" />

View File

@ -1,4 +1,5 @@
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;

View File

@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
using InnovEnergy.Lib.Protocols.Modbus.Clients;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.Units.Composite;
@ -139,22 +140,22 @@ public class TruConvertAcDevice
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;
? dcPower / dcVoltage
: 0m;
// //acActualMeasurement
@ -214,54 +215,116 @@ public class TruConvertAcDevice
var gridFrequency = acActualMeasurement7.GetInt16(5201) * 0.01m;
return new TruConvertAcStatus
(
Ac: new Ac3Bus
(
new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
new AcPhase(gridVoltageL3,phaseCurrentL3, ACos(powerAcL3/apparentPowerAcL3)),
gridFrequency // Gird Frequency
),
Dc: new DcConnection(dcVoltage, dcCurrent),
{
Ac = new Ac3Bus
{
Frequency = gridFrequency,
SerialNumber : acSerialNumber.GetInt32(2009).ToString(),
L1 = new AcPhase
{
Voltage = gridVoltageL1,
Current = phaseCurrentL1,
Phi = ACos(powerAcL1 / apparentPowerAcL1), // TODO: 2pi
},
L2 = new AcPhase
{
Voltage = gridVoltageL2,
Current = phaseCurrentL2,
Phi = ACos(powerAcL2 / apparentPowerAcL2), // TODO: 2pi
},
L3 = new AcPhase
{
Voltage = gridVoltageL3,
Current = phaseCurrentL3,
Phi = ACos(powerAcL3 / apparentPowerAcL3), // TODO: 2pi
}
},
Dc = new DcBus
{
Current = dcCurrent,
Voltage = dcVoltage,
},
// acActualMainValues
MainState : acActualMain.GetInt16(5001).ConvertTo<MainState>(),
NumberOfConnectedSlaves : acActualMain.GetUInt16(5002),
NumberOfConnectedSubSlaves : acActualMain.GetUInt16(5003),
MainState = acActualMain.GetInt16(5001).ConvertTo<MainState>(),
Alarms = alarms,
Warnings = warnings,
GridType = acActualAcDc.GetUInt16(5024).ConvertTo<AcDcGridType>(),
SerialNumber = acSerialNumber.GetInt32(2009).ToString(), // TODO: why tostring ?
NumberOfConnectedSlaves = acActualMain.GetUInt16(5002),
NumberOfConnectedSubSlaves = acActualMain.GetUInt16(5003),
AcDcNominalGridFrequency = acActualAcDc.GetUInt16(5021) * 0.1m,
AcDcNominalGridVoltage = acActualAcDc.GetUInt16(5022),
AcDcActNominalPower = acActualAcDc.GetUInt16(5023),
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,
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
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,
InletAirTemperature = acActualTemp.GetInt16(5501) * 0.1m,
//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,
};
//ac Actual AcDc 2
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),
VoltageIntNtoPe : acActualMeasurement9.GetInt16(5221) * 0.1m,
VoltageExtNtoPe : acActualMeasurement9.GetInt16(5222) * 0.1m,
//acActualTemp
InletAirTemperature : acActualTemp.GetInt16(5501) * 0.1m,
Warnings : warnings,
Alarms : alarms
);
// (
// Ac: new Ac3Bus
// (
// new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
// new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
// new AcPhase(gridVoltageL3,phaseCurrentL3, ACos(powerAcL3/apparentPowerAcL3)),
// 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),
//
// //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,
//
// //ac Actual AcDc 2
// 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),
//
// VoltageIntNtoPe : acActualMeasurement9.GetInt16(5221) * 0.1m,
// VoltageExtNtoPe : acActualMeasurement9.GetInt16(5222) * 0.1m,
// //acActualTemp
// InletAirTemperature : acActualTemp.GetInt16(5501) * 0.1m,
//
// Warnings : warnings,
// Alarms : alarms
// );
}
}

View File

@ -1,43 +1,45 @@
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.Enums;
using InnovEnergy.Lib.StatusApi;
using InnovEnergy.Lib.Units;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
using AlarmMessages = IReadOnlyList<AlarmMessage>;
using WarningMessages = IReadOnlyList<WarningMessage>;
public record TruConvertAcStatus
(
Ac3Bus 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)
// ReSharper disable UnusedAutoPropertyAccessor.Global
#pragma warning disable CS8618
public record TruConvertAcStatus : ThreePhaseInverterStatus
{
public MainState MainState { get; init; }
public String SerialNumber { get; init; }
public AcDcGridType GridType { get; init; }
public WarningMessages Warnings { get; init; }
public AlarmMessages Alarms { get; init; }
public Decimal NumberOfConnectedSlaves { get; init; }
public Decimal NumberOfConnectedSubSlaves { get; init; }
public Frequency AcDcNominalGridFrequency { get; init; }
public Voltage AcDcNominalGridVoltage { get; init; }
public Power AcDcActNominalPower { get; init; }
public Decimal AcDcPowerLimitingStatusAct { get; init; } // TODO: enum
public Voltage AcDcDcVoltageReference { get; init; }
public Voltage AcDcDcLinkVoltageMinAct { get; init; }
public Voltage AcDcDcLinkVoltageMaxAct { get; init; }
public Voltage AcDcDcLinkChargedMinVoltage { get; init; }
public Decimal AcDcStmActCustomer { get; init; }
public Decimal AcDcOverloadIntegratorStatusL1 { get; init; }
public Decimal AcDcOverloadIntegratorStatusL2 { get; init; }
public Decimal AcDcOverloadIntegratorStatusL3 { get; init; }
public Power AcSignedPowerValue { get; init; }
public Voltage ActualDcLinkVoltageUpperHalf { get; init; }
public Voltage ActualDcLinkVoltageLowerHalf { get; init; }
public Voltage ActualDcLinkVoltageUpperHalfExt { get; init; }
public Voltage ActualDcLinkVoltageLowerHalfExt { get; init; }
public Voltage VoltageIntNtoPe { get; init; }
public Voltage VoltageExtNtoPe { get; init; }
public Temperature InletAirTemperature { get; init; }
}

View File

@ -8,17 +8,17 @@ namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
public enum WarningMessage
{
ERR_WARN_FAN =10500, //AC-DC module warning
ERR_WARN_I_OFFSET =10503, //AC-DC module warning
ERR_WARN_VG_OFFSET =10504, //AC-DC module warning
ERR_WARN_VC_OFFSET =10505, //AC-DC module warning
ERR_WARN_DC_OFFSET =10506, //AC-DC module warning
ERR_WARN_NTC_PROTECT =10507, //DC-link circuit need more time for cool down
ERR_WARN_AIR_TEMP =10508, //Overtemperature inlet air: power is derated
SurgeDetected =10509, //Temporary overvoltage in grid measurement detected (surge)
ERR_WARN_TEMP_DERATING =11021, //Temperature derating active
ERR_WARN_OVERLOAD =11022, //Overload handling is active
ERR_WARN_RUNTIME_EEPROM =11023, //AC-DC module warning
ERR_WARN_OVERCURRENT =11024 //Overcurrent handling is active
ERR_WARN_FAN = 10500, //AC-DC module warning
ERR_WARN_I_OFFSET = 10503, //AC-DC module warning
ERR_WARN_VG_OFFSET = 10504, //AC-DC module warning
ERR_WARN_VC_OFFSET = 10505, //AC-DC module warning
ERR_WARN_DC_OFFSET = 10506, //AC-DC module warning
ERR_WARN_NTC_PROTECT = 10507, //DC-link circuit need more time for cool down
ERR_WARN_AIR_TEMP = 10508, //Overtemperature inlet air: power is derated
SurgeDetected = 10509, //Temporary overvoltage in grid measurement detected (surge)
ERR_WARN_TEMP_DERATING = 11021, //Temperature derating active
ERR_WARN_OVERLOAD = 11022, //Overload handling is active
ERR_WARN_RUNTIME_EEPROM = 11023, //AC-DC module warning
ERR_WARN_OVERCURRENT = 11024 //Overcurrent handling is active
}

View File

@ -2,7 +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.Units.Composite;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.DcControlRegisters;
@ -138,27 +138,31 @@ public class TruConvertDcDevice
var dcCurrent = dcLinkVoltage != 0m ? dcPower / dcLinkVoltage : 0m;
return new TruConvertDcStatus
(
{
Left = new DcBus()
{
Current = dcCurrent,
Voltage = dcLinkVoltage
},
Dc: new DcConnection
(
dcLinkVoltage,
dcCurrent
),
Right = new DcBus()
{
Current = dcBatteryValue2.GetInt16(5111),
Voltage =dcBatteryValue.GetUInt16(5101) * 0.1m,
},
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)
);
MainState = (MainState)dcPrValMain.GetInt16(5001),
NumberOfConnectedSlaves = dcPrValMain.GetUInt16(5002),
NumberOfConnectedSubSlaves = dcPrValMain.GetUInt16(5003),
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,3 +1,4 @@
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
using InnovEnergy.Lib.StatusApi;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Composite;
@ -9,24 +10,20 @@ using AlarmMessages = IReadOnlyList<AlarmMessage>;
using WarningMessages = IReadOnlyList<WarningMessage>;
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
public record TruConvertDcStatus
(
DcBus DcLeft,
DcBus DcRight,
State MainState,
Power TotalDcPower, // TODO: necessary?
State StatusOfCurrentLimiting,
Decimal OverloadCapacity,
Temperature DcDcInletTemperature,
State Alarms,
State Warnings,
State PowerOperation
// UInt16 NumberOfConnectedSlaves, // TODO: necessary?
// UInt16 NumberOfConnectedSubSlaves, // TODO: necessary?
) :
DcDcConverterStatus(DcLeft, DcRight)
public record TruConvertDcStatus : DcDcConverterStatus
{
public static TruConvertDcStatus operator |(TruConvertDcStatus left, TruConvertDcStatus right) => OpParallel(left, right);
private static readonly Func<TruConvertDcStatus, TruConvertDcStatus, TruConvertDcStatus> OpParallel = Operators.Op<TruConvertDcStatus>("|");
public MainState MainState { get; init; }
public Power TotalDcPower { get; init; } // TODO: necessary?
public DcCurrentLimitStates StatusOfCurrentLimiting { get; init; }
public Decimal OverloadCapacity { get; init; }
public Temperature DcDcInletTemperature { get; init; }
public AlarmMessages Alarms { get; init; } = Array.Empty<AlarmMessage>();
public WarningMessages Warnings { get; init; } = Array.Empty<WarningMessage>();
public Boolean PowerOperation { get; init; }
public Decimal NumberOfConnectedSlaves { get; init; } // TODO: necessary?
public Decimal NumberOfConnectedSubSlaves { get; init; } // TODO: necessary?
}
// {
// public static TruConvertDcStatus operator |(TruConvertDcStatus left, TruConvertDcStatus right) => OpParallel(left, right);
// private static readonly Func<TruConvertDcStatus, TruConvertDcStatus, TruConvertDcStatus> OpParallel = Operators.Op<TruConvertDcStatus>("|");
// }

View File

@ -1,8 +1,6 @@
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
using InnovEnergy.Lib.Protocols.Modbus.Protocol;
using static InnovEnergy.Lib.Protocols.Modbus.Protocol.MultiRegisterEndianness;
using static InnovEnergy.Lib.Protocols.Modbus.Protocol.RegisterIndexing;
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
@ -15,8 +13,6 @@ public abstract class ModbusClient
{
protected ModbusConnection Connection { get; }
protected Byte SlaveId { get; }
protected RegisterIndexing RegisterIndexing { get; }
protected MultiRegisterEndianness Endianness { get; }
// TODO: add additional functions: coils...
@ -45,19 +41,14 @@ public abstract class ModbusClient
}
protected ModbusClient(ModbusConnection connection,
Byte slaveId,
RegisterIndexing registerIndexing = OneBased,
MultiRegisterEndianness endianness = LittleEndian)
Byte slaveId)
{
Connection = connection;
SlaveId = slaveId;
RegisterIndexing = registerIndexing;
Endianness = endianness;
}
public void CloseConnection() => Connection.Close();
protected UInt16 LogicToWire(UInt16 address) => RegisterIndexing.LogicToSerialized(address);
protected UInt16 SerializedToLogic(UInt16 address) => RegisterIndexing.SerializedToLogic(address);
}

View File

@ -30,9 +30,7 @@ public class ModbusRtuClient : ModbusClient
public override ModbusRegisters ReadInputRegisters(UInt16 readAddress, UInt16 nValues)
{
var wireReadAddress = LogicToWire(readAddress);
var cmd = new ReadInputRegistersCommandFrame(SlaveId, wireReadAddress, nValues);
var cmd = new ReadInputRegistersCommandFrame(SlaveId, readAddress, nValues);
var crc = CalcCrc(cmd);
// TX
@ -56,9 +54,7 @@ public class ModbusRtuClient : ModbusClient
public override ModbusRegisters ReadHoldingRegisters(UInt16 readAddress, UInt16 nValues)
{
var wireReadAddress = LogicToWire(readAddress);
var cmd = new ReadHoldingRegistersCommandFrame(SlaveId, wireReadAddress, nValues);
var cmd = new ReadHoldingRegistersCommandFrame(SlaveId, readAddress, nValues);
var crc = CalcCrc(cmd.Data);
// TX
@ -86,9 +82,7 @@ public class ModbusRtuClient : ModbusClient
public override UInt16 WriteRegisters(UInt16 writeAddress, UInt16s values)
{
var wireWriteAddress = LogicToWire(writeAddress);
var cmd = new WriteRegistersCommandFrame(SlaveId, wireWriteAddress, values);
var cmd = new WriteRegistersCommandFrame(SlaveId, writeAddress, values);
var crc = CalcCrc(cmd);
var nToRead = cmd.ExpectedResponseSize + CrcSize;
@ -111,13 +105,10 @@ public class ModbusRtuClient : ModbusClient
public override ModbusRegisters ReadWriteRegisters(UInt16 readAddress, UInt16 nbToRead, UInt16 writeAddress, UInt16s registersToWrite)
{
var wireReadAddress = LogicToWire(readAddress);
var wireWriteAddress = LogicToWire(writeAddress);
var cmd = new ReadWriteRegistersCommandFrame(SlaveId,
wireReadAddress,
readAddress,
nbToRead,
wireWriteAddress,
writeAddress,
registersToWrite);
var crc = CalcCrc(cmd);

View File

@ -1,11 +1,10 @@
using System.Diagnostics;
using InnovEnergy.Lib.Protocols.Modbus.Connections;
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
using InnovEnergy.Lib.Protocols.Modbus.Protocol;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Commands;
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies;
using InnovEnergy.Lib.Protocols.Modbus.Tcp;
using static InnovEnergy.Lib.Protocols.Modbus.Protocol.MultiRegisterEndianness;
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
@ -21,21 +20,16 @@ public class ModbusTcpClient : ModbusClient
private UInt16 NextId() => unchecked(++_Id);
public ModbusTcpClient(ModbusConnection connection,
Byte slaveId,
RegisterIndexing registerIndexing = RegisterIndexing.OneBased,
MultiRegisterEndianness endianness = LittleEndian) : base(connection, slaveId, registerIndexing, endianness)
public ModbusTcpClient(ModbusConnection connection, Byte slaveId) : base(connection, slaveId)
{
}
public override IReadOnlyList<Boolean> ReadDiscreteInputs(UInt16 readAddress, UInt16 nValues)
{
var wireReadAddress = LogicToWire(readAddress);
var id = NextId(); // TODO: check response id
var cmd = new ReadDiscreteInputsCommandFrame(SlaveId, wireReadAddress, nValues);
var cmd = new ReadDiscreteInputsCommandFrame(SlaveId, readAddress, nValues);
var hdr = new MbapHeader(id, cmd.Data.Count);
var frm = new ModbusTcpFrame(hdr, cmd);
@ -52,10 +46,9 @@ public class ModbusTcpClient : ModbusClient
public override ModbusRegisters ReadInputRegisters(UInt16 readAddress, UInt16 nValues)
{
var wireReadAddress = LogicToWire(readAddress);
var id = NextId(); // TODO: check response id
var cmd = new ReadInputRegistersCommandFrame(SlaveId, wireReadAddress, nValues);
var cmd = new ReadInputRegistersCommandFrame(SlaveId, readAddress, nValues);
var hdr = new MbapHeader(id, cmd.Data.Count);
var frm = new ModbusTcpFrame(hdr, cmd);
@ -77,10 +70,8 @@ public class ModbusTcpClient : ModbusClient
public override ModbusRegisters ReadHoldingRegisters(UInt16 readAddress, UInt16 nValues)
{
var wireReadAddress = LogicToWire(readAddress);
var id = NextId(); // TODO: check response id
var cmd = new ReadHoldingRegistersCommandFrame(SlaveId, wireReadAddress, nValues);
var cmd = new ReadHoldingRegistersCommandFrame(SlaveId, readAddress, nValues);
var hdr = new MbapHeader(id, cmd.Data.Count);
var frm = new ModbusTcpFrame(hdr, cmd);
@ -99,10 +90,8 @@ public class ModbusTcpClient : ModbusClient
public override UInt16 WriteMultipleCoils(UInt16 writeAddress, Coils coils)
{
var wireWriteAddress = LogicToWire(writeAddress);
var id = NextId(); // TODO: check response id
var cmd = new WriteCoilsCommandFrame(SlaveId, wireWriteAddress, coils);
var id = NextId(); // TODO: check response id
var cmd = new WriteCoilsCommandFrame(SlaveId, writeAddress, coils);
var hdr = new MbapHeader(id, cmd.Data.Count);
var frm = new ModbusTcpFrame(hdr, cmd);
@ -120,10 +109,8 @@ public class ModbusTcpClient : ModbusClient
public override UInt16 WriteRegisters(UInt16 writeAddress, UInt16s values)
{
var wireWriteAddress = LogicToWire(writeAddress);
var id = NextId(); // TODO: check response id
var cmd = new WriteRegistersCommandFrame(SlaveId, wireWriteAddress, values);
var cmd = new WriteRegistersCommandFrame(SlaveId, writeAddress, values);
var hdr = new MbapHeader(id, cmd.Data.Count);
var frm = new ModbusTcpFrame(hdr, cmd);
@ -142,15 +129,14 @@ public class ModbusTcpClient : ModbusClient
public override ModbusRegisters ReadWriteRegisters(UInt16 readAddress, UInt16 nbToRead, UInt16 writeAddress, UInt16s registersToWrite)
{
var wireReadAddress = LogicToWire(readAddress);
var wireWriteAddress = LogicToWire(writeAddress);
var id = NextId(); // TODO: check response id
var cmd = new ReadWriteRegistersCommandFrame(SlaveId,
wireReadAddress,
readAddress,
nbToRead,
wireWriteAddress,
writeAddress,
registersToWrite);
var hdr = new MbapHeader(id, cmd.Data.Count);

View File

@ -1,6 +1,5 @@
using System.Collections;
using InnovEnergy.Lib.Utils;
using static InnovEnergy.Lib.Protocols.Modbus.Protocol.MultiRegisterEndianness;
namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
@ -10,22 +9,11 @@ public struct MbRegisters : IReadOnlyList<UInt16>
{
private MbWords Words { get; }
private UInt16 StartRegister { get; }
private MultiRegisterEndianness Endianness { get; }
public MbRegisters(MbWords words,
UInt16 startRegister,
MultiRegisterEndianness endianness,
RegisterIndexing registerIndexing)
public MbRegisters(MbWords words, UInt16 startRegister)
{
Words = words;
Endianness = endianness;
var start = startRegister - (Int16) registerIndexing; // TODO: check
if (start < 0)
throw new ArgumentOutOfRangeException(nameof(startRegister));
StartRegister = (UInt16)start;
Words = words;
StartRegister = startRegister;
}
public IEnumerator<UInt16> GetEnumerator() => Words.GetEnumerator();
@ -74,9 +62,6 @@ public struct MbRegisters : IReadOnlyList<UInt16>
var hi = (UInt32) GetUInt16(i);
var lo = (UInt32) GetUInt16(++i);
if (Endianness == LittleEndian)
(lo, hi) = (hi, lo);
return hi << 16 | lo;
}

View File

@ -1,17 +1,15 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
using T = BatteryStatus;
[OpParallel]
public partial record BatteryStatus : DeviceStatus, IDcConnection
#pragma warning disable CS8618
public record BatteryStatus : IDcConnection
{
public DcBus Dc { get; init; }
public Percent Soc { get; init; }
public Temperature Temperature { get; init; }
}

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = BatteryStatus;
[GeneratedCode("generate.sh", "1")]
public partial record BatteryStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -0,0 +1,12 @@
namespace InnovEnergy.Lib.StatusApi;
#pragma warning disable CS8618
public record CombinedStatus<T>
{
public T Combined { get; init; }
public IReadOnlyList<T> Children { get; init; }
public Boolean Available => Children.Any();
}

View File

@ -1,10 +1,10 @@
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record DcDcConverterStatus : DeviceStatus
#pragma warning disable CS8618
public record DcDcConverterStatus
{
public DcBus Left { get; init; }
public DcBus Right { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = DcDcConverterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record DcDcConverterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,52 +0,0 @@
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
public abstract record DeviceStatus
{
public String DeviceType => GetType()
.Unfold(t => t.BaseType)
.First(t => t.IsAbstract)
.Name
.Replace("Status", "");
}
// public static class Program
// {
// public static void Main(string[] args)
// {
// var x = new ThreePhasePvInverterStatus
// {
// Ac = new()
// {
// Frequency = 50,
// L1 = new()
// {
// Current = 10,
// Voltage = 10,
// Phi = 0,
// },
// L2 = new()
// {
// Current = 52,
// Voltage = 220,
// Phi = Angle.Pi / 2,
// },
// L3 = new()
// {
// Current = 158,
// Voltage = 454,
// Phi = Angle.Pi / 3,
// },
// },
// Strings = new DcBus[]
// {
// new() { Current = 10, Voltage = 22 },
// new() { Current = 12, Voltage = 33 },
// }
// };
//
// var s = x.ToJson();
// }
// }

View File

@ -1,5 +0,0 @@
namespace InnovEnergy.Lib.StatusApi.Generator;
[AttributeUsage(AttributeTargets.Class)]
internal class OpParallelAttribute : Attribute
{}

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = Template;
[GeneratedCode("generate.sh", "1")]
public partial record Template
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,13 +0,0 @@
#!/usr/bin/env bash
scriptDir=$( dirname -- "$0"; )
cd "$scriptDir/.." || exit
for path in $(grep -e '\[OpParallel\]' -l *.cs)
do
file=$(basename -- "$path")
class="${file%.*}"
echo "generating $file"
sed "s/Template/$class/g" "./Generator/Template.txt" > "./$class.generated.cs"
done

View File

@ -1,11 +1,11 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record MpptStatus : IDcConnection, IPvConnection
#pragma warning disable CS8618
public record MpptStatus : IDcConnection, IPvConnection
{
public DcBus Dc { get; init; }
public IReadOnlyList<DcBus> Strings { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = MpptStatus;
[GeneratedCode("generate.sh", "1")]
public partial record MpptStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,11 +1,11 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record PowerMeterStatus : DeviceStatus, IAc3Connection
#pragma warning disable CS8618
public record PowerMeterStatus : IAc3Connection
{
public Ac3Bus Ac { get; init; }
}

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = PowerMeterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record PowerMeterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,14 +1,12 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record SinglePhaseInverterStatus :
DeviceStatus,
IAc1Connection,
IDcConnection
#pragma warning disable CS8618
public record SinglePhaseInverterStatus : IAc1Connection, IDcConnection
{
public Ac1Bus Ac { get; init; }
public DcBus Dc { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = SinglePhaseInverterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record SinglePhaseInverterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,14 +1,11 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record SinglePhasePvInverterStatus :
DeviceStatus,
IAc1Connection,
IPvConnection
#pragma warning disable CS8618
public record SinglePhasePvInverterStatus : IAc1Connection, IPvConnection
{
public Ac1Bus Ac { get; init; }
public IReadOnlyList<DcBus> Strings { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = SinglePhasePvInverterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record SinglePhasePvInverterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,16 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../InnovEnergy.Lib.props" />
<!-- <Import Project="../../App/InnovEnergy.App.props" />-->
<ItemGroup>
<ProjectReference Include="../Protocols/Modbus/Modbus.csproj" />
<ProjectReference Include="../Units/Units.csproj" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="./Generator/generate.sh" />
</Target>
</Project>

View File

@ -1,14 +1,11 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record ThreePhaseInverterStatus :
DeviceStatus,
IAc3Connection,
IDcConnection
#pragma warning disable CS8618
public record ThreePhaseInverterStatus : IAc3Connection, IDcConnection
{
public Ac3Bus Ac { get; init; }
public DcBus Dc { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = ThreePhaseInverterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record ThreePhaseInverterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,14 +1,11 @@
using InnovEnergy.Lib.StatusApi.Connections;
using InnovEnergy.Lib.StatusApi.Generator;
using InnovEnergy.Lib.Units.Composite;
namespace InnovEnergy.Lib.StatusApi;
[OpParallel]
public partial record ThreePhasePvInverterStatus :
DeviceStatus,
IAc3Connection,
IPvConnection
#pragma warning disable CS8618
public record ThreePhasePvInverterStatus : IAc3Connection, IPvConnection
{
public Ac3Bus Ac { get; init; }
public IReadOnlyList<DcBus> Strings { get; init; }

View File

@ -1,16 +0,0 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
using System.CodeDom.Compiler;
using InnovEnergy.Lib.Units.Composite;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.StatusApi;
using T = ThreePhasePvInverterStatus;
[GeneratedCode("generate.sh", "1")]
public partial record ThreePhasePvInverterStatus
{
private static readonly Func<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
public static T operator |(T left, T right) => OpParallel(left, right);
}

View File

@ -1,20 +1,19 @@
using InnovEnergy.Lib.Utils;
using static DecimalMath.DecimalEx;
namespace InnovEnergy.Lib.Units.Composite;
#pragma warning disable CS8618
public record Ac3Bus
{
public AcPhase L1 { get; init; }
public AcPhase L2 { get; init; }
public AcPhase L3 { get; init; }
public Frequency Frequency { get; init; }
public AcPhase L1 { get; init; }
public AcPhase L2 { get; init; }
public AcPhase L3 { get; init; }
public Frequency Frequency { get; init; }
public ApparentPower ApparentPower => L1.ApparentPower + L2.ApparentPower + L3.ApparentPower;
public ReactivePower ReactivePower => L1.ReactivePower + L2.ReactivePower + L3.ReactivePower;
public Power ActivePower => L1.ActivePower + L2.ActivePower + L3.ActivePower;
public Angle Phi => ATan2(ReactivePower, ActivePower);
public static Ac3Bus operator |(Ac3Bus left, Ac3Bus right) => OpParallel(left, right);
private static readonly Func<Ac3Bus, Ac3Bus, Ac3Bus> OpParallel = "|".CreateBinaryOpForProps<Ac3Bus>();
}

View File

@ -19,7 +19,7 @@ public record AcPhase : IBus
init => _Current = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative");
}
public Angle Phi { get; init; }
public Angle Phi { get; init; }
public ApparentPower ApparentPower => Voltage.Value * Current.Value ;
public Power ActivePower => ApparentPower.Value * PowerFactor;

View File

@ -1,5 +1,3 @@
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.Lib.Units.Composite;
public record DcBus : IBus
@ -8,7 +6,4 @@ public record DcBus : IBus
public Current Current { get; init; }
public Power Power => Current * Voltage;
public static DcBus operator |(DcBus left, DcBus right) => OpParallel(left, right);
private static readonly Func<DcBus, DcBus, DcBus> OpParallel = "|".CreateBinaryOpForProps<DcBus>();
}

View File

@ -0,0 +1,71 @@
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
#define Generate
using static System.Math;
using System.Text.Json;
using System.Text.Json.Serialization;
using InnovEnergy.Lib.Utils;
using System.CodeDom.Compiler;
namespace InnovEnergy.Lib.Units;
using T = Percent;
[GeneratedCode("generate.sh", "1")]
[JsonConverter(typeof(PercentConverter))]
public readonly partial struct Percent
{
public Decimal Value { get; }
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
// scalar multiplication
public static T operator *(Decimal scalar, T t) => new T(scalar * t.Value);
public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value);
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
// addition
public static T operator +(T left, T right) => new T(left.Value + right.Value);
public static T operator -(T left, T right) => new T(left.Value - right.Value);
public static T operator -(T t) => new T(-t.Value);
// compare
public static Boolean operator ==(T left, T right) => left.Value == right.Value;
public static Boolean operator !=(T left, T right) => left.Value != right.Value;
public static Boolean operator > (T left, T right) => left.Value > right.Value;
public static Boolean operator < (T left, T right) => left.Value < right.Value;
public static Boolean operator >=(T left, T right) => left.Value >= right.Value;
public static Boolean operator <=(T left, T right) => left.Value <= right.Value;
// conversion
public static implicit operator T(Decimal d) => new T(d);
public static implicit operator T(Double d) => new T((Decimal)d);
public static implicit operator T(Int32 i) => new T(i);
public static implicit operator Decimal(T t) => t.Value;
// equality
public Boolean Equals(T other) => Value == other.Value;
public override Boolean Equals(Object? obj) => obj is T other && Equals(other);
public override Int32 GetHashCode() => Value.GetHashCode();
}
internal class PercentConverter : JsonConverter<Percent>
{
public override Percent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new Percent(reader.GetDecimal());
}
public override void Write(Utf8JsonWriter writer, Percent value, JsonSerializerOptions options)
{
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
writer.WriteNumberValue(rounded);
}
}

View File

@ -1,23 +1,7 @@
using System.Text.Json.Serialization;
namespace InnovEnergy.Lib.Units;
public static class Units
{
static Units()
{
JsonConverters = typeof(Units)
.Assembly
.GetTypes()
.Where(t => t.IsAssignableTo(typeof(JsonConverter)))
.Select(Activator.CreateInstance)
.Cast<JsonConverter>()
.ToArray();
}
public static IReadOnlyList<JsonConverter> JsonConverters { get; }
public static Byte DisplaySignificantDigits { get; set; } = 3;
public static Byte JsonSignificantDigits { get; set; } = 3;
@ -32,3 +16,88 @@ public static class Units
public static Temperature Celsius(this Decimal value) => value;
public static Energy KWh (this Decimal value) => value;
}
public static class Prefixes
{
private static readonly IReadOnlyList<String> Big = new[]
{
"",
"k",
"M",
"G",
"T",
"P",
"E",
"Y",
};
private static readonly IReadOnlyList<String> Small = new[]
{
"",
"m",
"µ",
"n",
"p",
"f",
"a",
"z",
"y",
};
public static String TestGetPrefix(Double v, String unit)
{
if (v == 0)
return "";
var log10 = Math.Log10(v / 10);
var l = (Int32)Math.Floor(log10 / 3);
var lookUp = l > 0 ? Big : Small;
var i = Math.Abs(l);
return $"{v / Math.Pow(10.0, l * 3.0)} {lookUp[i]}{unit}";
}
public static String TestGetPrefix(Decimal v, String unit)
{
if (v == 0m)
return "";
var d = (Double)v;
var log10 = Math.Log10(d / 10);
var l = (Int32)Math.Floor(log10 / 3);
var lookUp = l > 0 ? Big : Small;
var i = Math.Abs(l);
return $"{d / Math.Pow(10.0, l * 3.0)} {lookUp[i]}{unit}";
}
public static String TestGetPrefix2(Decimal v, String unit)
{
if (v == 0m)
return "";
var a = Math.Abs(v);
var s = Math.Sign(v);
var i = 0;
while (a >= 10000m)
{
a /= 1000;
i++;
}
while (a < 10m)
{
a *= 1000;
i--;
}
var lookUp = i >= 0 ? Big : Small;
var r = Decimal.Floor(a * 10m) / 10m;
return $"{r*s} {lookUp[Math.Abs(i)]}{unit}";
}
}

View File

@ -0,0 +1,26 @@
namespace InnovEnergy.Lib.Utils.Reflection;
public abstract class DataMember : Member
{
public abstract Object? Get();
public abstract void Set(Object value);
public abstract Boolean IsWriteable { get; }
public abstract Boolean IsReadable { get; }
}
public static class DataMembers
{
public static IEnumerable<DataMember> OfInstance<T>(T instance) where T : notnull
{
return instance.GetDataMembers();
}
public static IEnumerable<DataMember> GetDataMembers<T>(this T instance) where T : notnull
{
var fields = instance.GetFields();
var props = instance.GetProperties();
return fields.Concat<DataMember>(props);
}
}

View File

@ -0,0 +1,49 @@
using System.Reflection;
using static System.Reflection.BindingFlags;
namespace InnovEnergy.Lib.Utils.Reflection;
public class Field : DataMember
{
private readonly Object? _Instance;
private readonly FieldInfo _FieldInfo;
public override String Name => _FieldInfo.Name;
public override Type Type => _FieldInfo.FieldType;
public override Boolean IsPublic => _FieldInfo.IsPublic;
public override Boolean IsPrivate => _FieldInfo.IsPrivate;
public override Boolean IsStatic => _FieldInfo.IsStatic;
public override Boolean IsWriteable => true;
public override Boolean IsReadable => true;
public override IEnumerable<Attribute> Attributes => _FieldInfo
.GetCustomAttributes(inherit: false)
.OfType<Attribute>();
internal Field(Object? instance, FieldInfo fieldInfo)
{
_Instance = instance;
_FieldInfo = fieldInfo;
}
public override Object? Get() => _FieldInfo.GetValue(_Instance) ;
public override void Set(Object value) => _FieldInfo.SetValue(_Instance, value);
}
public static class Fields
{
public static IEnumerable<Field> GetFields<T>(this T instance) where T : notnull
{
return instance
.GetType()
.GetFields(Instance | Static | Public | NonPublic)
.Select(fi => new Field(fi.IsStatic ? null : instance, fi));
}
public static IEnumerable<Field> OfInstance<T>(T instance) where T : notnull
{
return instance.GetFields();
}
}

View File

@ -0,0 +1,33 @@
namespace InnovEnergy.Lib.Utils.Reflection;
public abstract class Member
{
public abstract String Name { get; }
public abstract Type Type { get; }
public abstract Boolean IsPublic { get; }
public abstract Boolean IsPrivate { get; }
public abstract Boolean IsStatic { get; }
public abstract IEnumerable<Attribute> Attributes { get; }
}
public static class Members
{
public static IEnumerable<Member> OfInstance<T>(T instance) where T : notnull
{
return instance.GetMembers();
}
public static IEnumerable<Member> GetMembers<T>(this T instance) where T : notnull
{
var fields = instance.GetFields();
var props = instance.GetProperties();
var methods = instance.GetMethods();
return fields
.Concat<Member>(props)
.Concat(methods);
}
}

View File

@ -0,0 +1,52 @@
using System.Reflection;
using static System.Reflection.BindingFlags;
namespace InnovEnergy.Lib.Utils.Reflection;
public class Method : Member
{
private readonly MethodInfo _MethodInfo;
private readonly Object? _Instance;
public override Boolean IsPublic => _MethodInfo.IsPublic;
public override Boolean IsPrivate => _MethodInfo.IsPrivate;
public override Boolean IsStatic => _MethodInfo.IsStatic;
public override String Name => _MethodInfo.Name;
public override Type Type => _MethodInfo.ReturnType;
public override IEnumerable<Attribute> Attributes => _MethodInfo.GetCustomAttributes();
public IEnumerable<Parameter> Parameters => _MethodInfo
.GetParameters()
.Select(i => new Parameter(i));
internal Method(Object? instance, MethodInfo fieldInfo)
{
_MethodInfo = fieldInfo;
_Instance = instance;
}
public Object? Invoke(params Object[] parameters)
{
return _MethodInfo.Invoke(_Instance, parameters);
}
}
public static class Methods
{
public static IEnumerable<Method> GetMethods<T>(this T instance) where T : notnull
{
return typeof(T)
.GetMethods(Instance | Static | Public | NonPublic)
.Select(mi => new Method(mi.IsStatic ? null : instance, mi));
}
public static IEnumerable<Method> OfInstance<T>(T instance) where T : notnull
{
return instance.GetMethods();
}
}

View File

@ -0,0 +1,14 @@
using System.Reflection;
namespace InnovEnergy.Lib.Utils.Reflection
{
public class Parameter
{
private readonly ParameterInfo _ParameterInfo;
internal Parameter(ParameterInfo parameterInfo) => _ParameterInfo = parameterInfo;
public Type Type => _ParameterInfo.ParameterType;
public String Name => _ParameterInfo.Name ?? "<no name>";
}
}

View File

@ -0,0 +1,87 @@
using System.Reflection;
using static System.Reflection.BindingFlags;
namespace InnovEnergy.Lib.Utils.Reflection
{
public class Property : DataMember
{
private readonly Object? _Instance;
private readonly PropertyInfo _PropertyInfo;
internal Property(Object? instance, PropertyInfo propertyInfo)
{
_Instance = instance;
_PropertyInfo = propertyInfo;
}
public override Boolean IsWriteable => _PropertyInfo.CanWrite;
public override Boolean IsReadable => _PropertyInfo.CanRead;
public override String Name => _PropertyInfo.Name;
public override Type Type => _PropertyInfo.PropertyType;
public override Boolean IsPublic => _PropertyInfo.IsPublic();
public override Boolean IsPrivate => _PropertyInfo.IsPrivate();
public override Boolean IsStatic => _PropertyInfo.IsStatic();
public override IEnumerable<Attribute> Attributes => GetAttributes<Attribute>();
public override Object? Get() => _PropertyInfo.GetValue(_Instance);
public override void Set(Object value) => _PropertyInfo.SetValue(_Instance, value);
public IEnumerable<T> GetAttributes<T> () where T : Attribute => _PropertyInfo
.GetCustomAttributes(inherit: false)
.OfType<T>();
public Boolean HasAttribute<T> () where T : Attribute => GetAttributes<T>().Any();
}
public static class Properties
{
public static IEnumerable<Property> GetProperties<T>(this T instance) where T : notnull
{
return instance
.GetType()
.GetProperties(Instance | Static | Public | NonPublic)
.Where(p => p.GetIndexParameters().Length == 0) // no indexers please
.Select(pi => new Property(pi.IsStatic() ? null : instance, pi));
}
public static IEnumerable<Property> OfInstance<T>(T instance) where T : notnull
{
return instance.GetProperties();
}
}
internal static class PropertyInfoExtensions
{
public static Boolean IsStatic(this PropertyInfo i)
{
var getter = i.GetMethod;
var setter = i.SetMethod;
return (getter is not null && getter.IsStatic)
|| (setter is not null && setter.IsStatic);
}
public static Boolean IsPublic(this PropertyInfo i)
{
var getter = i.GetMethod;
var setter = i.SetMethod;
return (getter is not null && getter.IsPublic)
|| (setter is not null && setter.IsPublic);
}
public static Boolean IsPrivate(this PropertyInfo i)
{
var getter = i.GetMethod;
var setter = i.SetMethod;
return (getter is not null && getter.IsPrivate)
|| (setter is not null && setter.IsPrivate);
}
}
}

View File

@ -23,8 +23,8 @@ public static class WebServer
listener.Start();
var handlers404 = handlers
.Concat(Default.HttpResponse)
.ToArray();
.Concat(Default.HttpResponse)
.ToArray();
while (true)
{
@ -34,9 +34,9 @@ public static class WebServer
var request = context.ConvertRequest();
var response = handlers404
.Select(f => f(request))
.SkipWhile(r => r == null)
.First()!;
.Select(f => f(request))
.SkipWhile(r => r == null)
.First()!;
context.SendResponse(response);
}

View File

@ -0,0 +1,48 @@
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
public static class DcControlRegisters
{
public const UInt16 Date = 1001;
public const UInt16 Time = 1003;
public const UInt16 IpAddress = 1005;
public const UInt16 Subnet = 1007;
public const UInt16 Gateway = 1009;
public const UInt16 ResetParamToDefault = 1011;
public const UInt16 TimeoutCommunication = 1017;
public const UInt16 RestartFlag = 1018;
public const UInt16 ConnectedSystemConfig = 1019;
public const UInt16 UpdateSwTrigger = 1027;
public const UInt16 AutomaticSwUpdate = 1028;
public const UInt16 CustomerValuesSaveReset = 1029;
public const UInt16 SerialNumberSystemControl = 2001;
public const UInt16 SerialNumberDcDc = 2003;
public const UInt16 MaterialNumberDcDc = 2005;
public const UInt16 PowerStageOperation = 4001;
public const UInt16 ResetsAlarmAndWarning = 4003;
public const UInt16 SlaveAddress = 4008;
public const UInt16 SlaveAlarmPolicy = 4009;
public const UInt16 SubSlaveAddress = 4011;
public const UInt16 ModbusSlaveId = 4012;
public const UInt16 MaximumBatteryVoltage = 4101;
public const UInt16 MinimumBatteryVoltage = 4102;
public const UInt16 MaximumBatteryVoltageR = 4103; // same as the two previous ones just Different resolution// not sure
public const UInt16 MinimumBatteryVoltageR = 4104; // same as the two previous ones just Different resolution// not sure
public const UInt16 MaximumBatteryChargingCurrent = 4107;
public const UInt16 MaximumBatteryDischargingCurrent = 4110;
public const UInt16 MaximumVoltageOfVcc = 4113;
public const UInt16 MaximumCurrentOfVcc = 4116;
public const UInt16 StartCurrentOfVcc = 4119;
public const UInt16 MaximalPowerAtDc = 4122;
public const UInt16 MaximumVoltageAlarmThreshold = 4125;
public const UInt16 MinimumVoltageAlarmThreshold = 4128;
//DcDc operation only
public const UInt16 BatteryCurrentSet = 4501;
public const UInt16 DynamicCurrentPerMillisecond = 4502;
public const UInt16 DcLinkControlMode = 4505;
public const UInt16 ReferenceVoltage = 4506;
public const UInt16 UpperVoltageWindow = 4507;
public const UInt16 LowerVoltageWindow = 4508;
public const UInt16 VoltageDeadBand = 4509;
}

View File

@ -0,0 +1,44 @@
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
public record TruConvertDcControl
{
public UInt32 Date { get; init;}
public UInt32 Time { get; init;}
public UInt32 IpAddress { get; init;} //= 0x C0A80102;
public UInt32 Subnet { get; init;} //= 0x FFFFFF00;
public UInt32 Gateway { get; init;} //= 0x C0A80102;
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 ;
public UInt16 CustomerValuesSaveReset { get; init;} = 0 ;
public UInt32 SerialNumberSystemControl { get; init;}
public UInt32 SerialNumberDcDc { get; init;}
public UInt32 MaterialNumberDcDc { get; init;}
public Boolean PowerStageEnable { get; init;} = true; //Coil
public Boolean ResetsAlarmAndWarning { get; init;} = false; //Coil
public UInt16 SlaveAddress { get; init;} = Slave.Broadcast;
public UInt16 SlaveAlarmPolicy { get; init;} = 0; // this is must be a an enum
public UInt16 SubSlaveAddress { get; init;} = 0;
public Boolean ModbusSlaveId { get; init;} = false; // Coil
public Decimal MaximumBatteryVoltage { get; init;} = 0; // resolution 0.01
public Decimal MinimumBatteryVoltage { get; init;} = 0; // resolution 0.01
public Decimal MaximumBatteryChargingCurrent { get; init;} = 0; // resolution 0.1
public Decimal MaximumBatteryDischargingCurrent { get; init;} = 0; // resolution 0.1
public Decimal MaximalPowerAtDc { get; init;} = 0;
public Decimal MaximumVoltageAlarmThreshold { get; init;} = 55; // resolution 0.1
public Decimal MinimumVoltageAlarmThreshold { get; init;} = 0; // resolution 0.1
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;} = 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);
}