From d13e7547c58f995843e3bebdf2a91bb988f6ef74 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 8 Mar 2023 16:14:22 +0100 Subject: [PATCH 01/16] Remove Energy related measurements from EmuMeterDriver for the time being --- csharp/App/EmuMeterDriver/Config.cs | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/csharp/App/EmuMeterDriver/Config.cs b/csharp/App/EmuMeterDriver/Config.cs index ee0ad64db..0b5182735 100644 --- a/csharp/App/EmuMeterDriver/Config.cs +++ b/csharp/App/EmuMeterDriver/Config.cs @@ -20,32 +20,32 @@ public static class Config public static readonly IReadOnlyList Signals = new Signal[] { - new(s => s.CurrentL1, "/Ac/L1/Current", "0.0 A"), - new(s => s.CurrentL2, "/Ac/L2/Current", "0.0 A"), - new(s => s.CurrentL3, "/Ac/L3/Current", "0.0 A"), - new(s => s.CurrentL123, "/Ac/Current", "0.0 A"), + new(s => s.Ac.L1.Current, "/Ac/L1/Current", "0.0 A"), + new(s => s.Ac.L2.Current, "/Ac/L2/Current", "0.0 A"), + new(s => s.Ac.L3.Current, "/Ac/L3/Current", "0.0 A"), + new(s => s.Ac.L1.Current + s.Ac.L2.Current + s.Ac.L3.Current, "/Ac/Current", "0.0 A"), - new(s => s.VoltageL1N, "/Ac/L1/Voltage", "0.0 A"), - new(s => s.VoltageL2N, "/Ac/L2/Voltage", "0.0 A"), - new(s => s.VoltageL3N, "/Ac/L3/Voltage", "0.0 A"), - new(s => (s.VoltageL1N + s.VoltageL2N + s.VoltageL3N) / 3.0m, "/Ac/Voltage", "0.0 A"), + new(s => s.Ac.L1.Voltage, "/Ac/L1/Voltage", "0.0 A"), + new(s => s.Ac.L2.Voltage, "/Ac/L2/Voltage", "0.0 A"), + new(s => s.Ac.L3.Voltage, "/Ac/L3/Voltage", "0.0 A"), + new(s => (s.Ac.L1.Voltage + s.Ac.L2.Voltage + s.Ac.L3.Voltage) / 3.0m, "/Ac/Voltage", "0.0 A"), - new(s => s.ActivePowerL1, "/Ac/L1/Power", "0 W"), - new(s => s.ActivePowerL2, "/Ac/L2/Power", "0 W"), - new(s => s.ActivePowerL3, "/Ac/L3/Power", "0 W"), - new(s => s.ActivePowerL123, "/Ac/Power", "0 W"), + new(s => s.Ac.L1.ActivePower, "/Ac/L1/Power", "0 W"), + new(s => s.Ac.L2.ActivePower, "/Ac/L2/Power", "0 W"), + new(s => s.Ac.L3.ActivePower, "/Ac/L3/Power", "0 W"), + new(s => s.Ac.ActivePower, "/Ac/Power", "0 W"), - new(s => s.EnergyImportL123, "Ac/Energy/Forward", "0.00 kWh"), - new(s => s.EnergyExportL123, "Ac/Energy/Reverse", "0.00 kWh"), - - new(s => s.EnergyImportL1, "Ac/L1/Energy/Forward", "0.00 kWh"), - new(s => s.EnergyExportL1, "Ac/L1/Energy/Reverse", "0.00 kWh"), - - new(s => s.EnergyImportL2, "Ac/L2/Energy/Forward", "0.00 kWh"), - new(s => s.EnergyExportL2, "Ac/L2/Energy/Reverse", "0.00 kWh"), - - new(s => s.EnergyImportL3, "Ac/L3/Energy/Forward", "0.00 kWh"), - new(s => s.EnergyExportL3, "Ac/L3/Energy/Reverse", "0.00 kWh"), + // new(s => s.EnergyImportL123, "Ac/Energy/Forward", "0.00 kWh"), + // new(s => s.EnergyExportL123, "Ac/Energy/Reverse", "0.00 kWh"), + // + // new(s => s.EnergyImportL1, "Ac/L1/Energy/Forward", "0.00 kWh"), + // new(s => s.EnergyExportL1, "Ac/L1/Energy/Reverse", "0.00 kWh"), + // + // new(s => s.EnergyImportL2, "Ac/L2/Energy/Forward", "0.00 kWh"), + // new(s => s.EnergyExportL2, "Ac/L2/Energy/Reverse", "0.00 kWh"), + // + // new(s => s.EnergyImportL3, "Ac/L3/Energy/Forward", "0.00 kWh"), + // new(s => s.EnergyExportL3, "Ac/L3/Energy/Reverse", "0.00 kWh"), }; public static VeProperties DefaultProperties => new VeProperties From 65624cf1aa6143dde0dd125bc847f74d8458b9c0 Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 09:05:59 +0100 Subject: [PATCH 02/16] added dynamic s3 key generation --- csharp/App/Backend/Database/User.cs | 77 ++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index ea2344aaf..3f8fef76a 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -1,14 +1,17 @@ using System.Diagnostics.CodeAnalysis; +using System.Net.Http.Headers; using System.Net.Mail; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; +using System.Text.RegularExpressions; using Flurl.Http; using Innovenergy.Backend.Model; using Innovenergy.Backend.Utils; using InnovEnergy.Lib.Utils; using SQLite; +using ResponseExtensions = Flurl.Http.ResponseExtensions; #pragma warning disable CS0472 #pragma warning disable CS8602 @@ -59,11 +62,42 @@ public partial class Db return Create(user); } - [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] + + private static Byte[] HmacSha256Digest(String message, String secret) + { + var encoding = new UTF8Encoding(); + var keyBytes = encoding.GetBytes(secret); + var messageBytes = encoding.GetBytes(message); + var cryptographer = new HMACSHA256(keyBytes); + + var bytes = cryptographer.ComputeHash(messageBytes); + return bytes; + } + + public String BuildSignature(String method, String path, String data, Int64 time, String secret) + { + var messageToSign = ""; + messageToSign += method + " /v2/" + path + "\n"; + messageToSign += data + "\n"; + + // query strings + messageToSign += "\n"; + // headers + messageToSign += "\n"; + + messageToSign += time; + + Console.WriteLine("Message to sign:\n" + messageToSign); + + + var hmac = HmacSha256Digest(messageToSign, secret); + return Convert.ToBase64String(hmac); + } public Object CreateAndSaveUserS3ApiKey(User user) { //EXOSCALE API URL - const String url = "https://api-ch-dk-2.exoscale.com/v2/access-key"; + const String url = "https://api-ch-dk-2.exoscale.com/v2/"; + const String path = "access-key"; const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; @@ -80,27 +114,32 @@ public partial class Db instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); } - var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "getObject", "listBucket" }, ["content"] = instList}; - var expiration = DateTime.Now.AddSeconds(60); + var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; + var stringPayload = jsonPayload.ToJsonString(); - var signature = $"POST /v2/access-key\n{jsonPayload}\n\n\n{((DateTimeOffset)expiration).ToUnixTimeSeconds()}"; - using var hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); - - signature = Encoding.UTF8 - .GetBytes(signature) - .Apply(hmacSha256.ComputeHash) - .Apply(Convert.ToBase64String); + var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; + var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret); - var keyJson = url - .WithHeader("Authorization", - $"EXO2-HMAC-SHA256 credential={apiKey},expires={((DateTimeOffset)expiration).ToUnixTimeSeconds()},signature={signature}"); + var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature; + var client = new HttpClient(); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); + + var content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); + + + var response = client.PostAsync(url+path, content).Result; + + if (response.StatusCode.ToString() != "OK") + { + return response; + } + + var responseString = response.Content.ReadAsStringAsync().Result; + var newKey = Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); + return SetUserS3ApiKey(user, newKey); - var result = keyJson.PostJsonAsync(jsonPayload.ToString()) - .ReceiveJson() - .Result; - return result; - // return SetUserS3ApiKey(user, keyJson.GetValue("key")); } public Result SetUserS3ApiKey(User user, String key) From 4812e371b0351e50608e5a84551a2f1c4f4e3d62 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 10:52:31 +0100 Subject: [PATCH 03/16] Rewrite IsParentOfChild --- csharp/App/Backend/Database/Folder.cs | 1 - csharp/App/Backend/Database/User.cs | 15 ++++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/csharp/App/Backend/Database/Folder.cs b/csharp/App/Backend/Database/Folder.cs index 8a4785639..0867b4e50 100644 --- a/csharp/App/Backend/Database/Folder.cs +++ b/csharp/App/Backend/Database/Folder.cs @@ -26,7 +26,6 @@ public partial class Db return Folders.Where(f => f.ParentId == parent.Id); } - public IEnumerable GetDescendantFolders(Folder parent) { return parent.Traverse(GetChildFolders); diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 682ecbc5b..22a65a0bd 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -30,17 +30,10 @@ public partial class Db public Boolean IsParentOfChild(User parent, User child) { - var parentPointer = child.ParentId; - - if (parent.Id == child.Id) - return true; - - while (parentPointer != null && parentPointer != parent.Id) - { - parentPointer = GetUserById(parentPointer).ParentId; - } - - return parentPointer == parent.Id; + return child + .Unfold(u => GetUserById(u.ParentId)) + .Select(u => u.Id) + .Contains(parent.Id); } public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); From 7295e6482b393e748455c08a37a6d137b73788e7 Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 10:54:30 +0100 Subject: [PATCH 04/16] changes to s3 bucket keys generation --- csharp/App/Backend/Database/User.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 22a65a0bd..7f9e33fbc 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -90,6 +90,8 @@ public partial class Db //EXOSCALE API URL const String url = "https://api-ch-dk-2.exoscale.com/v2/"; const String path = "access-key"; + + //TODO HIDE ME const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; From 27b24b44a0aeaadaaeaa48db154503d4d190f589 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 10:58:08 +0100 Subject: [PATCH 05/16] Rewrite IsParentOfChild even shorter :P --- csharp/App/Backend/Database/User.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 22a65a0bd..3782a36d0 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -31,9 +31,13 @@ public partial class Db public Boolean IsParentOfChild(User parent, User child) { return child - .Unfold(u => GetUserById(u.ParentId)) - .Select(u => u.Id) - .Contains(parent.Id); + .Unfold(GetParent) + .Any(u => u.Id == parent.Id); + } + + public User? GetParent(User u) + { + return GetUserById(u.ParentId); } public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); From ba79b2d16f92f058bbd57e5350f3d3f63a4e550e Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 10:58:55 +0100 Subject: [PATCH 06/16] Tentatively create struct DbConnection, not used yet. --- csharp/App/Backend/Database/DbConnection.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 csharp/App/Backend/Database/DbConnection.cs 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 From 99ef91b7be8b10d91faef0c44609e672f93cb5e0 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 11:01:48 +0100 Subject: [PATCH 07/16] Use safe defaults for User (fail early) --- csharp/App/Backend/Model/User.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/App/Backend/Model/User.cs b/csharp/App/Backend/Model/User.cs index 762c819a4..b206423c2 100644 --- a/csharp/App/Backend/Model/User.cs +++ b/csharp/App/Backend/Model/User.cs @@ -5,11 +5,11 @@ namespace InnovEnergy.App.Backend.Model; public class User : TreeNode { [Indexed] - public String Email { get; set; } = ""; - public Boolean HasWriteAccess { get; set; } - public String S3Key { 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 S3Key { get; set; } = null!; + public String Salt { get; set; } = null!; + public String Password { get; set; } = null!; // TODO: must reset pwd } From 8935353e38291541b7e82648e23c00dd5dd3294c Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 11:12:56 +0100 Subject: [PATCH 08/16] Introduce Ancestors, GetParent --- csharp/App/Backend/Database/User.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 3782a36d0..081a162a2 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -30,14 +30,25 @@ public partial class Db public Boolean IsParentOfChild(User parent, User child) { - return child - .Unfold(GetParent) + return Ancestors(child) .Any(u => u.Id == parent.Id); } + private IEnumerable Ancestors(User child) + { + return child.Unfold(GetParent); + } + public User? GetParent(User u) { - return GetUserById(u.ParentId); + 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); From 932f8bdac43924821861060d65c37f511e02ce89 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 12:40:47 +0100 Subject: [PATCH 09/16] lift AMPT to new StatusApi --- .../Lib/Devices/AMPT/AmptCommunicationUnit.cs | 167 ++++++++---------- .../AMPT/AmptCommunicationUnitStatus.cs | 16 ++ csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs | 15 -- csharp/Lib/Devices/AMPT/AmptStatus.cs | 27 +-- csharp/Lib/Devices/AMPT/AmptStringStatus.cs | 7 - 5 files changed, 93 insertions(+), 139 deletions(-) create mode 100644 csharp/Lib/Devices/AMPT/AmptCommunicationUnitStatus.cs delete mode 100644 csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs delete mode 100644 csharp/Lib/Devices/AMPT/AmptStringStatus.cs 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 From d04ebffc76a229ede43c4e3549a4abd907a7b43a Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 12:50:21 +0100 Subject: [PATCH 10/16] added installation specific s3 keys --- csharp/App/Backend/Controllers/Controller.cs | 52 ++++++++---- csharp/App/Backend/Database/Db.cs | 8 +- csharp/App/Backend/Database/Installation.cs | 1 + csharp/App/Backend/Database/User.cs | 88 ++++++++++++++------ csharp/App/Backend/Model/Installation.cs | 1 + csharp/App/Backend/Model/User.cs | 1 - 6 files changed, 108 insertions(+), 43 deletions(-) diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 9136ac151..484039384 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -58,18 +58,26 @@ public class Controller [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] - [HttpPost($"{nameof(UpdateS3Credentials)}")] - public Object UpdateS3Credentials() + [HttpGet($"{nameof(GetInstallationS3Key)}")] + public Object GetInstallationS3Key(Int64 installationId) { - // TODO: S3Credentials should be per session, not per user - var caller = GetCaller(); if (caller is null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); + + var installation = db + .GetAllAccessibleInstallations(caller) + .FirstOrDefault(i => i.Id == installationId); - return db.CreateAndSaveUserS3ApiKey(caller); + if(installation == null) + { + return new HttpResponseMessage(HttpStatusCode.Unauthorized); + } + + var key = db.GetInstallationS3Key(installationId); + return key ?? db.CreateAndSaveInstallationS3ApiKey(installation); } @@ -229,10 +237,9 @@ public class Controller // TODO: distinguish between create and update var caller = GetCaller(); - if (caller == null) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); - using var db = Db.Connect(); + if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser)) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); return db.GetUserById(updatedUser.Id) != null ? db.UpdateUser(updatedUser) @@ -252,15 +259,21 @@ public class Controller using var db = Db.Connect(); - var hasAccessToInstallation = db + var installationFromAccessibleInstallations = db .GetAllAccessibleInstallations(caller) - .Any(i => i.Id == installation.Id); + .FirstOrDefault(i => i.Id == installation.Id); - if (!hasAccessToInstallation) + if (installationFromAccessibleInstallations == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); // TODO: accessibility by other users etc // TODO: sanity check changes + // foreach(var property in installationFromAccessibleInstallations.GetType().GetProperties()){ + // if(installation.GetType().GetProperties().Contains(property)) + // { + // property.SetValue(installationFromAccessibleInstallations, property.GetValue(installation)); + // } + // } return db.UpdateInstallation(installation); } @@ -278,17 +291,24 @@ public class Controller using var db = Db.Connect(); - var hasAccessToFolder = db + var installationFromAccessibleFolders = db .GetAllAccessibleFolders(caller) - .Any(f => f.Id == folder.Id); + .FirstOrDefault(f => f.Id == folder.Id); - if (!hasAccessToFolder) + if (installationFromAccessibleFolders == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); // TODO: accessibility by other users etc // TODO: sanity check changes - - return db.UpdateFolder(folder); + + // foreach(var property in installationFromAccessibleFolders.GetType().GetProperties()){ + // if(folder.GetType().GetProperties().Contains(property)) + // { + // property.SetValue(installationFromAccessibleFolders, property.GetValue(folder)); + // } + // } + + return db.UpdateFolder(installationFromAccessibleFolders); } [Returns(HttpStatusCode.OK)] diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 5c87cc9c8..95afb8bc2 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -209,6 +209,12 @@ public partial class Db : IDisposable return Result.Ok; } - + + public Object? GetInstallationS3Key(Int64 installationId) + { + return Installations + .Where(installation => installation.Id == installationId) + .Select(installation => installation.S3Key); + } } diff --git a/csharp/App/Backend/Database/Installation.cs b/csharp/App/Backend/Database/Installation.cs index 6e554f483..e02ad9277 100644 --- a/csharp/App/Backend/Database/Installation.cs +++ b/csharp/App/Backend/Database/Installation.cs @@ -31,5 +31,6 @@ public partial class Db return Delete(installation); } + } diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 7f9e33fbc..0e11edde9 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -28,12 +28,12 @@ public partial class Db return Users.FirstOrDefault(u => u.Id == id); } - public Boolean IsParentOfChild(User parent, User child) + public Boolean IsParentOfChild(Int64 parentId, User child) { return child .Unfold(u => GetUserById(u.ParentId)) .Select(u => u.Id) - .Contains(parent.Id); + .Contains(parentId); } public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); @@ -66,7 +66,7 @@ public partial class Db return bytes; } - public String BuildSignature(String method, String path, String data, Int64 time, String secret) + private static String BuildSignature(String method, String path, String data, Int64 time, String secret) { var messageToSign = ""; messageToSign += method + " /v2/" + path + "\n"; @@ -85,7 +85,59 @@ public partial class Db var hmac = HmacSha256Digest(messageToSign, secret); return Convert.ToBase64String(hmac); } - public Object CreateAndSaveUserS3ApiKey(User user) + + // public Object CreateAndSaveUserS3ApiKey(User user) + // { + // //EXOSCALE API URL + // const String url = "https://api-ch-dk-2.exoscale.com/v2/"; + // const String path = "access-key"; + // + // //TODO HIDE ME + // const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; + // const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; + // + // var installationList = User2Installation + // .Where(i => i.UserId == user.Id) + // .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id)) + // .ToList(); + // + // + // var instList = new JsonArray(); + // + // foreach (var installation in installationList) + // { + // instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); + // } + // + // var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; + // var stringPayload = jsonPayload.ToJsonString(); + // + // var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; + // var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret); + // + // var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature; + // + // var client = new HttpClient(); + // client.DefaultRequestHeaders.Authorization = + // new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader); + // + // var content = new StringContent(stringPayload, Encoding.UTF8, "application/json"); + // + // + // var response = client.PostAsync(url+path, content).Result; + // + // if (response.StatusCode.ToString() != "OK") + // { + // return response; + // } + // + // var responseString = response.Content.ReadAsStringAsync().Result; + // return Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); + // // return SetUserS3ApiKey(user, newKey); + // + // } + + public Object CreateAndSaveInstallationS3ApiKey(Installation installation) { //EXOSCALE API URL const String url = "https://api-ch-dk-2.exoscale.com/v2/"; @@ -95,20 +147,10 @@ public partial class Db const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; - var installationList = User2Installation - .Where(i => i.UserId == user.Id) - .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id)) - .ToList(); - - var instList = new JsonArray(); - - foreach (var installation in installationList) - { - instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); - } - - var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; + instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"}); + + var jsonPayload = new JsonObject { ["name"] = installation.Id, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList}; var stringPayload = jsonPayload.ToJsonString(); var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60; @@ -132,16 +174,12 @@ public partial class Db var responseString = response.Content.ReadAsStringAsync().Result; var newKey = Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"')); - return SetUserS3ApiKey(user, newKey); - - } - public Result SetUserS3ApiKey(User user, String key) - { - user.S3Key = key; - return Update(user); + installation.S3Key = newKey; + UpdateInstallation(installation); + return newKey; } - + public Result UpdateUser(User user) { var oldUser = GetUserById(user.Id); diff --git a/csharp/App/Backend/Model/Installation.cs b/csharp/App/Backend/Model/Installation.cs index b9858e9c8..b04885b40 100644 --- a/csharp/App/Backend/Model/Installation.cs +++ b/csharp/App/Backend/Model/Installation.cs @@ -14,6 +14,7 @@ public class Installation : TreeNode public Double Long { get; set; } public String S3Bucket { get; set; } = ""; + public String S3Key { get; set; } } diff --git a/csharp/App/Backend/Model/User.cs b/csharp/App/Backend/Model/User.cs index 762c819a4..2a4346dd4 100644 --- a/csharp/App/Backend/Model/User.cs +++ b/csharp/App/Backend/Model/User.cs @@ -7,7 +7,6 @@ public class User : TreeNode [Indexed] public String Email { get; set; } = ""; public Boolean HasWriteAccess { get; set; } - public String S3Key { get; set; } public String Salt { get; set; } public String Password { get; set; } From eb73beb1cdb6b741481c03a98e9c366938155b36 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 13:16:26 +0100 Subject: [PATCH 11/16] Implement failure resilient TCP connections for Adam and AMPT --- .../Lib/Devices/AMPT/AmptCommunicationUnit.cs | 30 ++++++--- csharp/Lib/Devices/Adam6060/Adam6060Device.cs | 61 ++++++++++++++----- 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs b/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs index 4238877ac..6491f4e8b 100644 --- a/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs +++ b/csharp/Lib/Devices/AMPT/AmptCommunicationUnit.cs @@ -27,30 +27,42 @@ public class AmptCommunicationUnit { try { - var modbus = OpenConnection(); - return TryReadStatus(modbus); + OpenConnection(); + return TryReadStatus(); } - catch + catch { - Modbus?.CloseConnection(); + CloseConnection(); return null; } } - private ModbusTcpClient OpenConnection() + private void CloseConnection() + { + try + { + Modbus?.CloseConnection(); + } + catch + { + // ignored + } + + Modbus = null; + } + + private void OpenConnection() { if (Modbus is null) { var connection = new ModbusTcpConnection(Hostname, Port); Modbus = new ModbusTcpClient(connection, SlaveAddress); } - - return Modbus; } - private static AmptCommunicationUnitStatus TryReadStatus(ModbusTcpClient modbus) + private AmptCommunicationUnitStatus TryReadStatus() { - var r = modbus.ReadHoldingRegisters(1, 116); + var r = Modbus!.ReadHoldingRegisters(1, 116); var currentFactor = Pow(10.0m, r.GetInt16(73)); var voltageFactor = Pow(10.0m, r.GetInt16(74)); diff --git a/csharp/Lib/Devices/Adam6060/Adam6060Device.cs b/csharp/Lib/Devices/Adam6060/Adam6060Device.cs index 09af1a1a0..d910baf41 100644 --- a/csharp/Lib/Devices/Adam6060/Adam6060Device.cs +++ b/csharp/Lib/Devices/Adam6060/Adam6060Device.cs @@ -7,31 +7,60 @@ namespace InnovEnergy.Lib.Devices.Adam6060; public class Adam6060Device { - private ModbusTcpClient Modbus { get; } + public String Hostname { get; } + public UInt16 Port { get; } + public Byte SlaveAddress { get; } + + private ModbusTcpClient? Modbus { get; set; } public Adam6060Device(String hostname, UInt16 port = 5004, Byte slaveAddress = 2) { - var connection = new ModbusTcpConnection(hostname, port); - Modbus = new ModbusTcpClient(connection, slaveAddress); + Hostname = hostname; + Port = port; + SlaveAddress = slaveAddress; + } + + private void OpenConnection() + { + if (Modbus is null) + { + var connection = new ModbusTcpConnection(Hostname, Port); + Modbus = new ModbusTcpClient(connection, SlaveAddress); + } } public Adam6060Status? ReadStatus() { try { + OpenConnection(); return TryReadStatus(); } - catch (Exception) + catch { - Modbus.CloseConnection(); + CloseConnection(); return null; } } + private void CloseConnection() + { + try + { + Modbus?.CloseConnection(); + } + catch + { + // ignored + } + + Modbus = null; + } + private Adam6060Status TryReadStatus() { - var inputs = Modbus.ReadDiscreteInputs(DigitalInputsStartRegister, NbDigitalInputs); - var relays = Modbus.ReadDiscreteInputs(RelaysStartRegister, NbRelays); + var inputs = Modbus!.ReadDiscreteInputs(DigitalInputsStartRegister, NbDigitalInputs); + var relays = Modbus!.ReadDiscreteInputs(RelaysStartRegister, NbRelays); return new Adam6060Status { @@ -55,17 +84,19 @@ public class Adam6060Device { try { - Modbus.WriteMultipleCoils(RelaysStartRegister, control.Relay0, - control.Relay1, - control.Relay2, - control.Relay3, - control.Relay4, - control.Relay5); + OpenConnection(); + + Modbus!.WriteMultipleCoils(RelaysStartRegister, control.Relay0, + control.Relay1, + control.Relay2, + control.Relay3, + control.Relay4, + control.Relay5); return true; } - catch (Exception) + catch { - Modbus.CloseConnection(); + CloseConnection(); return false; } } From fd10522786899acd87a5239807666d626dcb1d02 Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 13:20:37 +0100 Subject: [PATCH 12/16] Added daily deletion of Installation s3 keys --- csharp/App/Backend/Database/Db.cs | 9 +++++++++ csharp/App/Backend/Database/User.cs | 2 +- csharp/App/Backend/Model/Installation.cs | 2 +- csharp/App/Backend/Program.cs | 16 +++++++++++++--- csharp/App/Backend/db.sqlite | Bin 290816 -> 294912 bytes 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 95afb8bc2..06bdbd2e7 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -216,5 +216,14 @@ public partial class Db : IDisposable .Where(installation => installation.Id == installationId) .Select(installation => installation.S3Key); } + + public void DeleteS3KeysDaily() + { + foreach (var installation in Installations.ToList()) + { + installation.S3Key = null; + Update(installation); + } + } } diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 28c159508..b07bc6b62 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -152,7 +152,7 @@ public partial class Db // // } - public Object CreateAndSaveInstallationS3ApiKey(Installation installation) + public Object? CreateAndSaveInstallationS3ApiKey(Installation installation) { //EXOSCALE API URL const String url = "https://api-ch-dk-2.exoscale.com/v2/"; diff --git a/csharp/App/Backend/Model/Installation.cs b/csharp/App/Backend/Model/Installation.cs index b04885b40..b6c4b7968 100644 --- a/csharp/App/Backend/Model/Installation.cs +++ b/csharp/App/Backend/Model/Installation.cs @@ -14,7 +14,7 @@ public class Installation : TreeNode public Double Long { get; set; } public String S3Bucket { get; set; } = ""; - public String S3Key { get; set; } + public String? S3Key { get; set; } } diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index ae662b779..7b6df34b9 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -1,3 +1,4 @@ +using System.Reactive.Linq; using InnovEnergy.App.Backend.Database; using Microsoft.OpenApi.Models; @@ -10,11 +11,13 @@ public static class Program using (var db = Db.Connect()) db.CreateFakeRelations(); + Observable.Interval(TimeSpan.FromDays(1)).Subscribe((_) => deleteInstallationS3KeysDaily()); + var builder = WebApplication.CreateBuilder(args); - + builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle - + builder.Services.AddHttpContextAccessor(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod())); @@ -34,7 +37,7 @@ public static class Program app.UseSwagger(); app.UseSwaggerUI(cfg => cfg.EnableFilter()); } - + app.UseCors(); app.UseHttpsRedirection(); app.UseAuthorization(); @@ -44,6 +47,13 @@ public static class Program app.Run(); } + private static void deleteInstallationS3KeysDaily() + { + using var db = Db.Connect(); + db.DeleteS3KeysDaily(); + + } + private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next) { var headers = ctx.Request.Headers; diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index 92600a4864c5a88b620d6123ca5b78f4b298c2b9..6b0d2d49212f4ae6cb4b3e90de9a5b1112591689 100644 GIT binary patch delta 98398 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 Date: Thu, 9 Mar 2023 14:01:57 +0100 Subject: [PATCH 13/16] Introduce State for Enums --- csharp/Lib/Units/State.cs | 9 ++++++++- csharp/Lib/Units/StateOfT.cs | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 csharp/Lib/Units/StateOfT.cs diff --git a/csharp/Lib/Units/State.cs b/csharp/Lib/Units/State.cs index 3e8e48395..88610517c 100644 --- a/csharp/Lib/Units/State.cs +++ b/csharp/Lib/Units/State.cs @@ -34,4 +34,11 @@ public readonly struct State : IReadOnlyList public Int32 Count => Values.Count; public String this[Int32 index] => Values[index]; -} \ No newline at end of file + + public static State From(T t) where T : Enum + { + return new State(t); + } +} + + diff --git a/csharp/Lib/Units/StateOfT.cs b/csharp/Lib/Units/StateOfT.cs new file mode 100644 index 000000000..4a1242dc2 --- /dev/null +++ b/csharp/Lib/Units/StateOfT.cs @@ -0,0 +1,27 @@ +using System.Collections; + +namespace InnovEnergy.Lib.Units; + +public readonly struct State : IReadOnlyList where T:Enum +{ + public IReadOnlyList Values { get; } + + public State(IReadOnlyList values) => Values = values; + + public State(params T[] values) : this((IReadOnlyList)values){} + public State(params State[] states) : this((IReadOnlyList)states.SelectMany(s => s.Values).ToList()){} + + public static implicit operator State(T s) => new((IReadOnlyList)s); + public static implicit operator State(List s) => new((IReadOnlyList)s); + public static implicit operator State(T[] s) => new((IReadOnlyList)s); + + public static State operator |(State left, State right) => new State(left, right); + + public IEnumerator GetEnumerator() => Values.GetEnumerator(); + + public override String ToString() => String.Join("; ", Values); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public Int32 Count => Values.Count; + public T this[Int32 index] => Values[index]; +} \ No newline at end of file From d885fa94c680621f64786cc2213464912870c4f6 Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 9 Mar 2023 16:32:11 +0100 Subject: [PATCH 14/16] Remove Result class; --- csharp/App/Backend/Database/Db.cs | 149 ++++++++++---------- csharp/App/Backend/Database/Folder.cs | 10 +- csharp/App/Backend/Database/Installation.cs | 6 +- csharp/App/Backend/Database/User.cs | 17 +-- csharp/App/Backend/Utils/Result.cs | 29 ---- 5 files changed, 86 insertions(+), 125 deletions(-) delete mode 100644 csharp/App/Backend/Utils/Result.cs diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 06bdbd2e7..39d529d93 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -10,16 +10,16 @@ namespace InnovEnergy.App.Backend.Database; public partial class Db : IDisposable { internal const String DbPath = "./db.sqlite"; - + private readonly SQLiteConnection _Db; // internal handle to the connection, disposable - + private TableQuery Sessions => _Db.Table(); - + [SuppressMessage("ReSharper", "AccessToDisposedClosure")] static Db() { // on startup create/migrate tables - + using var db = new SQLiteConnection(DbPath); db.RunInTransaction(() => @@ -31,105 +31,115 @@ public partial class Db : IDisposable db.CreateTable(); db.CreateTable(); }); - } // private, force access through Connect() private Db() => _Db = new SQLiteConnection(DbPath); - + public static Db Connect() => new Db(); public void Dispose() => _Db.Dispose(); - - + + // the C in CRUD - private Result Create(TreeNode treeNode) + private Int64 Create(TreeNode treeNode) { try { _Db.Insert(treeNode); + return SQLite3.LastInsertRowid(_Db.Handle); } catch (Exception e) { - return Result.Error(e); + return -1; } + } + + + private Boolean Create(Session session) + { + try + { + _Db.Insert(session); + return true; + } + catch (Exception e) + { + return false; + } + } - return Result.Ok; - } - // the U in CRUD - private Result Update(TreeNode treeNode) + private Boolean Update(TreeNode treeNode) { try { _Db.InsertOrReplace(treeNode); + return true; } catch (Exception e) { - return Result.Error(e); + return false; } - - return Result.Ok; } - + // the D in CRUD - private Result Delete(TreeNode treeNode) + private Boolean Delete(TreeNode treeNode) { try { _Db.Delete(treeNode); + return true; } catch (Exception e) { - return Result.Error(e); + return false; } - - return Result.Ok; } - + public IEnumerable GetAllAccessibleInstallations(User user) { - var direct = GetDirectlyAccessibleInstallations(user); + var direct = GetDirectlyAccessibleInstallations(user); var fromFolders = GetAllAccessibleFolders(user) - .SelectMany(GetChildInstallations); + .SelectMany(GetChildInstallations); return direct - .Concat(fromFolders) - .Distinct(); + .Concat(fromFolders) + .Distinct(); } - + public IEnumerable GetAllAccessibleFolders(User user) { return GetDirectlyAccessibleFolders(user) - .SelectMany(GetDescendantFolders) - .Distinct(); - + .SelectMany(GetDescendantFolders) + .Distinct(); + // Distinct because the user might have direct access // to a child folder of a folder he has already access to } - - + + public IEnumerable GetDirectlyAccessibleInstallations(User user) { return User2Installation - .Where(r => r.UserId == user.Id) - .Select(r => r.InstallationId) - .Select(GetInstallationById) - .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user + .Where(r => r.UserId == user.Id) + .Select(r => r.InstallationId) + .Select(GetInstallationById) + .NotNull() + .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user } - + public IEnumerable GetDirectlyAccessibleFolders(User user) { return User2Folder - .Where(r => r.UserId == user.Id) - .Select(r => r.FolderId) - .Select(GetFolderById) - .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user; + .Where(r => r.UserId == user.Id) + .Select(r => r.FolderId) + .Select(GetFolderById) + .NotNull() + .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user; } - - public Result AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId) + + public Boolean AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId) { var con = new User2Installation { @@ -139,17 +149,16 @@ public partial class Db : IDisposable try { - _Db.InsertOrReplace(con); + _Db.Insert(con); + return true; } catch (Exception e) { - return Result.Error(e); + return false; } - - return Result.Ok; } - - public Result AddToAccessibleFolders(Int64 userId, Int64 updatedFolderId) + + public Boolean AddToAccessibleFolders(Int64 userId, Int64 updatedFolderId) { var con = new User2Folder { @@ -159,16 +168,14 @@ public partial class Db : IDisposable try { - _Db.InsertOrReplace(con); + _Db.Insert(con); + return true; } catch (Exception e) { - return Result.Error(e); + return false; } - - return Result.Ok; } - public User? GetUserByToken(String token) @@ -182,39 +189,26 @@ public partial class Db : IDisposable } - public Result NewSession(Session ses) - { - try - { - _Db.InsertOrReplace(ses); - } - catch (Exception e) - { - return Result.Error(e); - } + public Boolean NewSession(Session ses) => Create(ses); - return Result.Ok; - } - - public Result DeleteSession(Int64 id) + public Boolean DeleteSession(Int64 id) { try { Sessions.Delete(u => u.UserId == id); + return true; } catch (Exception e) { - return Result.Error(e); + return false; } - - return Result.Ok; } public Object? GetInstallationS3Key(Int64 installationId) { return Installations - .Where(installation => installation.Id == installationId) - .Select(installation => installation.S3Key); + .Where(installation => installation.Id == installationId) + .Select(installation => installation.S3Key); } public void DeleteS3KeysDaily() @@ -225,5 +219,4 @@ public partial class Db : IDisposable Update(installation); } } -} - +} \ No newline at end of file diff --git a/csharp/App/Backend/Database/Folder.cs b/csharp/App/Backend/Database/Folder.cs index 0867b4e50..1204ed06c 100644 --- a/csharp/App/Backend/Database/Folder.cs +++ b/csharp/App/Backend/Database/Folder.cs @@ -46,31 +46,31 @@ public partial class Db return parent.Traverse(GetChildUsers); } - public Result CreateFolder(Folder folder) + public Int64 CreateFolder(Folder folder) { return Create(folder); } - public Result UpdateFolder(Folder folder) + public Boolean UpdateFolder(Folder folder) { // TODO: no circles in path return Update(folder); } - public Result ChangeParent(Installation child, Int64 parentId) + public Boolean ChangeParent(Installation child, Int64 parentId) { child.ParentId = parentId; return UpdateInstallation(child); } - public Result ChangeParent(Folder child, Int64 parentId) + public Boolean ChangeParent(Folder child, Int64 parentId) { child.ParentId = parentId; return UpdateFolder(child); } - public Result DeleteFolder(Folder folder) + public Boolean DeleteFolder(Folder folder) { // Delete direct children User2Folder .Delete(f => f.FolderId == folder.Id); diff --git a/csharp/App/Backend/Database/Installation.cs b/csharp/App/Backend/Database/Installation.cs index e02ad9277..cae033e78 100644 --- a/csharp/App/Backend/Database/Installation.cs +++ b/csharp/App/Backend/Database/Installation.cs @@ -14,18 +14,18 @@ public partial class Db public Installation? GetInstallationById(Int64 id) => Installations .FirstOrDefault(u => u.Id == id); - public Result CreateInstallation(Installation installation) + public Int64 CreateInstallation(Installation installation) { return Create(installation); } - public Result UpdateInstallation(Installation installation) + public Boolean UpdateInstallation(Installation installation) { return Update(installation); } - public Result DeleteInstallation(Installation installation) + public Boolean DeleteInstallation(Installation installation) { User2Installation.Delete(i => i.InstallationId == installation.Id); diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index b07bc6b62..980786076 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -1,16 +1,13 @@ -using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Net.Mail; using System.Security.Cryptography; using System.Text; using System.Text.Json.Nodes; using System.Text.RegularExpressions; -using Flurl.Http; using InnovEnergy.App.Backend.Model; using InnovEnergy.App.Backend.Utils; using InnovEnergy.Lib.Utils; using SQLite; -using ResponseExtensions = Flurl.Http.ResponseExtensions; #pragma warning disable CS0472 #pragma warning disable CS8602 @@ -53,10 +50,10 @@ public partial class Db public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); - public Result CreateUser(User user) + public Int64 CreateUser(User user) { if (GetUserByEmail(user.Email) is not null) - return Result.Error("User with that email already exists"); + return -1; // TODO: User with that email already exists //Salting and Hashing password var salt = Crypto.GenerateSalt(); @@ -195,23 +192,23 @@ public partial class Db return newKey; } - public Result UpdateUser(User user) + public Boolean UpdateUser(User user) { var oldUser = GetUserById(user.Id); if (oldUser == null) - return Result.Error("User doesn't exist"); + return false; // TODO: "User doesn't exist" //Checking for unchangeable things // TODO: depends on privileges of caller - user.Id = oldUser.Id; + user.Id = oldUser.Id; user.ParentId = oldUser.ParentId; - user.Email = oldUser.Email; + user.Email = oldUser.Email; return Update(user); } - public Result DeleteUser(User user) + public Boolean DeleteUser(User user) { User2Folder.Delete(u => u.UserId == user.Id); User2Installation.Delete(u => u.UserId == user.Id); diff --git a/csharp/App/Backend/Utils/Result.cs b/csharp/App/Backend/Utils/Result.cs deleted file mode 100644 index 979e9184e..000000000 --- a/csharp/App/Backend/Utils/Result.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace InnovEnergy.App.Backend.Utils; - -public class Result -{ - private const String OkMsg = "Ok"; - - private readonly String _Error; - - public static Result Ok = new Result(OkMsg); - - public Boolean IsError => _Error != OkMsg; - public Boolean IsSuccess => _Error == OkMsg; - - private Result(String error) => _Error = error; - - public static Result Error(String error) => new Result(error); - - public static Result Error(Exception exception) - { - - #if DEBUG - var msg = exception.ToString(); // includes stacktrace - #else - var msg = exception.Message; // excludes stacktrace - #endif - - return new Result(msg); - } -} \ No newline at end of file From 09a8eee138e726b733fca964a7f5fb2f8ef661fb Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 16:33:14 +0100 Subject: [PATCH 15/16] seperated create from update --- csharp/App/Backend/Controllers/Controller.cs | 60 +++++++++++++++++--- csharp/App/Backend/Database/Db.cs | 14 ++++- csharp/App/Backend/Database/Installation.cs | 3 +- csharp/App/Backend/Database/User.cs | 2 +- 4 files changed, 67 insertions(+), 12 deletions(-) diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 484039384..10d4ba578 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -4,7 +4,6 @@ using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Model; using InnovEnergy.App.Backend.Model.Relations; using InnovEnergy.App.Backend.Utils; -using InnovEnergy.Lib.Utils; using Microsoft.AspNetCore.Mvc; using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor; @@ -227,23 +226,68 @@ public class Controller return folder; } + + [Returns(HttpStatusCode.OK)] + [Returns(HttpStatusCode.Unauthorized)] + [HttpPost($"{nameof(CreateUser)}/")] + public Object CreateUser(User newUser) + { + var caller = GetCaller(); + using var db = Db.Connect(); + if (caller == null || !caller.HasWriteAccess) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); + newUser.ParentId = caller.Id; + + return db.CreateUser(newUser); + } + + [Returns(HttpStatusCode.OK)] + [Returns(HttpStatusCode.Unauthorized)] + [HttpPost($"{nameof(CreateInstallation)}/")] + public Object CreateInstallation(Installation installation) + { + var caller = GetCaller(); + using var db = Db.Connect(); + if (caller == null || !caller.HasWriteAccess) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); + + db.CreateInstallation(installation); + if (db.GetInstallationById(1)!.Name != installation.Name) + { + return new HttpResponseMessage(HttpStatusCode.BadRequest); + } + + return db.AddToAccessibleInstallations(caller.Id, 1); + + } + + [Returns(HttpStatusCode.OK)] + [Returns(HttpStatusCode.Unauthorized)] + [HttpPost($"{nameof(CreateFolder)}/")] + public Object CreateFolder(Folder folder) + { + var caller = GetCaller(); + using var db = Db.Connect(); + if (caller == null || !caller.HasWriteAccess || db.GetInstallationByName(folder.Name) != null) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); + + db.CreateFolder(folder); + return db.AddToAccessibleInstallations(caller.Id, db.GetInstallationByName(folder.Name)!.Id); + + } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [HttpPut($"{nameof(UpdateUser)}/")] public Object UpdateUser(User updatedUser) { - // TODO: distinguish between create and update - var caller = GetCaller(); using var db = Db.Connect(); - if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser)) - return new HttpResponseMessage(HttpStatusCode.Unauthorized); + if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser) || !caller.HasWriteAccess) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); - return db.GetUserById(updatedUser.Id) != null - ? db.UpdateUser(updatedUser) - : db.CreateUser(updatedUser); + return db.UpdateUser(updatedUser); } diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 06bdbd2e7..556704cc7 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -213,8 +213,7 @@ public partial class Db : IDisposable public Object? GetInstallationS3Key(Int64 installationId) { return Installations - .Where(installation => installation.Id == installationId) - .Select(installation => installation.S3Key); + .FirstOrDefault(installation => installation.Id == installationId).S3Key; } public void DeleteS3KeysDaily() @@ -225,5 +224,16 @@ public partial class Db : IDisposable Update(installation); } } + + public Installation? GetInstallationByName(String installationName) + { + return Installations + .FirstOrDefault(installation => installation.Name == installationName); + } + + public Int64 LastInsertRowId() + { + return _Db.; + } } diff --git a/csharp/App/Backend/Database/Installation.cs b/csharp/App/Backend/Database/Installation.cs index e02ad9277..9bd78ca93 100644 --- a/csharp/App/Backend/Database/Installation.cs +++ b/csharp/App/Backend/Database/Installation.cs @@ -31,6 +31,7 @@ public partial class Db return Delete(installation); } - + + } diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index b07bc6b62..af5f09043 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -53,7 +53,7 @@ public partial class Db public User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email); - public Result CreateUser(User user) + public Object CreateUser(User user) { if (GetUserByEmail(user.Email) is not null) return Result.Error("User with that email already exists"); From 2703dd1e110a10d19eb3c2b111f8b10f00db3db7 Mon Sep 17 00:00:00 2001 From: Kim Date: Thu, 9 Mar 2023 16:37:36 +0100 Subject: [PATCH 16/16] adds newly created inst and folders to accessible --- csharp/App/Backend/Controllers/Controller.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 10d4ba578..2351d5e92 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -252,13 +252,9 @@ public class Controller if (caller == null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.Unauthorized); - db.CreateInstallation(installation); - if (db.GetInstallationById(1)!.Name != installation.Name) - { - return new HttpResponseMessage(HttpStatusCode.BadRequest); - } + var id = db.CreateInstallation(installation); - return db.AddToAccessibleInstallations(caller.Id, 1); + return db.AddToAccessibleInstallations(caller.Id, id); } @@ -269,11 +265,11 @@ public class Controller { var caller = GetCaller(); using var db = Db.Connect(); - if (caller == null || !caller.HasWriteAccess || db.GetInstallationByName(folder.Name) != null) + if (caller == null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.Unauthorized); - db.CreateFolder(folder); - return db.AddToAccessibleInstallations(caller.Id, db.GetInstallationByName(folder.Name)!.Id); + var id = db.CreateFolder(folder); + return db.AddToAccessibleFolders(caller.Id, id); }