This commit is contained in:
Sina Blattmann 2023-03-08 15:46:52 +01:00
commit 19978dd211
12 changed files with 165 additions and 90 deletions

View File

@ -89,19 +89,23 @@ public partial class Db : IDisposable
public IEnumerable<Installation> GetAllAccessibleInstallations(User user)
{
var direct = GetDirectlyAccessibleInstallations(user).ToList();
var direct = GetDirectlyAccessibleInstallations(user);
var fromFolders = GetAllAccessibleFolders(user)
.SelectMany(GetChildInstallations)
.Except(direct);
.SelectMany(GetChildInstallations);
return direct.Concat(fromFolders);
return direct
.Concat(fromFolders)
.Distinct();
}
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
{
return GetDirectlyAccessibleFolders(user)
.SelectMany(GetDescendantFolders);
.SelectMany(GetDescendantFolders)
.Distinct();
// Distinct because the user might have direct access
// to a child folder of a folder he has already access to
}

View File

@ -1,9 +1,6 @@
using DecimalMath;
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 DecimalMath.DecimalEx;
namespace InnovEnergy.Lib.Devices.EmuMeter;
@ -38,107 +35,60 @@ public class EmuMeterDevice
{
// Console.WriteLine("Reading Emu Meter Data");
// TODO: get SerialNb, depends on Little/Big Endian support in Modbus Lib
// var registers = Modbus.ReadHoldingRegisters(5001, 4);
// var id = registers.GetInt32(5001);
var powerCurrent = Modbus.ReadHoldingRegisters(9000, 108).ToDecimals(); // TODO "ModbusRegisters"
var voltageFreq = Modbus.ReadHoldingRegisters(9200, 112).ToDecimals(); // To check with Ivo
var energyTotal = Modbus.ReadHoldingRegisters(6000, 24) .ToUInt64s();
var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s();
var activePowerL123 = powerCurrent[0];
// var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s();
var activePowerL1 = powerCurrent[1];
var activePowerL2 = powerCurrent[2];
var activePowerL3 = powerCurrent[3];
var reactivePowerL123 = powerCurrent[5];
var reactivePowerL1 = powerCurrent[6];
var reactivePowerL2 = powerCurrent[7];
var reactivePowerL3 = powerCurrent[8];
var apparentPowerL123 = powerCurrent[10];
var apparentPowerL1 = powerCurrent[11];
var apparentPowerL2 = powerCurrent[12];
var apparentPowerL3 = powerCurrent[13];
var currentL123 = powerCurrent[50];
var currentL1 = powerCurrent[51];
var currentL2 = powerCurrent[52];
var currentL3 = powerCurrent[53];
var voltageL1N = voltageFreq[0];
var voltageL2N = voltageFreq[1];
var voltageL3N = voltageFreq[2];
var voltageL1L2 = voltageFreq[3];
var voltageL2L3 = voltageFreq[4];
var voltageL3L1 = voltageFreq[5];
var powerFactorL1 = voltageFreq[50];
var powerFactorL2 = voltageFreq[51];
var powerFactorL3 = voltageFreq[52];
var frequency = voltageFreq[55];
var energyImportL123 = energyTotal[0 / 4] / 1000.0m;
var energyExportL123 = energyTotal[20 / 4] / 1000.0m;
var energyImportL1 = energyPhases[0 / 4] / 1000.0m;
var energyExportL1 = energyPhases[20 / 4] / 1000.0m;
var energyImportL2 = energyPhases[40 / 4] / 1000.0m;
var energyExportL2 = energyPhases[60 / 4] / 1000.0m;
var energyImportL3 = energyPhases[80 / 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
// );
var l1 = new AcPhase
{
Current = currentL1,
Voltage = voltageL1N,
Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works
};
var l2 = new AcPhase
{
Current = currentL2,
Voltage = voltageL2N,
Phi = ATan2(reactivePowerL2, activePowerL2)
};
var l3 = new AcPhase
{
Current = currentL3,
Voltage = voltageL3N,
Phi = ATan2(reactivePowerL3, activePowerL3)
};
return new EmuMeterStatus
{
Ac = new Ac3Bus
{
Frequency = frequency,
L1 = new AcPhase
{
Current = currentL1,
Voltage = voltageL1N,
Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works
},
L2 = new AcPhase
{
Current = currentL2,
Voltage = voltageL2N,
Phi = ATan2(reactivePowerL2, activePowerL2)
},
L3 = new AcPhase
{
Current = currentL3,
Voltage = voltageL3N,
Phi = ATan2(reactivePowerL3, activePowerL3)
}
L1 = l1,
L2 = l2,
L3 = l3
}
};

View File

@ -4,7 +4,7 @@ namespace InnovEnergy.Lib.Devices.EmuMeter;
public record EmuMeterStatus : PowerMeterStatus
{
// TODO add serial nb, (and other?)
// TODO: additional Measurements, device id
}

View File

@ -0,0 +1,17 @@
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units.Generator;
namespace InnovEnergy.Lib.Units;
[Sum]
public readonly partial struct Energy
{
public static String Unit => "kWh";
public static String Symbol => "E";
public Energy(Decimal value) => Value = value;
public static Power operator /(Energy energy, TimeSpan timeSpan) => energy.Value * 1000m / (Decimal) timeSpan.TotalHours ;
public static Power operator /(Energy energy, UnixTimeSpan timeSpan) => energy.Value * 3_600_000m / timeSpan.Ticks;
}

View File

@ -0,0 +1,97 @@
#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 = Energy;
[JsonConverter(typeof(EnergyConverter))]
public readonly partial struct Energy
{
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 EnergyConverter : JsonConverter<Energy>
{
public override Energy Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new Energy(reader.GetDecimal());
}
public override void Write(Utf8JsonWriter writer, Energy value, JsonSerializerOptions options)
{
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
writer.WriteNumberValue(rounded);
}
}

View File

@ -1,4 +1,5 @@
using InnovEnergy.Lib.Time.Unix;
using InnovEnergy.Lib.Units.Generator;
namespace InnovEnergy.Lib.Units;
@ -15,4 +16,9 @@ public readonly partial struct Power
public static Voltage operator /(Power power, Current current) => new Voltage(power.Value / current.Value);
public static Current operator /(Power power, Voltage voltage) => new Current(power.Value / voltage.Value);
public static Energy operator *(Power power, TimeSpan timeSpan) => power.Value / 1000m * (Decimal) timeSpan.TotalHours;
public static Energy operator *(TimeSpan timeSpan, Power power) => power.Value / 1000m * (Decimal) timeSpan.TotalHours;
public static Energy operator *(Power power, UnixTimeSpan timeSpan) => power.Value * timeSpan.Ticks / 3_600_000;
public static Energy operator *(UnixTimeSpan timeSpan, Power power) => power.Value * timeSpan.Ticks / 3_600_000;
}

View File

@ -7,6 +7,7 @@
<ItemGroup>
<ProjectReference Include="../Utils/Utils.csproj" />
<ProjectReference Include="../Time/Time.csproj" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">