Merge remote-tracking branch 'origin/main'

# Conflicts:
#	csharp/App/Backend/Database/User.cs
#	csharp/App/Backend/Model/User.cs
This commit is contained in:
Kim 2023-03-09 12:52:47 +01:00
commit f1394e4755
8 changed files with 134 additions and 147 deletions

View File

@ -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;}
}

View File

@ -30,10 +30,25 @@ public partial class Db
public Boolean IsParentOfChild(Int64 parentId, User child) public Boolean IsParentOfChild(Int64 parentId, User child)
{ {
return child return Ancestors(child)
.Unfold(u => GetUserById(u.ParentId)) .Any(u => u.Id == parentId);
.Select(u => u.Id) }
.Contains(parentId);
private IEnumerable<User> 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); public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email);

View File

@ -5,10 +5,10 @@ namespace InnovEnergy.App.Backend.Model;
public class User : TreeNode public class User : TreeNode
{ {
[Indexed] [Indexed]
public String Email { get; set; } = ""; public String Email { get; set; } = null!;
public Boolean HasWriteAccess { get; set; } public Boolean HasWriteAccess { get; set; } = false;
public String Salt { get; set; } public String Salt { get; set; } = null!;
public String Password { get; set; } public String Password { get; set; } = null!;
// TODO: must reset pwd // TODO: must reset pwd
} }

View File

@ -1,46 +1,60 @@
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 static DecimalMath.DecimalEx;
namespace InnovEnergy.Lib.Devices.AMPT; namespace InnovEnergy.Lib.Devices.AMPT;
public class AmptCommunicationUnit public class AmptCommunicationUnit
{ {
private ModbusTcpClient Modbus { get; } private ModbusTcpClient? Modbus { get; set; }
private const Int32 RegistersPerDevice = 16; private const UInt16 RegistersPerDevice = 16;
private const Int32 FirstDeviceOffset = 85; 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) public AmptCommunicationUnit(String hostname, UInt16 port = 502, Byte slaveAddress = 1)
{ {
var connection = new ModbusTcpConnection(hostname, port); Hostname = hostname;
Modbus = new ModbusTcpClient(connection, slaveAddress); Port = port;
SlaveAddress = slaveAddress;
} }
public AmptStatus? ReadStatus() public AmptCommunicationUnitStatus? ReadStatus()
{ {
try try
{ {
return TryReadStatus(); var modbus = OpenConnection();
return TryReadStatus(modbus);
} }
catch (Exception) catch
{ {
Modbus.CloseConnection(); Modbus?.CloseConnection();
return null; 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)); private ModbusTcpClient OpenConnection()
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 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 nbrOfDevices = r.GetUInt16(78);
var devices = Enumerable var devices = Enumerable
@ -48,84 +62,45 @@ public class AmptCommunicationUnit
.Select(ReadDeviceStatus) .Select(ReadDeviceStatus)
.ToList(); .ToList();
var amptSt = new AmptStatus return new AmptCommunicationUnitStatus
(
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)
{ {
var avgVoltage = 0.0m; Sid = r.GetUInt32(1),
IdSunSpec = r.GetUInt16(3),
for (var i = 0; i < numberOfDevice; i++) 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 Dc = new DcBus
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 []
{ {
new DcConnection Voltage = r.GetUInt32((UInt16)(baseAddress + 6)) * voltageFactor,
( Current = r.GetUInt16((UInt16)(baseAddress + 5)) * currentFactor
Voltage : r.GetUInt32((UInt16)(b + 8)) * voltageFactor, },
Current : r.GetUInt16((UInt16)(b + 14)) * currentFactor Strings = new DcBus[]
), {
new DcConnection new()
( {
Voltage : r.GetUInt32((UInt16)(b + 9)) * voltageFactor, Voltage = r.GetUInt32((UInt16)(baseAddress + 8)) * voltageFactor,
Current : r.GetUInt16((UInt16)(b + 15)) * currentFactor 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,
};
} }
} }
} }

View File

@ -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<AmptStatus> Devices { get; init; } = Array.Empty<AmptStatus>();
}

View File

@ -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<DcConnection> Strings
): MpptStatus(Dc, Strings)
{}

View File

@ -1,24 +1,9 @@
using InnovEnergy.Lib.StatusApi;
using InnovEnergy.Lib.Units;
namespace InnovEnergy.Lib.Devices.AMPT; namespace InnovEnergy.Lib.Devices.AMPT;
public record AmptStatus public record AmptStatus : MpptStatus
(
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<AmptDeviceStatus> Devices
//internal const UInt16 StartRegister = 1;
//internal const UInt16 TotalNbOfRegister = 116;
)
{ {
public Energy ProductionToday { get; init; } // converted to kW in AmptCU class
} }

View File

@ -1,7 +0,0 @@
namespace InnovEnergy.Lib.Devices.AMPT;
public record AmptStringStatus
{
public Decimal Voltage { get; init; }
public Decimal Current { get; init; }
}