diff --git a/csharp/App/Backend/Database/DbConnection.cs b/csharp/App/Backend/Database/DbConnection.cs new file mode 100644 index 000000000..4676ca498 --- /dev/null +++ b/csharp/App/Backend/Database/DbConnection.cs @@ -0,0 +1,18 @@ +using InnovEnergy.App.Backend.Model; +using SQLite; + +namespace InnovEnergy.App.Backend.Database; + + +// TODO ? +public struct DbConnection +{ + public DbConnection(SQLiteConnection connection, User caller) + { + Connection = connection; + Caller = caller; + } + + public SQLiteConnection Connection { get;} + public User Caller { get;} +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 0e11edde9..28c159508 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -30,10 +30,25 @@ public partial class Db public Boolean IsParentOfChild(Int64 parentId, User child) { - return child - .Unfold(u => GetUserById(u.ParentId)) - .Select(u => u.Id) - .Contains(parentId); + return Ancestors(child) + .Any(u => u.Id == parentId); + } + + private IEnumerable Ancestors(User child) + { + return child.Unfold(GetParent); + } + + public User? GetParent(User u) + { + return IsRoot(u) + ? null + : GetUserById(u.ParentId); + } + + public static Boolean IsRoot(User u) + { + return u.ParentId == 0; // root has ParentId 0 by definition } public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); diff --git a/csharp/App/Backend/Model/User.cs b/csharp/App/Backend/Model/User.cs index 2a4346dd4..a6fd45c61 100644 --- a/csharp/App/Backend/Model/User.cs +++ b/csharp/App/Backend/Model/User.cs @@ -5,10 +5,10 @@ namespace InnovEnergy.App.Backend.Model; public class User : TreeNode { [Indexed] - public String Email { get; set; } = ""; - public Boolean HasWriteAccess { get; set; } - public String Salt { get; set; } - public String Password { get; set; } + public String Email { get; set; } = null!; + public Boolean HasWriteAccess { get; set; } = false; + public String Salt { get; set; } = null!; + public String Password { get; set; } = null!; // TODO: must reset pwd } diff --git a/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs b/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs index e701c8463..4238877ac 100644 --- a/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs +++ b/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs @@ -1,46 +1,60 @@ -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 static DecimalMath.DecimalEx; namespace InnovEnergy.Lib.Devices.AMPT; public class AmptCommunicationUnit { - private ModbusTcpClient Modbus { get; } + private ModbusTcpClient? Modbus { get; set; } - private const Int32 RegistersPerDevice = 16; - private const Int32 FirstDeviceOffset = 85; + private const UInt16 RegistersPerDevice = 16; + private const UInt16 FirstDeviceOffset = 85; + + public String Hostname { get; } + public UInt16 Port { get; } + public Byte SlaveAddress { get; } - public AmptCommunicationUnit(String hostname, UInt16 port = 502, Byte slaveAddress = 1) { - var connection = new ModbusTcpConnection(hostname, port); - Modbus = new ModbusTcpClient(connection, slaveAddress); + Hostname = hostname; + Port = port; + SlaveAddress = slaveAddress; } - public AmptStatus? ReadStatus() + public AmptCommunicationUnitStatus? ReadStatus() { try { - return TryReadStatus(); + var modbus = OpenConnection(); + return TryReadStatus(modbus); } - catch (Exception) + catch { - Modbus.CloseConnection(); + Modbus?.CloseConnection(); return null; } } - - private AmptStatus TryReadStatus() - { - // Console.WriteLine("Reading Ampt Device"); - - var r = Modbus.ReadHoldingRegisters(1, 116); - var currentFactor = DecimalEx.Pow(10.0m, r.GetInt16(73)); - var voltageFactor = DecimalEx.Pow(10.0m, r.GetInt16(74)); - var energyFactor = DecimalEx.Pow(10.0m, r.GetInt16(76) + 3); // +3 => converted from Wh to kWh + private ModbusTcpClient OpenConnection() + { + if (Modbus is null) + { + var connection = new ModbusTcpConnection(Hostname, Port); + Modbus = new ModbusTcpClient(connection, SlaveAddress); + } + + return Modbus; + } + + private static AmptCommunicationUnitStatus TryReadStatus(ModbusTcpClient modbus) + { + var r = modbus.ReadHoldingRegisters(1, 116); + + var currentFactor = Pow(10.0m, r.GetInt16(73)); + var voltageFactor = Pow(10.0m, r.GetInt16(74)); + var energyFactor = Pow(10.0m, r.GetInt16(76) + 3); // +3 => converted from Wh to kWh var nbrOfDevices = r.GetUInt16(78); var devices = Enumerable @@ -48,84 +62,45 @@ public class AmptCommunicationUnit .Select(ReadDeviceStatus) .ToList(); - var amptSt = new AmptStatus - ( - Sid : r.GetUInt32(1), - IdSunSpec : r.GetUInt16(3), - Manufacturer : r.GetString(5, 16), - Model : r.GetString(21, 16), - Version : r.GetString(45, 8), - SerialNumber : r.GetString(53, 16), - DeviceAddress : r.GetInt16(69), - IdVendor : r.GetUInt16(71), - Devices : devices - // devices.d Current1 = r.GetInt16(90) * currentFactor, - // Current2 = r.GetInt16(106) * currentFactor, - // Voltage1 = r.GetUInt32(91) * voltageFactor, - // Voltage2 = r.GetUInt32(107) * voltageFactor - - ); - - - return amptSt; - - Decimal ReadDevicesVoltage(Int32 numberOfDevice) + return new AmptCommunicationUnitStatus { - var avgVoltage = 0.0m; - - for (var i = 0; i < numberOfDevice; i++) + Sid = r.GetUInt32(1), + IdSunSpec = r.GetUInt16(3), + Manufacturer = r.GetString(5, 16), + Model = r.GetString(21, 16), + Version = r.GetString(45, 8), + SerialNumber = r.GetString(53, 16), + DeviceAddress = r.GetInt16(69), + IdVendor = r.GetUInt16(71), + Devices = devices + }; + + AmptStatus ReadDeviceStatus(Int32 deviceNumber) + { + var baseAddress = (UInt16)(FirstDeviceOffset + deviceNumber * RegistersPerDevice); // base address + + return new AmptStatus { - var b = (UInt16)(FirstDeviceOffset + i * RegistersPerDevice); // base address - - avgVoltage+= r.GetUInt32((UInt16)(b + 6)) * voltageFactor; - } - - return avgVoltage / numberOfDevice; - } - - Decimal ReadDevicesCurrent(Int32 numberOfDevice) - { - Decimal avgCurrent = 0; - - for (var i = 0; i < numberOfDevice; i++) - { - var b = (UInt16)(FirstDeviceOffset + i * RegistersPerDevice); // base address - - avgCurrent+= r!.GetUInt32((UInt16)(b + 5)) * voltageFactor; - } - - return avgCurrent / numberOfDevice; - } - - AmptDeviceStatus ReadDeviceStatus(Int32 deviceNumber) - { - var b = (UInt16)(FirstDeviceOffset + deviceNumber * RegistersPerDevice); // base address - - return new AmptDeviceStatus - ( - Dc : new DcConnection - ( - Voltage:r.GetUInt32((UInt16)(b + 6)) * voltageFactor, - Current:r.GetUInt16((UInt16)(b + 5)) * currentFactor - ), - DeviceId : r.GetInt16 (b) , - Timestamp : r.GetUInt32((UInt16)(b + 3)), - ProductionToday : r.GetUInt32((UInt16)(b + 12))* energyFactor, - - Strings : new [] + Dc = new DcBus { - new DcConnection - ( - Voltage : r.GetUInt32((UInt16)(b + 8)) * voltageFactor, - Current : r.GetUInt16((UInt16)(b + 14)) * currentFactor - ), - new DcConnection - ( - Voltage : r.GetUInt32((UInt16)(b + 9)) * voltageFactor, - Current : r.GetUInt16((UInt16)(b + 15)) * currentFactor - ) - } - ); + Voltage = r.GetUInt32((UInt16)(baseAddress + 6)) * voltageFactor, + Current = r.GetUInt16((UInt16)(baseAddress + 5)) * currentFactor + }, + Strings = new DcBus[] + { + new() + { + Voltage = r.GetUInt32((UInt16)(baseAddress + 8)) * voltageFactor, + Current = r.GetUInt16((UInt16)(baseAddress + 14)) * currentFactor + }, + new() + { + Voltage = r.GetUInt32((UInt16)(baseAddress + 9)) * voltageFactor, + Current = r.GetUInt16((UInt16)(baseAddress + 15)) * currentFactor + } + }, + ProductionToday = r.GetUInt32((UInt16)(baseAddress + 12)) * energyFactor, + }; } } } \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/AmptCommunicationUnitStatus.cs b/csharp/Lib/Devices/AMPT/AmptCommunicationUnitStatus.cs new file mode 100644 index 000000000..757d3592f --- /dev/null +++ b/csharp/Lib/Devices/AMPT/AmptCommunicationUnitStatus.cs @@ -0,0 +1,16 @@ +namespace InnovEnergy.Lib.Devices.AMPT; + +public record AmptCommunicationUnitStatus +{ + public UInt32 Sid { get; init; } // A well-known value 0x53756e53, uniquely identifies this as a SunSpec Modbus Map + public UInt16 IdSunSpec { get; init; } // A well-known value 1, uniquely identifies this as a SunSpec Common Model + + public String Manufacturer { get; init; } = "undefined"; // A well-known value registered with SunSpec for compliance: "Ampt" + public String Model { get; init; } = "undefined"; // Manufacturer specific value "Communication Unit" + public String Version { get; init; } = "undefined"; // Software Version + public String SerialNumber { get; init; } = "undefined"; // Manufacturer specific value + public Int16 DeviceAddress { get; init; } // Modbus Device ID + public UInt16 IdVendor { get; init; } // Ampt SunSpec Vendor Code 64050 + + public IReadOnlyList Devices { get; init; } = Array.Empty(); +} \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs b/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs deleted file mode 100644 index 8ee809b72..000000000 --- a/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs +++ /dev/null @@ -1,15 +0,0 @@ -using InnovEnergy.Lib.StatusApi; -using InnovEnergy.Lib.StatusApi.Connections; - -namespace InnovEnergy.Lib.Devices.AMPT; - -public record AmptDeviceStatus -( - DcConnection Dc, - // UInt16 NbrOfStrings, - Int16 DeviceId, // The string number - UInt32 Timestamp, // The UTC timestamp of the measurements - Decimal ProductionToday, // converted to kW in AmptCU class - IReadOnlyList Strings -): MpptStatus(Dc, Strings) -{} \ No newline at end of file diff --git a/csharp/Lib/Devices/AMPT/AmptStatus.cs b/csharp/Lib/Devices/AMPT/AmptStatus.cs index 45ff33662..8e2140bdc 100644 --- a/csharp/Lib/Devices/AMPT/AmptStatus.cs +++ b/csharp/Lib/Devices/AMPT/AmptStatus.cs @@ -1,24 +1,9 @@ +using InnovEnergy.Lib.StatusApi; +using InnovEnergy.Lib.Units; + namespace InnovEnergy.Lib.Devices.AMPT; -public record AmptStatus -( - UInt32 Sid, // A well-known value 0x53756e53, uniquely identifies this as a SunSpec Modbus Map - UInt16 IdSunSpec, // A well-known value 1, uniquely identifies this as a SunSpec Common Model - // UInt16 L, // Well-known # of 16-bit registers to follow : 66 - String? Manufacturer, // A well-known value registered with SunSpec for compliance: "Ampt" - String? Model, // Manufacturer specific value "Communication Unit" - String? Version, // Software Version - String? SerialNumber, // Manufacturer specific value - Int16 DeviceAddress, // Modbus Device ID - UInt16 IdVendor, // Ampt SunSpec Vendor Code 64050 - // Decimal Current1, - // Decimal Current2, - // Decimal Voltage1, - // Decimal Voltage2, - IReadOnlyList Devices - //internal const UInt16 StartRegister = 1; - //internal const UInt16 TotalNbOfRegister = 116; -) +public record AmptStatus : MpptStatus { - -} \ No newline at end of file + public Energy ProductionToday { get; init; } // converted to kW in AmptCU class +} diff --git a/csharp/Lib/Devices/AMPT/AmptStringStatus.cs b/csharp/Lib/Devices/AMPT/AmptStringStatus.cs deleted file mode 100644 index bd3b2c5e0..000000000 --- a/csharp/Lib/Devices/AMPT/AmptStringStatus.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace InnovEnergy.Lib.Devices.AMPT; - -public record AmptStringStatus -{ - public Decimal Voltage { get; init; } - public Decimal Current { get; init; } -} \ No newline at end of file