Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
19978dd211
|
@ -89,19 +89,23 @@ public partial class Db : IDisposable
|
||||||
|
|
||||||
public IEnumerable<Installation> GetAllAccessibleInstallations(User user)
|
public IEnumerable<Installation> GetAllAccessibleInstallations(User user)
|
||||||
{
|
{
|
||||||
var direct = GetDirectlyAccessibleInstallations(user).ToList();
|
var direct = GetDirectlyAccessibleInstallations(user);
|
||||||
var fromFolders = GetAllAccessibleFolders(user)
|
var fromFolders = GetAllAccessibleFolders(user)
|
||||||
.SelectMany(GetChildInstallations)
|
.SelectMany(GetChildInstallations);
|
||||||
.Except(direct);
|
|
||||||
|
|
||||||
return direct.Concat(fromFolders);
|
return direct
|
||||||
|
.Concat(fromFolders)
|
||||||
|
.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
||||||
{
|
{
|
||||||
return GetDirectlyAccessibleFolders(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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
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.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
using static DecimalMath.DecimalEx;
|
using static DecimalMath.DecimalEx;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
|
@ -37,108 +34,61 @@ public class EmuMeterDevice
|
||||||
private EmuMeterStatus TryReadStatus()
|
private EmuMeterStatus TryReadStatus()
|
||||||
{
|
{
|
||||||
// Console.WriteLine("Reading Emu Meter Data");
|
// 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 powerCurrent = Modbus.ReadHoldingRegisters(9000, 108).ToDecimals(); // TODO "ModbusRegisters"
|
||||||
var voltageFreq = Modbus.ReadHoldingRegisters(9200, 112).ToDecimals(); // To check with Ivo
|
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 energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s();
|
||||||
|
|
||||||
var activePowerL123 = powerCurrent[0];
|
|
||||||
var activePowerL1 = powerCurrent[1];
|
var activePowerL1 = powerCurrent[1];
|
||||||
var activePowerL2 = powerCurrent[2];
|
var activePowerL2 = powerCurrent[2];
|
||||||
var activePowerL3 = powerCurrent[3];
|
var activePowerL3 = powerCurrent[3];
|
||||||
var reactivePowerL123 = powerCurrent[5];
|
|
||||||
var reactivePowerL1 = powerCurrent[6];
|
var reactivePowerL1 = powerCurrent[6];
|
||||||
var reactivePowerL2 = powerCurrent[7];
|
var reactivePowerL2 = powerCurrent[7];
|
||||||
var reactivePowerL3 = powerCurrent[8];
|
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 currentL1 = powerCurrent[51];
|
||||||
var currentL2 = powerCurrent[52];
|
var currentL2 = powerCurrent[52];
|
||||||
var currentL3 = powerCurrent[53];
|
var currentL3 = powerCurrent[53];
|
||||||
|
|
||||||
var voltageL1N = voltageFreq[0];
|
var voltageL1N = voltageFreq[0];
|
||||||
var voltageL2N = voltageFreq[1];
|
var voltageL2N = voltageFreq[1];
|
||||||
var voltageL3N = voltageFreq[2];
|
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 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 l1 = new AcPhase
|
||||||
var energyExportL1 = energyPhases[20 / 4] / 1000.0m;
|
{
|
||||||
var energyImportL2 = energyPhases[40 / 4] / 1000.0m;
|
Current = currentL1,
|
||||||
var energyExportL2 = energyPhases[60 / 4] / 1000.0m;
|
Voltage = voltageL1N,
|
||||||
var energyImportL3 = energyPhases[80 / 4] / 1000.0m;
|
Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works
|
||||||
var energyExportL3 = energyPhases[100 / 4] / 1000.0m;
|
};
|
||||||
|
var l2 = new AcPhase
|
||||||
// Ac: new Ac3Bus
|
{
|
||||||
// (
|
Current = currentL2,
|
||||||
// new AcPhase(
|
Voltage = voltageL2N,
|
||||||
// voltageL1N,
|
Phi = ATan2(reactivePowerL2, activePowerL2)
|
||||||
// currentL1,
|
};
|
||||||
// GetPhi(powerFactorL1)
|
var l3 = new AcPhase
|
||||||
// ),
|
{
|
||||||
//
|
Current = currentL3,
|
||||||
// new AcPhase(
|
Voltage = voltageL3N,
|
||||||
// voltageL2N,
|
Phi = ATan2(reactivePowerL3, activePowerL3)
|
||||||
// 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 Ac3Bus
|
Ac = new Ac3Bus
|
||||||
{
|
{
|
||||||
Frequency = frequency,
|
Frequency = frequency,
|
||||||
L1 = new AcPhase
|
L1 = l1,
|
||||||
{
|
L2 = l2,
|
||||||
Current = currentL1,
|
L3 = l3
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
|
|
||||||
public record EmuMeterStatus : PowerMeterStatus
|
public record EmuMeterStatus : PowerMeterStatus
|
||||||
{
|
{
|
||||||
// TODO add serial nb, (and other?)
|
// TODO: additional Measurements, device id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public readonly partial struct UnixTimeSpan
|
||||||
|
|
||||||
public static UnixTimeSpan operator /(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks / b);
|
public static UnixTimeSpan operator /(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks / b);
|
||||||
public static UnixTimeSpan operator /(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks / (UInt32)b);
|
public static UnixTimeSpan operator /(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks / (UInt32)b);
|
||||||
public static UInt32 operator /(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks / b.Ticks;
|
public static UInt32 operator /(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks / b.Ticks;
|
||||||
|
|
||||||
public static UnixTimeSpan operator %(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks % b);
|
public static UnixTimeSpan operator %(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks % b);
|
||||||
public static UnixTimeSpan operator %(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b);
|
public static UnixTimeSpan operator %(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b);
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
using InnovEnergy.Lib.Time.Unix;
|
||||||
using InnovEnergy.Lib.Units.Generator;
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
@ -14,5 +15,10 @@ public readonly partial struct Power
|
||||||
// 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);
|
||||||
public static Current operator /(Power power, Voltage voltage) => new Current(power.Value / voltage.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;
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Utils/Utils.csproj" />
|
<ProjectReference Include="../Utils/Utils.csproj" />
|
||||||
|
<ProjectReference Include="../Time/Time.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
|
|
Loading…
Reference in New Issue