Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
8dd591d752
|
@ -6,9 +6,6 @@
|
||||||
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="RemoteSupportConsole.csproj.DotSettings" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliWrap" Version="3.6.0" />
|
<PackageReference Include="CliWrap" Version="3.6.0" />
|
||||||
|
|
|
@ -6,6 +6,6 @@ public static class Utils
|
||||||
{
|
{
|
||||||
public static Decimal Round3(this Decimal d)
|
public static Decimal Round3(this Decimal d)
|
||||||
{
|
{
|
||||||
return DecimalUtils.RoundToSignificantFigures(d, 3);
|
return DecimalUtils.RoundToSignificantDigits(d, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.AMPT;
|
namespace InnovEnergy.Lib.Devices.AMPT;
|
||||||
|
|
||||||
|
@ -11,5 +11,5 @@ public record AmptDeviceStatus
|
||||||
UInt32 Timestamp, // The UTC timestamp of the measurements
|
UInt32 Timestamp, // The UTC timestamp of the measurements
|
||||||
Decimal ProductionToday, // converted to kW in AmptCU class
|
Decimal ProductionToday, // converted to kW in AmptCU class
|
||||||
IReadOnlyList<DcConnection> Strings
|
IReadOnlyList<DcConnection> Strings
|
||||||
): Mppt(Dc, Strings)
|
): MpptStatus(Dc, Strings)
|
||||||
{}
|
{}
|
|
@ -1,7 +1,5 @@
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
|
@ -33,18 +31,11 @@ public class Battery48TlDevice
|
||||||
|
|
||||||
public Battery48TLStatus? ReadStatus() //Already try catch is implemented
|
public Battery48TLStatus? ReadStatus() //Already try catch is implemented
|
||||||
{
|
{
|
||||||
if (Modbus is null) // TODO : remove fake
|
|
||||||
{
|
|
||||||
Console.WriteLine("Battery is null");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console.WriteLine("Reading Battery Data");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var registers = Modbus.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters);
|
return Modbus
|
||||||
return TryReadStatus(registers);
|
.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters)
|
||||||
|
.ParseBatteryStatus();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -53,92 +44,4 @@ public class Battery48TlDevice
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Battery48TLStatus? TryReadStatus(ModbusRegisters data)
|
|
||||||
{
|
|
||||||
var soc = data.ParseDecimal(register: 1054, scaleFactor: 0.1m);
|
|
||||||
var eocReached = data.ParseEocReached();
|
|
||||||
|
|
||||||
var warnings = new List<String>();
|
|
||||||
|
|
||||||
if (data.ParseBool(1006, 1)) warnings.Add("TaM1: BMS temperature high");
|
|
||||||
if (data.ParseBool(1006, 4)) warnings.Add("TbM1: Battery temperature high");
|
|
||||||
if (data.ParseBool(1006, 6)) warnings.Add("VBm1: Bus voltage low");
|
|
||||||
if (data.ParseBool(1006, 8)) warnings.Add("VBM1: Bus voltage high");
|
|
||||||
if (data.ParseBool(1006, 10)) warnings.Add("IDM1: Discharge current high");
|
|
||||||
if (data.ParseBool(1006, 24)) warnings.Add("vsM1: String voltage high");
|
|
||||||
if (data.ParseBool(1006, 26)) warnings.Add("iCM1: Charge current high");
|
|
||||||
if (data.ParseBool(1006, 28)) warnings.Add("iDM1: Discharge current high");
|
|
||||||
if (data.ParseBool(1006, 30)) warnings.Add("MID1: String voltages unbalanced");
|
|
||||||
if (data.ParseBool(1006, 32)) warnings.Add("BLPW: Not enough charging power on bus");
|
|
||||||
if (data.ParseBool(1006, 35)) warnings.Add("Ah_W: String SOC low");
|
|
||||||
if (data.ParseBool(1006, 38)) warnings.Add("MPMM: Midpoint wiring problem");
|
|
||||||
if (data.ParseBool(1006, 39)) warnings.Add("TCMM:");
|
|
||||||
if (data.ParseBool(1006, 40)) warnings.Add("TCdi: Temperature difference between strings high");
|
|
||||||
if (data.ParseBool(1006, 41)) warnings.Add("WMTO:");
|
|
||||||
if (data.ParseBool(1006, 44)) warnings.Add("bit44:");
|
|
||||||
if (data.ParseBool(1006, 46)) warnings.Add("CELL1:");
|
|
||||||
|
|
||||||
var alarms = new List<String>();
|
|
||||||
|
|
||||||
if (data.ParseBool(1010, 0)) alarms.Add("Tam : BMS temperature too low");
|
|
||||||
if (data.ParseBool(1010, 2)) alarms.Add("TaM2 : BMS temperature too high");
|
|
||||||
if (data.ParseBool(1010, 3)) alarms.Add("Tbm : Battery temperature too low");
|
|
||||||
if (data.ParseBool(1010, 5)) alarms.Add("TbM2 : Battery temperature too high");
|
|
||||||
if (data.ParseBool(1010, 7)) alarms.Add("VBm2 : Bus voltage too low");
|
|
||||||
if (data.ParseBool(1010, 9)) alarms.Add("VBM2 : Bus voltage too high");
|
|
||||||
if (data.ParseBool(1010, 11)) alarms.Add("IDM2 : Discharge current too high");
|
|
||||||
if (data.ParseBool(1010, 12)) alarms.Add("ISOB : Electrical insulation failure");
|
|
||||||
if (data.ParseBool(1010, 13)) alarms.Add("MSWE : Main switch failure");
|
|
||||||
if (data.ParseBool(1010, 14)) alarms.Add("FUSE : Main fuse blown");
|
|
||||||
if (data.ParseBool(1010, 15)) alarms.Add("HTRE : Battery failed to warm up");
|
|
||||||
if (data.ParseBool(1010, 16)) alarms.Add("TCPE : Temperature sensor failure");
|
|
||||||
if (data.ParseBool(1010, 17)) alarms.Add("STRE :");
|
|
||||||
if (data.ParseBool(1010, 18)) alarms.Add("CME : Current sensor failure");
|
|
||||||
if (data.ParseBool(1010, 19)) alarms.Add("HWFL : BMS hardware failure");
|
|
||||||
if (data.ParseBool(1010, 20)) alarms.Add("HWEM : Hardware protection tripped");
|
|
||||||
if (data.ParseBool(1010, 21)) alarms.Add("ThM : Heatsink temperature too high");
|
|
||||||
if (data.ParseBool(1010, 22)) alarms.Add("vsm1 : String voltage too low");
|
|
||||||
if (data.ParseBool(1010, 23)) alarms.Add("vsm2 : Low string voltage failure");
|
|
||||||
if (data.ParseBool(1010, 25)) alarms.Add("vsM2 : String voltage too high");
|
|
||||||
if (data.ParseBool(1010, 27)) alarms.Add("iCM2 : Charge current too high");
|
|
||||||
if (data.ParseBool(1010, 29)) alarms.Add("iDM2 : Discharge current too high");
|
|
||||||
if (data.ParseBool(1010, 31)) alarms.Add("MID2 : String voltage unbalance too high");
|
|
||||||
if (data.ParseBool(1010, 33)) alarms.Add("CCBF : Internal charger hardware failure");
|
|
||||||
if (data.ParseBool(1010, 34)) alarms.Add("AhFL :");
|
|
||||||
if (data.ParseBool(1010, 36)) alarms.Add("TbCM :");
|
|
||||||
if (data.ParseBool(1010, 37)) alarms.Add("BRNF :");
|
|
||||||
if (data.ParseBool(1010, 42)) alarms.Add("HTFS : If Heaters Fuse Blown");
|
|
||||||
if (data.ParseBool(1010, 43)) alarms.Add("DATA : Parameters out of range");
|
|
||||||
if (data.ParseBool(1010, 45)) alarms.Add("CELL2:");
|
|
||||||
|
|
||||||
|
|
||||||
return new Battery48TLStatus(
|
|
||||||
Dc: new DcConnection
|
|
||||||
(
|
|
||||||
Voltage : data.ReadVoltage(),
|
|
||||||
Current : data.ReadCurrent()),
|
|
||||||
|
|
||||||
Soc : !eocReached && soc >= 100m ? 99.9m : soc,
|
|
||||||
Temperature : data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400),
|
|
||||||
BusVoltage : data.ParseDecimal(register: 1002, scaleFactor: 0.01m),
|
|
||||||
GreenLed : data.ParseLedState(register: 1005, led: LedColor.Green),
|
|
||||||
AmberLed : data.ParseLedState(register: 1006, led: LedColor.Amber),
|
|
||||||
BlueLed : data.ParseLedState(register: 1005, led: LedColor.Blue),
|
|
||||||
RedLed : data.ParseLedState(register: 1005, led: LedColor.Red),
|
|
||||||
Warnings : warnings,
|
|
||||||
Alarms : alarms,
|
|
||||||
MainSwitchClosed : data.ParseBool(baseRegister: 1014, bit: 0),
|
|
||||||
AlarmOutActive : data.ParseBool(baseRegister: 1014, bit: 1),
|
|
||||||
InternalFanActive : data.ParseBool(baseRegister: 1014, bit: 2),
|
|
||||||
VoltMeasurementAllowed: data.ParseBool(baseRegister: 1014, bit: 3),
|
|
||||||
AuxRelay : data.ParseBool(baseRegister: 1014, bit: 4),
|
|
||||||
RemoteState : data.ParseBool(baseRegister: 1014, bit: 5),
|
|
||||||
HeaterOn : data.ParseBool(baseRegister: 1014, bit: 6),
|
|
||||||
EocReached : eocReached,
|
|
||||||
BatteryCold : data.ParseBatteryCold(),
|
|
||||||
MaxChargingPower : data.CalcMaxChargePower(),
|
|
||||||
MaxDischargingPower : data.CalcMaxDischargePower()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,39 +1,50 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
|
using T = Battery48TLStatus;
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public record Battery48TLStatus
|
public record Battery48TLStatus : BatteryStatus
|
||||||
(
|
|
||||||
DcConnection Dc,
|
|
||||||
Decimal Soc,
|
|
||||||
Decimal Temperature,
|
|
||||||
//Decimal Current,
|
|
||||||
//Decimal Voltage,
|
|
||||||
Decimal BusVoltage,
|
|
||||||
LedState GreenLed,
|
|
||||||
LedState AmberLed,
|
|
||||||
LedState BlueLed,
|
|
||||||
LedState RedLed,
|
|
||||||
IReadOnlyList<String> Warnings,
|
|
||||||
IReadOnlyList<String> Alarms,
|
|
||||||
Boolean MainSwitchClosed,
|
|
||||||
Boolean AlarmOutActive,
|
|
||||||
Boolean InternalFanActive,
|
|
||||||
Boolean VoltMeasurementAllowed,
|
|
||||||
Boolean AuxRelay,
|
|
||||||
Boolean RemoteState,
|
|
||||||
Boolean HeaterOn,
|
|
||||||
Boolean EocReached,
|
|
||||||
Boolean BatteryCold,
|
|
||||||
Decimal MaxChargingPower,
|
|
||||||
Decimal MaxDischargingPower
|
|
||||||
)
|
|
||||||
: Battery(Dc, Soc, Temperature)
|
|
||||||
{
|
{
|
||||||
|
public Voltage CellsVoltage { get; init; }
|
||||||
|
|
||||||
|
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 State Warnings { get; init; }
|
||||||
|
public State Alarms { get; init; }
|
||||||
|
|
||||||
|
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>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: strings
|
||||||
|
// TODO
|
||||||
|
// public State LimitedBy { get; init; }
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// public Boolean AlarmOutActive { get; init; }
|
||||||
|
// public Boolean InternalFanActive { get; init; }
|
||||||
|
// public Boolean VoltMeasurementAllowed { get; init; }
|
||||||
|
// public Boolean AuxRelay { get; init; }
|
||||||
|
// public Boolean RemoteState { get; init; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
|
||||||
|
|
||||||
public static class BatteryDataParser
|
|
||||||
{
|
|
||||||
public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0)
|
|
||||||
{
|
|
||||||
var value = data[register].ConvertTo<Int32>(); // widen to 32bit signed
|
|
||||||
|
|
||||||
if (value >= 0x8000)
|
|
||||||
value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&!
|
|
||||||
|
|
||||||
return (Decimal)(value + offset) * scaleFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Decimal ReadCurrent(this ModbusRegisters data)
|
|
||||||
{
|
|
||||||
return ParseDecimal(data, register: 1001, scaleFactor: 0.01m, offset: -10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Decimal ReadVoltage(this ModbusRegisters data)
|
|
||||||
{
|
|
||||||
return ParseDecimal(data, register: 1000, scaleFactor: 0.01m);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit)
|
|
||||||
{
|
|
||||||
var x = bit / 16;
|
|
||||||
var y = bit % 16;
|
|
||||||
|
|
||||||
var value = (UInt32)data[baseRegister + x];
|
|
||||||
|
|
||||||
return (value & (1 << y)) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static LedState ParseLedState(this ModbusRegisters data, Int32 register, LedColor led)
|
|
||||||
{
|
|
||||||
var lo = ParseBool(data, register, (led.ConvertTo<Int16>() * 2).ConvertTo<Int16>());
|
|
||||||
var hi = ParseBool(data, register, (led.ConvertTo<Int16>() * 2 + 1).ConvertTo<Int16>());
|
|
||||||
|
|
||||||
if (hi)
|
|
||||||
{
|
|
||||||
if (lo)
|
|
||||||
{
|
|
||||||
return LedState.BlinkingFast;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return LedState.BlinkingSlow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (lo)
|
|
||||||
{
|
|
||||||
return LedState.On;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return LedState.Off;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static String ParseRegisters(this ModbusRegisters data, Int32 register, Int16 count)
|
|
||||||
{
|
|
||||||
var container = "";
|
|
||||||
|
|
||||||
var start = register;
|
|
||||||
var end = register + count;
|
|
||||||
|
|
||||||
for (var i = start; i < end; i++)
|
|
||||||
{
|
|
||||||
var binary = Convert.ToString(data[register], 2);
|
|
||||||
container += binary.PadLeft(16, '0');
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal 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 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;
|
|
||||||
var di = dv / rInt;
|
|
||||||
var pLimit = vLimit * (i + di);
|
|
||||||
|
|
||||||
return pLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt)
|
|
||||||
{
|
|
||||||
var di = iLimit - i;
|
|
||||||
var dv = di * rInt;
|
|
||||||
var pLimit = iLimit * (v + dv);
|
|
||||||
|
|
||||||
return pLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint)
|
|
||||||
{
|
|
||||||
// const Int32 holdZone = 300;
|
|
||||||
// const Int32 maxAllowedTemp = 315;
|
|
||||||
|
|
||||||
var kp = 0.05m;
|
|
||||||
var error = setpoint - power;
|
|
||||||
var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m));
|
|
||||||
|
|
||||||
return controlOutput;
|
|
||||||
|
|
||||||
// var a = holdZone - maxAllowedTemp;
|
|
||||||
// var b = -a * maxAllowedTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Decimal CalcMaxChargePower(this ModbusRegisters data)
|
|
||||||
{
|
|
||||||
var v = ReadVoltage(data);
|
|
||||||
var i = ReadCurrent(data);
|
|
||||||
|
|
||||||
var pLimits = new[]
|
|
||||||
{
|
|
||||||
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin),
|
|
||||||
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax),
|
|
||||||
CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin),
|
|
||||||
CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax)
|
|
||||||
};
|
|
||||||
|
|
||||||
var pLimit = pLimits.Min();
|
|
||||||
|
|
||||||
return Math.Max(pLimit, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Decimal CalcMaxDischargePower(this ModbusRegisters data)
|
|
||||||
{
|
|
||||||
var v = ReadVoltage(data);
|
|
||||||
var i = ReadCurrent(data);
|
|
||||||
var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
|
|
||||||
|
|
||||||
var pLimits = new[]
|
|
||||||
{
|
|
||||||
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin),
|
|
||||||
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax),
|
|
||||||
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin),
|
|
||||||
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax),
|
|
||||||
// CalcPowerLimitImposedByTempLimit(t,315,300)
|
|
||||||
};
|
|
||||||
|
|
||||||
var pLimit = pLimits.Max();
|
|
||||||
|
|
||||||
return Math.Min(pLimit, 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
|
public static class ModbusParser
|
||||||
|
{
|
||||||
|
internal static Battery48TLStatus ParseBatteryStatus(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
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(),
|
||||||
|
MaxChargingPower = data.CalcMaxChargePower(),
|
||||||
|
MaxDischargingPower = data.CalcMaxDischargePower(),
|
||||||
|
CellsVoltage = data.ParseCellsVoltage(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0)
|
||||||
|
{
|
||||||
|
var value = data[register].ConvertTo<Int32>(); // widen to 32bit signed
|
||||||
|
|
||||||
|
if (value >= 0x8000)
|
||||||
|
value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&!
|
||||||
|
|
||||||
|
return (Decimal)(value + offset) * scaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Decimal ParseCurrent(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit)
|
||||||
|
{
|
||||||
|
var x = bit / 16;
|
||||||
|
var y = bit % 16;
|
||||||
|
|
||||||
|
var value = (UInt32)data[baseRegister + x];
|
||||||
|
|
||||||
|
return (value & (1 << y)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LedState ParseLedState(this ModbusRegisters data, Int32 register, LedColor led)
|
||||||
|
{
|
||||||
|
var lo = data.ParseBool(register, (led.ConvertTo<Int16>() * 2 ).ConvertTo<Int16>());
|
||||||
|
var hi = data.ParseBool(register, (led.ConvertTo<Int16>() * 2 + 1).ConvertTo<Int16>());
|
||||||
|
|
||||||
|
return (hi, lo) switch
|
||||||
|
{
|
||||||
|
(false, false) => LedState.Off,
|
||||||
|
(false, true) => LedState.On,
|
||||||
|
(true, false) => LedState.BlinkingSlow,
|
||||||
|
(true, true) => LedState.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;
|
||||||
|
var di = dv / rInt;
|
||||||
|
var pLimit = vLimit * (i + di);
|
||||||
|
|
||||||
|
return pLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt)
|
||||||
|
{
|
||||||
|
var di = iLimit - i;
|
||||||
|
var dv = di * rInt;
|
||||||
|
var pLimit = iLimit * (v + dv);
|
||||||
|
|
||||||
|
return pLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint)
|
||||||
|
{
|
||||||
|
// const Int32 holdZone = 300;
|
||||||
|
// const Int32 maxAllowedTemp = 315;
|
||||||
|
|
||||||
|
var kp = 0.05m;
|
||||||
|
var error = setpoint - power;
|
||||||
|
var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m));
|
||||||
|
|
||||||
|
return controlOutput;
|
||||||
|
|
||||||
|
// var a = holdZone - maxAllowedTemp;
|
||||||
|
// var b = -a * maxAllowedTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Decimal CalcMaxChargePower(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
var v = ParseCellsVoltage(data);
|
||||||
|
var i = ParseCurrent(data);
|
||||||
|
|
||||||
|
var pLimits = new[]
|
||||||
|
{
|
||||||
|
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin),
|
||||||
|
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax),
|
||||||
|
CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin),
|
||||||
|
CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax)
|
||||||
|
};
|
||||||
|
|
||||||
|
var pLimit = pLimits.Min();
|
||||||
|
|
||||||
|
return Math.Max(pLimit, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static DcBus ParseDcBus(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Current = data.ParseCurrent(),
|
||||||
|
Voltage = data.ParseBusVoltage(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Decimal CalcMaxDischargePower(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
var v = ParseCellsVoltage(data);
|
||||||
|
var i = ParseCurrent(data);
|
||||||
|
var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400);
|
||||||
|
|
||||||
|
var pLimits = new[]
|
||||||
|
{
|
||||||
|
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin),
|
||||||
|
CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax),
|
||||||
|
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin),
|
||||||
|
CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax),
|
||||||
|
// CalcPowerLimitImposedByTempLimit(t,315,300)
|
||||||
|
};
|
||||||
|
|
||||||
|
var pLimit = pLimits.Max();
|
||||||
|
|
||||||
|
return Math.Min(pLimit, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (data.ParseBool(1010, 0)) yield return "Tam : BMS temperature too low";
|
||||||
|
if (data.ParseBool(1010, 2)) yield return "TaM2 : BMS temperature too high";
|
||||||
|
if (data.ParseBool(1010, 3)) yield return "Tbm : Battery temperature too low";
|
||||||
|
if (data.ParseBool(1010, 5)) yield return "TbM2 : Battery temperature too high";
|
||||||
|
if (data.ParseBool(1010, 7)) yield return "VBm2 : Bus voltage too low";
|
||||||
|
if (data.ParseBool(1010, 9)) yield return "VBM2 : Bus voltage too high";
|
||||||
|
if (data.ParseBool(1010, 11)) yield return "IDM2 : Discharge current too high";
|
||||||
|
if (data.ParseBool(1010, 12)) yield return "ISOB : Electrical insulation failure";
|
||||||
|
if (data.ParseBool(1010, 13)) yield return "MSWE : Main switch failure";
|
||||||
|
if (data.ParseBool(1010, 14)) yield return "FUSE : Main fuse blown";
|
||||||
|
if (data.ParseBool(1010, 15)) yield return "HTRE : Battery failed to warm up";
|
||||||
|
if (data.ParseBool(1010, 16)) yield return "TCPE : Temperature sensor failure";
|
||||||
|
if (data.ParseBool(1010, 17)) yield return "STRE :";
|
||||||
|
if (data.ParseBool(1010, 18)) yield return "CME : Current sensor failure";
|
||||||
|
if (data.ParseBool(1010, 19)) yield return "HWFL : BMS hardware failure";
|
||||||
|
if (data.ParseBool(1010, 20)) yield return "HWEM : Hardware protection tripped";
|
||||||
|
if (data.ParseBool(1010, 21)) yield return "ThM : Heatsink temperature too high";
|
||||||
|
if (data.ParseBool(1010, 22)) yield return "vsm1 : String voltage too low";
|
||||||
|
if (data.ParseBool(1010, 23)) yield return "vsm2 : Low string voltage failure";
|
||||||
|
if (data.ParseBool(1010, 25)) yield return "vsM2 : String voltage too high";
|
||||||
|
if (data.ParseBool(1010, 27)) yield return "iCM2 : Charge current too high";
|
||||||
|
if (data.ParseBool(1010, 29)) yield return "iDM2 : Discharge current too high";
|
||||||
|
if (data.ParseBool(1010, 31)) yield return "MID2 : String voltage unbalance too high";
|
||||||
|
if (data.ParseBool(1010, 33)) yield return "CCBF : Internal charger hardware failure";
|
||||||
|
if (data.ParseBool(1010, 34)) yield return "AhFL :";
|
||||||
|
if (data.ParseBool(1010, 36)) yield return "TbCM :";
|
||||||
|
if (data.ParseBool(1010, 37)) yield return "BRNF :";
|
||||||
|
if (data.ParseBool(1010, 42)) yield return "HTFS : If Heaters Fuse Blown";
|
||||||
|
if (data.ParseBool(1010, 43)) yield return "DATA : Parameters out of range";
|
||||||
|
if (data.ParseBool(1010, 45)) yield return "CELL2:";
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
||||||
|
internal static IEnumerable<String> ParseWarnings(this ModbusRegisters data)
|
||||||
|
{
|
||||||
|
if (data.ParseBool(1006, 1)) yield return "TaM1: BMS temperature high";
|
||||||
|
if (data.ParseBool(1006, 4)) yield return "TbM1: Battery temperature high";
|
||||||
|
if (data.ParseBool(1006, 6)) yield return "VBm1: Bus voltage low";
|
||||||
|
if (data.ParseBool(1006, 8)) yield return "VBM1: Bus voltage high";
|
||||||
|
if (data.ParseBool(1006, 10)) yield return "IDM1: Discharge current high";
|
||||||
|
if (data.ParseBool(1006, 24)) yield return "vsM1: String voltage high";
|
||||||
|
if (data.ParseBool(1006, 26)) yield return "iCM1: Charge current high";
|
||||||
|
if (data.ParseBool(1006, 28)) yield return "iDM1: Discharge current high";
|
||||||
|
if (data.ParseBool(1006, 30)) yield return "MID1: String voltages unbalanced";
|
||||||
|
if (data.ParseBool(1006, 32)) yield return "BLPW: Not enough charging power on bus";
|
||||||
|
if (data.ParseBool(1006, 35)) yield return "Ah_W: String SOC low";
|
||||||
|
if (data.ParseBool(1006, 38)) yield return "MPMM: Midpoint wiring problem";
|
||||||
|
if (data.ParseBool(1006, 39)) yield return "TCMM:";
|
||||||
|
if (data.ParseBool(1006, 40)) yield return "TCdi: Temperature difference between strings high";
|
||||||
|
if (data.ParseBool(1006, 41)) yield return "WMTO:";
|
||||||
|
if (data.ParseBool(1006, 44)) yield return "bit44:";
|
||||||
|
if (data.ParseBool(1006, 46)) yield return "CELL1:";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
|
using DecimalMath;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Phases;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static DecimalMath.DecimalEx;
|
using static DecimalMath.DecimalEx;
|
||||||
|
|
||||||
|
@ -29,7 +30,9 @@ public class EmuMeterDevice
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static Decimal GetPhi(Decimal cosPhi) => cosPhi.Clamp(-1m, 1m).Apply(ACos);
|
|
||||||
|
|
||||||
|
//private static Decimal GetPhi(Decimal cosPhi) => cosPhi.Clamp(-1m, 1m).Apply(ACos);
|
||||||
|
|
||||||
private EmuMeterStatus TryReadStatus()
|
private EmuMeterStatus TryReadStatus()
|
||||||
{
|
{
|
||||||
|
@ -75,45 +78,70 @@ public class EmuMeterDevice
|
||||||
var energyImportL3 = energyPhases[80 / 4] / 1000.0m;
|
var energyImportL3 = energyPhases[80 / 4] / 1000.0m;
|
||||||
var energyExportL3 = energyPhases[100 / 4] / 1000.0m;
|
var energyExportL3 = energyPhases[100 / 4] / 1000.0m;
|
||||||
|
|
||||||
|
// Ac: new Ac3Bus
|
||||||
|
// (
|
||||||
|
// new AcPhase(
|
||||||
|
// voltageL1N,
|
||||||
|
// currentL1,
|
||||||
|
// GetPhi(powerFactorL1)
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// new AcPhase(
|
||||||
|
// voltageL2N,
|
||||||
|
// currentL2,
|
||||||
|
// GetPhi(powerFactorL2)
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// new AcPhase(
|
||||||
|
// voltageL3N,
|
||||||
|
// currentL3,
|
||||||
|
// GetPhi(powerFactorL3)
|
||||||
|
// ),
|
||||||
|
// frequency
|
||||||
|
// ),
|
||||||
|
// activePowerL123,
|
||||||
|
// reactivePowerL123,
|
||||||
|
// apparentPowerL123,
|
||||||
|
// currentL123,
|
||||||
|
// voltageL1L2,
|
||||||
|
// voltageL2L3,
|
||||||
|
// voltageL3L1,
|
||||||
|
// energyImportL123,
|
||||||
|
// energyImportL1,
|
||||||
|
// energyImportL2,
|
||||||
|
// energyImportL3,
|
||||||
|
// energyExportL123,
|
||||||
|
// energyExportL1,
|
||||||
|
// energyExportL2,
|
||||||
|
// energyExportL3
|
||||||
|
// );
|
||||||
|
|
||||||
return new EmuMeterStatus
|
return new EmuMeterStatus
|
||||||
(
|
{
|
||||||
Ac: new ThreePhaseAcConnection
|
Ac = new Ac3Bus
|
||||||
(
|
{
|
||||||
new AcPhase(
|
Frequency = frequency,
|
||||||
voltageL1N,
|
L1 = new AcPhase
|
||||||
currentL1,
|
{
|
||||||
GetPhi(powerFactorL1)
|
Current = currentL1,
|
||||||
),
|
Voltage = voltageL1N,
|
||||||
|
Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works
|
||||||
new AcPhase(
|
},
|
||||||
voltageL2N,
|
L2 = new AcPhase
|
||||||
currentL2,
|
{
|
||||||
GetPhi(powerFactorL2)
|
Current = currentL2,
|
||||||
),
|
Voltage = voltageL2N,
|
||||||
|
Phi = ATan2(reactivePowerL2, activePowerL2)
|
||||||
new AcPhase(
|
},
|
||||||
voltageL3N,
|
L3 = new AcPhase
|
||||||
currentL3,
|
{
|
||||||
GetPhi(powerFactorL3)
|
Current = currentL3,
|
||||||
),
|
Voltage = voltageL3N,
|
||||||
frequency
|
Phi = ATan2(reactivePowerL3, activePowerL3)
|
||||||
),
|
}
|
||||||
activePowerL123,
|
}
|
||||||
reactivePowerL123,
|
};
|
||||||
apparentPowerL123,
|
|
||||||
currentL123,
|
|
||||||
voltageL1L2,
|
|
||||||
voltageL2L3,
|
|
||||||
voltageL3L1,
|
|
||||||
energyImportL123,
|
|
||||||
energyImportL1,
|
|
||||||
energyImportL2,
|
|
||||||
energyImportL3,
|
|
||||||
energyExportL123,
|
|
||||||
energyExportL1,
|
|
||||||
energyExportL2,
|
|
||||||
energyExportL3
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,26 +1,10 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
|
|
||||||
public record EmuMeterStatus
|
public record EmuMeterStatus : PowerMeterStatus
|
||||||
(
|
{
|
||||||
ThreePhaseAcConnection Ac,
|
// TODO add serial nb, (and other?)
|
||||||
Decimal ActivePowerL123,
|
}
|
||||||
Decimal ReactivePowerL123,
|
|
||||||
Decimal ApparentPowerL123,
|
|
||||||
Decimal CurrentL123,
|
|
||||||
Decimal VoltageL1L2,
|
|
||||||
Decimal VoltageL2L3,
|
|
||||||
Decimal VoltageL3L1,
|
|
||||||
Decimal EnergyImportL123,
|
|
||||||
Decimal EnergyImportL1,
|
|
||||||
Decimal EnergyImportL2,
|
|
||||||
Decimal EnergyImportL3,
|
|
||||||
Decimal EnergyExportL123,
|
|
||||||
Decimal EnergyExportL1,
|
|
||||||
Decimal EnergyExportL2,
|
|
||||||
Decimal EnergyExportL3
|
|
||||||
):GridMeter(Ac)
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
using InnovEnergy.Lib.StatusApi.Phases;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static DecimalMath.DecimalEx;
|
using static DecimalMath.DecimalEx;
|
||||||
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters;
|
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters;
|
||||||
|
@ -216,7 +215,7 @@ public class TruConvertAcDevice
|
||||||
|
|
||||||
return new TruConvertAcStatus
|
return new TruConvertAcStatus
|
||||||
(
|
(
|
||||||
Ac: new ThreePhaseAcConnection
|
Ac: new Ac3Bus
|
||||||
(
|
(
|
||||||
new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
|
new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
|
||||||
new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
|
new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
|
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ using WarningMessages = IReadOnlyList<WarningMessage>;
|
||||||
|
|
||||||
public record TruConvertAcStatus
|
public record TruConvertAcStatus
|
||||||
(
|
(
|
||||||
ThreePhaseAcConnection Ac,
|
Ac3Bus Ac,
|
||||||
DcConnection Dc,
|
DcConnection Dc,
|
||||||
String SerialNumber,
|
String SerialNumber,
|
||||||
MainState MainState,
|
MainState MainState,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
||||||
|
|
||||||
|
@ -8,20 +9,24 @@ using AlarmMessages = IReadOnlyList<AlarmMessage>;
|
||||||
using WarningMessages = IReadOnlyList<WarningMessage>;
|
using WarningMessages = IReadOnlyList<WarningMessage>;
|
||||||
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
|
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
|
||||||
|
|
||||||
public record TruConvertDcStatus
|
public record TruConvertDcStatus
|
||||||
(
|
(
|
||||||
DcConnection Dc,
|
DcBus DcLeft,
|
||||||
MainState MainState,
|
DcBus DcRight,
|
||||||
UInt16 NumberOfConnectedSlaves,
|
State MainState,
|
||||||
UInt16 NumberOfConnectedSubSlaves,
|
Power TotalDcPower, // TODO: necessary?
|
||||||
Decimal BatteryVoltage,
|
State StatusOfCurrentLimiting,
|
||||||
Decimal BatteryCurrent,
|
Decimal OverloadCapacity,
|
||||||
Decimal TotalDcPower,
|
Temperature DcDcInletTemperature,
|
||||||
DcCurrentLimitStates StatusOfCurrentLimiting,
|
State Alarms,
|
||||||
Decimal OverloadCapacity,
|
State Warnings,
|
||||||
Decimal DcDcInletTemperature,
|
State PowerOperation
|
||||||
AlarmMessages Alarms,
|
|
||||||
WarningMessages Warnings,
|
// UInt16 NumberOfConnectedSlaves, // TODO: necessary?
|
||||||
Boolean PowerOperation
|
// UInt16 NumberOfConnectedSubSlaves, // TODO: necessary?
|
||||||
):DcDevice(Dc)
|
) :
|
||||||
{}
|
DcDcConverterStatus(DcLeft, DcRight)
|
||||||
|
{
|
||||||
|
public static TruConvertDcStatus operator |(TruConvertDcStatus left, TruConvertDcStatus right) => OpParallel(left, right);
|
||||||
|
private static readonly Func<TruConvertDcStatus, TruConvertDcStatus, TruConvertDcStatus> OpParallel = Operators.Op<TruConvertDcStatus>("|");
|
||||||
|
}
|
|
@ -10,8 +10,8 @@ using T = BatteryStatus;
|
||||||
[OpParallel]
|
[OpParallel]
|
||||||
public partial record BatteryStatus : DeviceStatus, IDcConnection
|
public partial record BatteryStatus : DeviceStatus, IDcConnection
|
||||||
{
|
{
|
||||||
public required DcPhase Dc { get; init; }
|
public DcBus Dc { get; init; }
|
||||||
public required Percent Soc { get; init; }
|
public Percent Soc { get; init; }
|
||||||
public required Temperature Temperature { get; init; }
|
public Temperature Temperature { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IAc1Connection
|
public interface IAc1Connection
|
||||||
{
|
{
|
||||||
Ac1Phase Ac { get; }
|
Ac1Bus Ac { get; }
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IAc3Connection
|
public interface IAc3Connection
|
||||||
{
|
{
|
||||||
Ac3Phase Ac { get; }
|
Ac3Bus Ac { get; }
|
||||||
}
|
}
|
|
@ -5,5 +5,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IDcConnection
|
public interface IDcConnection
|
||||||
{
|
{
|
||||||
DcPhase Dc { get; }
|
DcBus Dc { get; }
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IPvConnection
|
public interface IPvConnection
|
||||||
{
|
{
|
||||||
IReadOnlyList<DcPhase> Strings { get; }
|
IReadOnlyList<DcBus> Strings { get; }
|
||||||
}
|
}
|
|
@ -6,8 +6,8 @@ namespace InnovEnergy.Lib.StatusApi;
|
||||||
[OpParallel]
|
[OpParallel]
|
||||||
public partial record DcDcConverterStatus : DeviceStatus
|
public partial record DcDcConverterStatus : DeviceStatus
|
||||||
{
|
{
|
||||||
public required DcPhase Left { get; init; }
|
public DcBus Left { get; init; }
|
||||||
public required DcPhase Right { get; init; }
|
public DcBus Right { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,64 @@
|
||||||
|
using System.Text.Json;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using static InnovEnergy.Lib.Units.Units;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record DeviceStatus
|
public abstract record DeviceStatus
|
||||||
{
|
{
|
||||||
|
private static readonly JsonSerializerOptions JsonSerializerOptions;
|
||||||
|
|
||||||
|
static DeviceStatus()
|
||||||
|
{
|
||||||
|
JsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
|
||||||
|
JsonConverters.ForEach(JsonSerializerOptions.Converters.Add); // how stupid is that?!!
|
||||||
|
}
|
||||||
|
|
||||||
public String DeviceType => GetType()
|
public String DeviceType => GetType()
|
||||||
.Generate(t => t.BaseType!)
|
.Generate(t => t.BaseType!)
|
||||||
.First(t => t.IsAbstract)
|
.First(t => t.IsAbstract)
|
||||||
.Name
|
.Name
|
||||||
.Replace("Status", "");
|
.Replace("Status", "");
|
||||||
}
|
|
||||||
|
public String ToJson() => JsonSerializer.Serialize(this, GetType(), JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -7,8 +7,8 @@ namespace InnovEnergy.Lib.StatusApi;
|
||||||
[OpParallel]
|
[OpParallel]
|
||||||
public partial record MpptStatus : IDcConnection, IPvConnection
|
public partial record MpptStatus : IDcConnection, IPvConnection
|
||||||
{
|
{
|
||||||
public required DcPhase Dc { get; init; }
|
public DcBus Dc { get; init; }
|
||||||
public required IReadOnlyList<DcPhase> Strings { get; init; }
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,5 @@ namespace InnovEnergy.Lib.StatusApi;
|
||||||
[OpParallel]
|
[OpParallel]
|
||||||
public partial record PowerMeterStatus : DeviceStatus, IAc3Connection
|
public partial record PowerMeterStatus : DeviceStatus, IAc3Connection
|
||||||
{
|
{
|
||||||
public required Ac3Phase Ac { get; init; }
|
public Ac3Bus Ac { get; init; }
|
||||||
}
|
}
|
|
@ -10,6 +10,6 @@ public partial record SinglePhaseInverterStatus :
|
||||||
IAc1Connection,
|
IAc1Connection,
|
||||||
IDcConnection
|
IDcConnection
|
||||||
{
|
{
|
||||||
public required Ac1Phase Ac { get; init; }
|
public Ac1Bus Ac { get; init; }
|
||||||
public required DcPhase Dc { get; init; }
|
public DcBus Dc { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ public partial record SinglePhasePvInverterStatus :
|
||||||
IAc1Connection,
|
IAc1Connection,
|
||||||
IPvConnection
|
IPvConnection
|
||||||
{
|
{
|
||||||
public required Ac1Phase Ac { get; init; }
|
public Ac1Bus Ac { get; init; }
|
||||||
public required IReadOnlyList<DcPhase> Strings { get; init; }
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="../InnovEnergy.Lib.props" />
|
<Import Project="../InnovEnergy.Lib.props" />
|
||||||
|
<!-- <Import Project="../../App/InnovEnergy.App.props" />-->
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Protocols/Modbus/Modbus.csproj" />
|
<ProjectReference Include="../Protocols/Modbus/Modbus.csproj" />
|
||||||
|
|
|
@ -10,7 +10,7 @@ public partial record ThreePhaseInverterStatus :
|
||||||
IAc3Connection,
|
IAc3Connection,
|
||||||
IDcConnection
|
IDcConnection
|
||||||
{
|
{
|
||||||
public required Ac3Phase Ac { get; init; }
|
public Ac3Bus Ac { get; init; }
|
||||||
public required DcPhase Dc { get; init; }
|
public DcBus Dc { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ public partial record ThreePhasePvInverterStatus :
|
||||||
IAc3Connection,
|
IAc3Connection,
|
||||||
IPvConnection
|
IPvConnection
|
||||||
{
|
{
|
||||||
public required Ac3Phase Ac { get; init; }
|
public Ac3Bus Ac { get; init; }
|
||||||
public required IReadOnlyList<DcPhase> Strings { get; init; }
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Angle;
|
||||||
public readonly partial struct Angle
|
public readonly partial struct Angle
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Angle
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class AngleConverter : JsonConverter<Angle>
|
||||||
|
{
|
||||||
|
public override Angle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Angle(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Angle value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = ApparentPower;
|
||||||
public readonly partial struct ApparentPower
|
public readonly partial struct ApparentPower
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct ApparentPower
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ApparentPowerConverter : JsonConverter<ApparentPower>
|
||||||
|
{
|
||||||
|
public override ApparentPower Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new ApparentPower(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, ApparentPower value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,18 @@
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
namespace InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
public record Ac1Phase : AcPhase
|
public record Ac1Bus : AcPhase
|
||||||
{
|
{
|
||||||
public required Frequency Frequency { get; init; }
|
public Frequency Frequency { get; init; }
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "RedundantCast")]
|
[SuppressMessage("ReSharper", "RedundantCast")]
|
||||||
public static Ac1Phase operator |(Ac1Phase left, Ac1Phase right)
|
public static Ac1Bus operator |(Ac1Bus left, Ac1Bus right)
|
||||||
{
|
{
|
||||||
var f = left.Frequency | right.Frequency;
|
var f = left.Frequency | right.Frequency;
|
||||||
var p = (AcPhase)left | (AcPhase)right;
|
var p = (AcPhase)left | (AcPhase)right;
|
||||||
|
|
||||||
return new Ac1Phase
|
return new Ac1Bus
|
||||||
{
|
{
|
||||||
Frequency = f,
|
Frequency = f,
|
||||||
Current = p.Current,
|
Current = p.Current,
|
||||||
|
@ -21,7 +20,6 @@ public record Ac1Phase : AcPhase
|
||||||
Phi = p.Phi
|
Phi = p.Phi
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,18 @@ using static DecimalMath.DecimalEx;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
namespace InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
public record Ac3Phase
|
public record Ac3Bus
|
||||||
{
|
{
|
||||||
public required AcPhase L1 { get; init; }
|
public AcPhase L1 { get; init; }
|
||||||
public required AcPhase L2 { get; init; }
|
public AcPhase L2 { get; init; }
|
||||||
public required AcPhase L3 { get; init; }
|
public AcPhase L3 { get; init; }
|
||||||
public required Frequency Frequency { get; init; }
|
public Frequency Frequency { get; init; }
|
||||||
|
|
||||||
public ApparentPower ApparentPower => L1.ApparentPower + L2.ApparentPower + L3.ApparentPower;
|
public ApparentPower ApparentPower => L1.ApparentPower + L2.ApparentPower + L3.ApparentPower;
|
||||||
public ReactivePower ReactivePower => L1.ReactivePower + L2.ReactivePower + L3.ReactivePower;
|
public ReactivePower ReactivePower => L1.ReactivePower + L2.ReactivePower + L3.ReactivePower;
|
||||||
public Power ActivePower => L1.ActivePower + L2.ActivePower + L3.ActivePower;
|
public Power ActivePower => L1.ActivePower + L2.ActivePower + L3.ActivePower;
|
||||||
public Angle Phi => ATan2(ReactivePower, ActivePower);
|
public Angle Phi => ATan2(ReactivePower, ActivePower);
|
||||||
|
|
||||||
public static Ac3Phase operator |(Ac3Phase left, Ac3Phase right) => OpParallel(left, right);
|
public static Ac3Bus operator |(Ac3Bus left, Ac3Bus right) => OpParallel(left, right);
|
||||||
private static readonly Func<Ac3Phase, Ac3Phase, Ac3Phase> OpParallel = "|".CreateBinaryOpForProps<Ac3Phase>();
|
private static readonly Func<Ac3Bus, Ac3Bus, Ac3Bus> OpParallel = "|".CreateBinaryOpForProps<Ac3Bus>();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,28 +3,28 @@ using static DecimalMath.DecimalEx;
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
namespace InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
|
|
||||||
public record AcPhase : IPhase
|
public record AcPhase : IBus
|
||||||
{
|
{
|
||||||
private readonly Voltage _Voltage;
|
private readonly Voltage _Voltage;
|
||||||
public required Voltage Voltage
|
public Voltage Voltage
|
||||||
{
|
{
|
||||||
get => _Voltage;
|
get => _Voltage;
|
||||||
init => _Voltage = value >= 0 ? value : throw new ArgumentException("RMS value cannot be negative");
|
init => _Voltage = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Current _Current;
|
private readonly Current _Current;
|
||||||
public required Current Current
|
public Current Current
|
||||||
{
|
{
|
||||||
get => _Current;
|
get => _Current;
|
||||||
init => _Current = value >= 0 ? value : throw new ArgumentException("RMS value cannot be negative");
|
init => _Current = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative");
|
||||||
}
|
}
|
||||||
|
|
||||||
public required Angle Phi { get; init; }
|
public Angle Phi { get; init; }
|
||||||
|
|
||||||
public ApparentPower ApparentPower => Voltage.Value * Current.Value ;
|
public ApparentPower ApparentPower => Voltage.Value * Current.Value ;
|
||||||
public Power ActivePower => ApparentPower.Value * PowerFactor;
|
public Power ActivePower => ApparentPower.Value * PowerFactor.Value;
|
||||||
public ReactivePower ReactivePower => ApparentPower.Value * Sin(Phi);
|
public ReactivePower ReactivePower => ApparentPower.Value * Sin(Phi);
|
||||||
public Decimal PowerFactor => Cos(Phi);
|
public Number PowerFactor => Cos(Phi);
|
||||||
|
|
||||||
|
|
||||||
public static AcPhase operator |(AcPhase left, AcPhase right)
|
public static AcPhase operator |(AcPhase left, AcPhase right)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
|
public record DcBus : IBus
|
||||||
|
{
|
||||||
|
public Voltage Voltage { get; init; }
|
||||||
|
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>();
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
|
||||||
|
|
||||||
public record DcPhase : IPhase
|
|
||||||
{
|
|
||||||
public required Voltage Voltage { get; init;}
|
|
||||||
public required Current Current { get; init;}
|
|
||||||
|
|
||||||
public Power Power => Current * Voltage;
|
|
||||||
|
|
||||||
public static DcPhase operator |(DcPhase left, DcPhase right) => OpParallel(left, right);
|
|
||||||
private static readonly Func<DcPhase, DcPhase, DcPhase> OpParallel = "|".CreateBinaryOpForProps<DcPhase>();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ namespace InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "MemberCanBeProtected.Global")]
|
[SuppressMessage("ReSharper", "MemberCanBeProtected.Global")]
|
||||||
|
|
||||||
public interface IPhase
|
public interface IBus
|
||||||
{
|
{
|
||||||
public Voltage Voltage { get; }
|
public Voltage Voltage { get; }
|
||||||
public Current Current { get; }
|
public Current Current { get; }
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Current;
|
||||||
public readonly partial struct Current
|
public readonly partial struct Current
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Current
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class CurrentConverter : JsonConverter<Current>
|
||||||
|
{
|
||||||
|
public override Current Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Current(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Current value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Equal
|
#define Equal
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Frequency;
|
||||||
public readonly partial struct Frequency
|
public readonly partial struct Frequency
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Frequency
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class FrequencyConverter : JsonConverter<Frequency>
|
||||||
|
{
|
||||||
|
public override Frequency Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Frequency(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Frequency value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Type
|
#define AggregationType
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Template;
|
||||||
public readonly partial struct Template
|
public readonly partial struct Template
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Template
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class TemplateConverter : JsonConverter<Template>
|
||||||
|
{
|
||||||
|
public override Template Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Template(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Template value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,5 +11,5 @@ do
|
||||||
file=$(basename -- "$path")
|
file=$(basename -- "$path")
|
||||||
class="${file%.*}"
|
class="${file%.*}"
|
||||||
echo "generating $file"
|
echo "generating $file"
|
||||||
sed "s/Template/$class/g; s/Type/$type/" "./Generator/Template.txt" > "./$class.generated.cs"
|
sed "s/Template/$class/g; s/AggregationType/$type/g" "./Generator/Template.txt" > "./$class.generated.cs"
|
||||||
done
|
done
|
|
@ -1,17 +0,0 @@
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Json;
|
|
||||||
|
|
||||||
public class CurrentConverter : JsonConverter<Current>
|
|
||||||
{
|
|
||||||
public override Current Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
return new Current(reader.GetDecimal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Current value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
writer.WriteNumberValue(value.Value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Json;
|
|
||||||
|
|
||||||
public class PowerConverter : JsonConverter<Power>
|
|
||||||
{
|
|
||||||
public override Power Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
return new Power(reader.GetDecimal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Power value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
writer.WriteNumberValue(value.Value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Json;
|
|
||||||
|
|
||||||
public class ResistanceConverter : JsonConverter<Current>
|
|
||||||
{
|
|
||||||
public override Current Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
return new Current(reader.GetDecimal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Current value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
writer.WriteNumberValue(value.Value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units.Json;
|
|
||||||
|
|
||||||
public class VoltageConverter : JsonConverter<Voltage>
|
|
||||||
{
|
|
||||||
public override Voltage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
return new Voltage(reader.GetDecimal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Voltage value, JsonSerializerOptions options)
|
|
||||||
{
|
|
||||||
writer.WriteNumberValue(value.Value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
[Sum]
|
||||||
|
public readonly partial struct Number
|
||||||
|
{
|
||||||
|
public static String Unit => "";
|
||||||
|
public static String Symbol => "";
|
||||||
|
|
||||||
|
public Number(Decimal value) => Value = value;
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
|
#define Sum
|
||||||
|
|
||||||
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
using T = Number;
|
||||||
|
|
||||||
|
public readonly partial struct Number
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
// parallel
|
||||||
|
|
||||||
|
#if Sum
|
||||||
|
|
||||||
|
public static T operator |(T left, T right) => new T(left.Value + right.Value);
|
||||||
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
|
#elif Mean
|
||||||
|
|
||||||
|
public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m);
|
||||||
|
|
||||||
|
#elif Equal
|
||||||
|
|
||||||
|
public static T operator |(T left, T right)
|
||||||
|
{
|
||||||
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
|
if (d == 0m)
|
||||||
|
return new T(0m);
|
||||||
|
|
||||||
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
|
||||||
|
const Decimal maxRelativeError = 0.05m;
|
||||||
|
|
||||||
|
if (relativeError > maxRelativeError)
|
||||||
|
throw new Exception($"{nameof(left)} and {nameof(right)} must be approximately equal.\n" +
|
||||||
|
$"Difference > {maxRelativeError * 100}% detected\n" +
|
||||||
|
$"{nameof(left)} : {left}\n" +
|
||||||
|
$"{nameof(right)}: {right}");
|
||||||
|
|
||||||
|
return new T((left.Value + right.Value) / 2m);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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 NumberConverter : JsonConverter<Number>
|
||||||
|
{
|
||||||
|
public override Number Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Number(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Number value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ public readonly struct Percent
|
||||||
public Percent(Decimal value) => Value = value;
|
public Percent(Decimal value) => Value = value;
|
||||||
|
|
||||||
// not generated
|
// not generated
|
||||||
|
// TODO: generate?
|
||||||
|
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value + Unit;
|
||||||
|
@ -20,4 +21,23 @@ public readonly struct Percent
|
||||||
|
|
||||||
// parallel
|
// parallel
|
||||||
public static Percent operator |(T left, T right) => new T((left.Value + right.Value) / 2m);
|
public static Percent operator |(T left, T right) => new T((left.Value + right.Value) / 2m);
|
||||||
|
|
||||||
|
// 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();
|
||||||
}
|
}
|
|
@ -10,7 +10,6 @@ public readonly partial struct Power
|
||||||
public static String Symbol => "P";
|
public static String Symbol => "P";
|
||||||
|
|
||||||
public Power(Decimal value) => Value = value;
|
public Power(Decimal value) => Value = value;
|
||||||
|
|
||||||
|
|
||||||
// P=UI
|
// P=UI
|
||||||
public static Voltage operator /(Power power, Current current) => new Voltage(power.Value / current.Value);
|
public static Voltage operator /(Power power, Current current) => new Voltage(power.Value / current.Value);
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Power;
|
||||||
public readonly partial struct Power
|
public readonly partial struct Power
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Power
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class PowerConverter : JsonConverter<Power>
|
||||||
|
{
|
||||||
|
public override Power Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Power(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Power value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = ReactivePower;
|
||||||
public readonly partial struct ReactivePower
|
public readonly partial struct ReactivePower
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct ReactivePower
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ReactivePowerConverter : JsonConverter<ReactivePower>
|
||||||
|
{
|
||||||
|
public override ReactivePower Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new ReactivePower(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, ReactivePower value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Resistance;
|
||||||
public readonly partial struct Resistance
|
public readonly partial struct Resistance
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Resistance
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ResistanceConverter : JsonConverter<Resistance>
|
||||||
|
{
|
||||||
|
public override Resistance Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Resistance(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Resistance value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
public readonly struct State
|
public readonly struct State : IReadOnlyList<String>
|
||||||
{
|
{
|
||||||
public IReadOnlyList<String> Values { get; }
|
public IReadOnlyList<String> Values { get; }
|
||||||
|
|
||||||
|
@ -13,13 +15,23 @@ public readonly struct State
|
||||||
}
|
}
|
||||||
|
|
||||||
public State(params String[] values) : this((IReadOnlyList<String>)values){}
|
public State(params String[] values) : this((IReadOnlyList<String>)values){}
|
||||||
public State(params State[] states) : this(states.SelectMany(s => s.Values).ToList()){}
|
public State(params State[] states) : this((IReadOnlyList<String>)states.SelectMany(s => s.Values).ToList()){}
|
||||||
|
|
||||||
public static implicit operator State(String s) => new State(s);
|
public static implicit operator State(String s) => new(s);
|
||||||
public static implicit operator State(Enum e) => new State(e.ToString());
|
public static implicit operator State(Enum e) => new(e.ToString());
|
||||||
public static implicit operator State(Boolean s) => new State(s.ToString());
|
public static implicit operator State(Boolean s) => new(s.ToString());
|
||||||
|
public static implicit operator State(List<String> s) => new((IReadOnlyList<String>)s);
|
||||||
|
public static implicit operator State(String[] s) => new((IReadOnlyList<String>)s);
|
||||||
|
public static implicit operator State(List<Enum> es) => new(es.Select(e => e.ToString()).ToList());
|
||||||
|
public static implicit operator State(Enum[] es) => new(es.Select(e => e.ToString()).ToList());
|
||||||
|
|
||||||
public static State operator |(State left, State right) => new State(left, right);
|
public static State operator |(State left, State right) => new(left, right);
|
||||||
|
|
||||||
|
public IEnumerator<String> GetEnumerator() => Values.GetEnumerator();
|
||||||
|
|
||||||
public override String ToString() => String.Join("; ", Values);
|
public override String ToString() => String.Join("; ", Values);
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
public Int32 Count => Values.Count;
|
||||||
|
public String this[Int32 index] => Values[index];
|
||||||
}
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Mean
|
#define Mean
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Temperature;
|
||||||
public readonly partial struct Temperature
|
public readonly partial struct Temperature
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Temperature
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class TemperatureConverter : JsonConverter<Temperature>
|
||||||
|
{
|
||||||
|
public override Temperature Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Temperature(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Temperature value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,27 @@
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Units.Json;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
public static class 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;
|
||||||
|
|
||||||
|
|
||||||
public const Decimal MaxRelativeError = 0.05m; // 5%
|
public const Decimal MaxRelativeError = 0.05m; // 5%
|
||||||
|
|
||||||
public static Current A (this Decimal value) => new Current(value);
|
public static Current A (this Decimal value) => new Current(value);
|
||||||
|
@ -16,14 +33,6 @@ public static class Units
|
||||||
public static Frequency Hz (this Decimal value) => new Frequency(value);
|
public static Frequency Hz (this Decimal value) => new Frequency(value);
|
||||||
public static Angle Rad (this Decimal value) => new Angle(value);
|
public static Angle Rad (this Decimal value) => new Angle(value);
|
||||||
public static Temperature Celsius(this Decimal value) => new Temperature(value);
|
public static Temperature Celsius(this Decimal value) => new Temperature(value);
|
||||||
|
|
||||||
|
|
||||||
public static readonly IReadOnlyList<JsonConverter> JsonConverters = new JsonConverter[]
|
|
||||||
{
|
|
||||||
new CurrentConverter(),
|
|
||||||
new VoltageConverter(),
|
|
||||||
new PowerConverter(),
|
|
||||||
new ResistanceConverter(),
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
};
|
|
||||||
}
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#define Equal
|
#define Equal
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ using T = Voltage;
|
||||||
public readonly partial struct Voltage
|
public readonly partial struct Voltage
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// scalar multiplication
|
||||||
|
|
||||||
|
@ -75,3 +78,19 @@ public readonly partial struct Voltage
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class VoltageConverter : JsonConverter<Voltage>
|
||||||
|
{
|
||||||
|
public override Voltage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Voltage(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Voltage value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +1,36 @@
|
||||||
using DecimalMath;
|
using DecimalMath;
|
||||||
|
using static System.Math;
|
||||||
|
using static DecimalMath.DecimalEx;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Utils;
|
namespace InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
public static class DecimalUtils
|
public static class DecimalUtils
|
||||||
{
|
{
|
||||||
public static Double RoundToSignificantFigures(this Double num, Int32 n)
|
public static Double RoundToSignificantDigits(this Double num, Int32 n)
|
||||||
{
|
{
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var d = Math.Ceiling(Math.Log10(num < 0 ? -num : num));
|
var d = Ceiling(Log10(num < 0 ? -num : num));
|
||||||
var power = n - (Int32)d;
|
var power = n - (Int32)d;
|
||||||
|
|
||||||
var magnitude = Math.Pow(10, power);
|
var magnitude = Math.Pow(10, power);
|
||||||
var shifted = Math.Round(num * magnitude);
|
var shifted = Round(num * magnitude);
|
||||||
|
|
||||||
return shifted / magnitude;
|
return shifted / magnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Decimal RoundToSignificantFigures(this Decimal num, Int32 n)
|
public static Decimal RoundToSignificantDigits(this Decimal num, Int32 n)
|
||||||
{
|
{
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var d = Math.Ceiling(DecimalEx.Log10(num < 0 ? -num : num));
|
var d = Ceiling(Log10(num < 0 ? -num : num));
|
||||||
var power = n - (Int32)d;
|
var power = n - (Int32)d;
|
||||||
|
|
||||||
var magnitude = DecimalEx.Pow(10, power);
|
var magnitude = DecimalEx.Pow(10, power);
|
||||||
var shifted = Math.Round(num * magnitude);
|
var shifted = Round(num * magnitude);
|
||||||
|
|
||||||
return shifted / magnitude;
|
return shifted / magnitude;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -106,7 +106,7 @@ public static class EnumerableUtils
|
||||||
public static IEnumerable<T> NullableToEnumerable<T>(this T? t)
|
public static IEnumerable<T> NullableToEnumerable<T>(this T? t)
|
||||||
{
|
{
|
||||||
if (t is not null)
|
if (t is not null)
|
||||||
yield return t!;
|
yield return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<(T left, T right)> Pairwise<T>(this IEnumerable<T> ts)
|
public static IEnumerable<(T left, T right)> Pairwise<T>(this IEnumerable<T> ts)
|
||||||
|
@ -246,7 +246,7 @@ public static class EnumerableUtils
|
||||||
public static IEnumerable<T> Generate<T>(this T seed, Func<T, T> next)
|
public static IEnumerable<T> Generate<T>(this T seed, Func<T, T> next)
|
||||||
{
|
{
|
||||||
var value = seed;
|
var value = seed;
|
||||||
while (true)
|
while (value is not null)
|
||||||
{
|
{
|
||||||
yield return value;
|
yield return value;
|
||||||
value = next(value);
|
value = next(value);
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
<Import Project="../../InnovEnergy.Lib.props" />
|
<Import Project="../../InnovEnergy.Lib.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -6,11 +6,6 @@
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../Utils/Utils.csproj" />
|
<ProjectReference Include="../../Utils/Utils.csproj" />
|
||||||
<ProjectReference Include="../../Time/Time.csproj" />
|
<ProjectReference Include="../../Time/Time.csproj" />
|
||||||
|
|
Loading…
Reference in New Issue