diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 345ac4e58..9136ac151 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -1,13 +1,14 @@ using System.Net; using System.Text; -using Innovenergy.Backend.Database; -using Innovenergy.Backend.Model; -using Innovenergy.Backend.Model.Relations; -using Innovenergy.Backend.Utils; +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; -namespace Innovenergy.Backend.Controllers; +namespace InnovEnergy.App.Backend.Controllers; [ApiController] [Route("api/")] @@ -173,7 +174,7 @@ public class Controller using var db = Db.Connect(); var folders = db - .GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure + .GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure .Select(f => PopulateChildren(db, f)); var installations = db.GetDirectlyAccessibleInstallations(caller); @@ -183,6 +184,25 @@ public class Controller .ToList(); // important! } + [Returns] // assuming swagger knows about arrays but not lists (JSON) + [Returns(HttpStatusCode.Unauthorized)] + [HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")] + public Object GetAllFoldersAndInstallations() + { + var caller = GetCaller(); + if (caller == null) + return new HttpResponseMessage(HttpStatusCode.Unauthorized); + + using var db = Db.Connect(); + + var folders = db.GetAllAccessibleFolders(caller) as IEnumerable; + var installations = db.GetAllAccessibleInstallations(caller); + + return folders + .Concat(installations) + .ToList(); // important! + } + private static Folder PopulateChildren(Db db, Folder folder, HashSet? hs = null) { // TODO: remove cycle detector diff --git a/csharp/App/Backend/Controllers/Credentials.cs b/csharp/App/Backend/Controllers/Credentials.cs index 2c5c72b45..f3149f7e8 100644 --- a/csharp/App/Backend/Controllers/Credentials.cs +++ b/csharp/App/Backend/Controllers/Credentials.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace Innovenergy.Backend.Controllers; +namespace InnovEnergy.App.Backend.Controllers; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] public record Credentials(String Username, String Password); \ No newline at end of file diff --git a/csharp/App/Backend/Controllers/ReturnsAttribute.cs b/csharp/App/Backend/Controllers/ReturnsAttribute.cs index 6d94088f5..2ef924055 100644 --- a/csharp/App/Backend/Controllers/ReturnsAttribute.cs +++ b/csharp/App/Backend/Controllers/ReturnsAttribute.cs @@ -1,7 +1,7 @@ using System.Net; using Microsoft.AspNetCore.Mvc; -namespace Innovenergy.Backend.Controllers; +namespace InnovEnergy.App.Backend.Controllers; public class ReturnsAttribute : ProducesResponseTypeAttribute { diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index aaabfbfd9..5c87cc9c8 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -1,11 +1,11 @@ using System.Diagnostics.CodeAnalysis; -using Innovenergy.Backend.Model; -using Innovenergy.Backend.Model.Relations; -using Innovenergy.Backend.Utils; +using InnovEnergy.App.Backend.Model; +using InnovEnergy.App.Backend.Model.Relations; +using InnovEnergy.App.Backend.Utils; using InnovEnergy.Lib.Utils; using SQLite; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db : IDisposable { @@ -89,21 +89,23 @@ public partial class Db : IDisposable public IEnumerable GetAllAccessibleInstallations(User user) { - var direct = GetDirectlyAccessibleInstallations(user).ToList(); + var direct = GetDirectlyAccessibleInstallations(user); var fromFolders = GetAllAccessibleFolders(user) - .SelectMany(GetChildInstallations) - .Except(direct); + .SelectMany(GetChildInstallations); - return direct.Concat(fromFolders); + return direct + .Concat(fromFolders) + .Distinct(); } - - - public IEnumerable GetAllAccessibleFolders(User user) { return GetDirectlyAccessibleFolders(user) - .SelectMany(GetDescendantFolders); + .SelectMany(GetDescendantFolders) + .Distinct(); + + // Distinct because the user might have direct access + // to a child folder of a folder he has already access to } @@ -113,7 +115,8 @@ public partial class Db : IDisposable .Where(r => r.UserId == user.Id) .Select(r => r.InstallationId) .Select(GetInstallationById) - .NotNull(); + .NotNull() + .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user } public IEnumerable GetDirectlyAccessibleFolders(User user) @@ -122,7 +125,8 @@ public partial class Db : IDisposable .Where(r => r.UserId == user.Id) .Select(r => r.FolderId) .Select(GetFolderById) - .NotNull(); + .NotNull() + .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user; } public Result AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId) diff --git a/csharp/App/Backend/Database/Fake.cs b/csharp/App/Backend/Database/Fake.cs index 427c78434..63fff39ee 100644 --- a/csharp/App/Backend/Database/Fake.cs +++ b/csharp/App/Backend/Database/Fake.cs @@ -1,6 +1,6 @@ -using Innovenergy.Backend.Model.Relations; +using InnovEnergy.App.Backend.Model.Relations; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/Database/Folder.cs b/csharp/App/Backend/Database/Folder.cs index 34e20f529..8a4785639 100644 --- a/csharp/App/Backend/Database/Folder.cs +++ b/csharp/App/Backend/Database/Folder.cs @@ -1,9 +1,9 @@ -using Innovenergy.Backend.Model; -using Innovenergy.Backend.Utils; +using InnovEnergy.App.Backend.Model; +using InnovEnergy.App.Backend.Utils; using InnovEnergy.Lib.Utils; using SQLite; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/Database/Installation.cs b/csharp/App/Backend/Database/Installation.cs index a362a39df..6e554f483 100644 --- a/csharp/App/Backend/Database/Installation.cs +++ b/csharp/App/Backend/Database/Installation.cs @@ -1,8 +1,8 @@ -using Innovenergy.Backend.Model; -using Innovenergy.Backend.Utils; +using InnovEnergy.App.Backend.Model; +using InnovEnergy.App.Backend.Utils; using SQLite; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/Database/User.cs b/csharp/App/Backend/Database/User.cs index 3f8fef76a..682ecbc5b 100644 --- a/csharp/App/Backend/Database/User.cs +++ b/csharp/App/Backend/Database/User.cs @@ -3,12 +3,11 @@ 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.App.Backend.Model; +using InnovEnergy.App.Backend.Utils; using InnovEnergy.Lib.Utils; using SQLite; using ResponseExtensions = Flurl.Http.ResponseExtensions; @@ -16,7 +15,7 @@ using ResponseExtensions = Flurl.Http.ResponseExtensions; #pragma warning disable CS0472 #pragma warning disable CS8602 -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/Database/User2Folder.cs b/csharp/App/Backend/Database/User2Folder.cs index 465eea722..29351f1dd 100644 --- a/csharp/App/Backend/Database/User2Folder.cs +++ b/csharp/App/Backend/Database/User2Folder.cs @@ -1,7 +1,7 @@ -using Innovenergy.Backend.Model.Relations; +using InnovEnergy.App.Backend.Model.Relations; using SQLite; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/Database/User2Installation.cs b/csharp/App/Backend/Database/User2Installation.cs index ca329deee..069588d7c 100644 --- a/csharp/App/Backend/Database/User2Installation.cs +++ b/csharp/App/Backend/Database/User2Installation.cs @@ -1,7 +1,7 @@ -using Innovenergy.Backend.Model.Relations; +using InnovEnergy.App.Backend.Model.Relations; using SQLite; -namespace Innovenergy.Backend.Database; +namespace InnovEnergy.App.Backend.Database; public partial class Db { diff --git a/csharp/App/Backend/HeaderFilter.cs b/csharp/App/Backend/HeaderFilter.cs index 8ca9cdff8..f3520b75e 100644 --- a/csharp/App/Backend/HeaderFilter.cs +++ b/csharp/App/Backend/HeaderFilter.cs @@ -1,7 +1,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Innovenergy.Backend; +namespace InnovEnergy.App.Backend; /// /// This is for convenient testing! Todo throw me out? diff --git a/csharp/App/Backend/Model/Folder.cs b/csharp/App/Backend/Model/Folder.cs index c9c39c594..041c0f479 100644 --- a/csharp/App/Backend/Model/Folder.cs +++ b/csharp/App/Backend/Model/Folder.cs @@ -1,4 +1,4 @@ -namespace Innovenergy.Backend.Model; +namespace InnovEnergy.App.Backend.Model; public class Folder : TreeNode { diff --git a/csharp/App/Backend/Model/Installation.cs b/csharp/App/Backend/Model/Installation.cs index 4cca94295..b9858e9c8 100644 --- a/csharp/App/Backend/Model/Installation.cs +++ b/csharp/App/Backend/Model/Installation.cs @@ -1,4 +1,4 @@ -namespace Innovenergy.Backend.Model; +namespace InnovEnergy.App.Backend.Model; public class Installation : TreeNode diff --git a/csharp/App/Backend/Model/Relations/Relation.cs b/csharp/App/Backend/Model/Relations/Relation.cs index 31250df14..a791a4609 100644 --- a/csharp/App/Backend/Model/Relations/Relation.cs +++ b/csharp/App/Backend/Model/Relations/Relation.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using SQLite; -namespace Innovenergy.Backend.Model.Relations; +namespace InnovEnergy.App.Backend.Model.Relations; public abstract class Relation { diff --git a/csharp/App/Backend/Model/Relations/Session.cs b/csharp/App/Backend/Model/Relations/Session.cs index ff64b6644..829cc9a60 100644 --- a/csharp/App/Backend/Model/Relations/Session.cs +++ b/csharp/App/Backend/Model/Relations/Session.cs @@ -1,6 +1,6 @@ using SQLite; -namespace Innovenergy.Backend.Model.Relations; +namespace InnovEnergy.App.Backend.Model.Relations; public class Session : Relation { diff --git a/csharp/App/Backend/Model/Relations/User2Folder.cs b/csharp/App/Backend/Model/Relations/User2Folder.cs index 6e9f56433..f93cab568 100644 --- a/csharp/App/Backend/Model/Relations/User2Folder.cs +++ b/csharp/App/Backend/Model/Relations/User2Folder.cs @@ -1,6 +1,6 @@ using SQLite; -namespace Innovenergy.Backend.Model.Relations; +namespace InnovEnergy.App.Backend.Model.Relations; internal class User2Folder : Relation { diff --git a/csharp/App/Backend/Model/Relations/User2Installation.cs b/csharp/App/Backend/Model/Relations/User2Installation.cs index 43fa45d0e..fcd2760dd 100644 --- a/csharp/App/Backend/Model/Relations/User2Installation.cs +++ b/csharp/App/Backend/Model/Relations/User2Installation.cs @@ -1,6 +1,6 @@ using SQLite; -namespace Innovenergy.Backend.Model.Relations; +namespace InnovEnergy.App.Backend.Model.Relations; internal class User2Installation : Relation { diff --git a/csharp/App/Backend/Model/TreeNode.Equality.cs b/csharp/App/Backend/Model/TreeNode.Equality.cs index b044a020f..f37cd51d7 100644 --- a/csharp/App/Backend/Model/TreeNode.Equality.cs +++ b/csharp/App/Backend/Model/TreeNode.Equality.cs @@ -1,12 +1,13 @@ using System.Diagnostics.CodeAnalysis; -namespace Innovenergy.Backend.Model; +namespace InnovEnergy.App.Backend.Model; public abstract partial class TreeNode { + // Note: Only consider Id, but not ParentId for TreeNode equality checks protected Boolean Equals(TreeNode other) { - return Id == other.Id && ParentId == other.ParentId; + return Id == other.Id; } public override Boolean Equals(Object? obj) @@ -17,8 +18,5 @@ public abstract partial class TreeNode } [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override Int32 GetHashCode() - { - return HashCode.Combine(Id, ParentId); - } + public override Int32 GetHashCode() => Id.GetHashCode(); } \ No newline at end of file diff --git a/csharp/App/Backend/Model/TreeNode.cs b/csharp/App/Backend/Model/TreeNode.cs index b3cf22170..d02890a62 100644 --- a/csharp/App/Backend/Model/TreeNode.cs +++ b/csharp/App/Backend/Model/TreeNode.cs @@ -1,6 +1,6 @@ using SQLite; -namespace Innovenergy.Backend.Model; +namespace InnovEnergy.App.Backend.Model; public abstract partial class TreeNode { diff --git a/csharp/App/Backend/Model/User.cs b/csharp/App/Backend/Model/User.cs index a1a0be392..762c819a4 100644 --- a/csharp/App/Backend/Model/User.cs +++ b/csharp/App/Backend/Model/User.cs @@ -1,6 +1,6 @@ using SQLite; -namespace Innovenergy.Backend.Model; +namespace InnovEnergy.App.Backend.Model; public class User : TreeNode { diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index fd0206737..ae662b779 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -1,7 +1,7 @@ -using Innovenergy.Backend.Database; +using InnovEnergy.App.Backend.Database; using Microsoft.OpenApi.Models; -namespace Innovenergy.Backend; +namespace InnovEnergy.App.Backend; public static class Program { diff --git a/csharp/App/Backend/Utils/Crypto.cs b/csharp/App/Backend/Utils/Crypto.cs index 199b17fb9..c176fd284 100644 --- a/csharp/App/Backend/Utils/Crypto.cs +++ b/csharp/App/Backend/Utils/Crypto.cs @@ -1,6 +1,6 @@ using System.Security.Cryptography; -namespace Innovenergy.Backend.Utils; +namespace InnovEnergy.App.Backend.Utils; public static class Crypto { diff --git a/csharp/App/Backend/Utils/Result.cs b/csharp/App/Backend/Utils/Result.cs index 21e85876c..979e9184e 100644 --- a/csharp/App/Backend/Utils/Result.cs +++ b/csharp/App/Backend/Utils/Result.cs @@ -1,4 +1,4 @@ -namespace Innovenergy.Backend.Utils; +namespace InnovEnergy.App.Backend.Utils; public class Result { 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 diff --git a/csharp/App/InnovEnergy.App.props b/csharp/App/InnovEnergy.App.props index 38d62f4dc..fe6d1e334 100644 --- a/csharp/App/InnovEnergy.App.props +++ b/csharp/App/InnovEnergy.App.props @@ -19,7 +19,7 @@ Exe - InnovEnergy.App.$(AssemblyName) + \ No newline at end of file diff --git a/csharp/App/RemoteSupportConsole/RemoteSupportConsole.csproj b/csharp/App/RemoteSupportConsole/RemoteSupportConsole.csproj index c00eed4bb..1f571c26d 100644 --- a/csharp/App/RemoteSupportConsole/RemoteSupportConsole.csproj +++ b/csharp/App/RemoteSupportConsole/RemoteSupportConsole.csproj @@ -6,9 +6,6 @@ - - - diff --git a/csharp/App/SaliMax/src/Utils.cs b/csharp/App/SaliMax/src/Utils.cs index 4104b5384..1cf608f84 100644 --- a/csharp/App/SaliMax/src/Utils.cs +++ b/csharp/App/SaliMax/src/Utils.cs @@ -6,6 +6,6 @@ public static class Utils { public static Decimal Round3(this Decimal d) { - return DecimalUtils.RoundToSignificantFigures(d, 3); + return DecimalUtils.RoundToSignificantDigits(d, 3); } } \ No newline at end of file diff --git a/csharp/InnovEnergy.props b/csharp/InnovEnergy.props index 5a4943931..7315812ef 100644 --- a/csharp/InnovEnergy.props +++ b/csharp/InnovEnergy.props @@ -9,7 +9,7 @@ net6.0 true false - $(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("src/", "").Replace("/",".")) + $(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("src/", "").Replace("/",".").Replace("\",".")) $(Company) Team diff --git a/csharp/InnovEnergy.sln.DotSettings b/csharp/InnovEnergy.sln.DotSettings index f48e450b2..0584ce254 100644 --- a/csharp/InnovEnergy.sln.DotSettings +++ b/csharp/InnovEnergy.sln.DotSettings @@ -4,6 +4,7 @@ False True + True True True True diff --git a/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs b/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs index 371395e46..8ee809b72 100644 --- a/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs +++ b/csharp/Lib/Devices/AMPT/AmptDeviceStatus.cs @@ -1,5 +1,5 @@ +using InnovEnergy.Lib.StatusApi; using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Devices; namespace InnovEnergy.Lib.Devices.AMPT; @@ -11,5 +11,5 @@ public record AmptDeviceStatus UInt32 Timestamp, // The UTC timestamp of the measurements Decimal ProductionToday, // converted to kW in AmptCU class IReadOnlyList Strings -): Mppt(Dc, Strings) +): MpptStatus(Dc, Strings) {} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs index 979eafaf7..6507ac32d 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs @@ -1,7 +1,5 @@ using InnovEnergy.Lib.Protocols.Modbus.Clients; using InnovEnergy.Lib.Protocols.Modbus.Connections; -using InnovEnergy.Lib.Protocols.Modbus.Conversions; -using InnovEnergy.Lib.StatusApi.Connections; namespace InnovEnergy.Lib.Devices.Battery48TL; @@ -33,18 +31,11 @@ public class Battery48TlDevice public Battery48TLStatus? ReadStatus() //Already try catch is implemented { - if (Modbus is null) // TODO : remove fake - { - Console.WriteLine("Battery is null"); - return null; - } - - // Console.WriteLine("Reading Battery Data"); - try { - var registers = Modbus.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters); - return TryReadStatus(registers); + return Modbus + .ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters) + .ParseBatteryStatus(); } catch (Exception e) { @@ -53,92 +44,4 @@ public class Battery48TlDevice return null; } } - - private Battery48TLStatus? TryReadStatus(ModbusRegisters data) - { - var soc = data.ParseDecimal(register: 1054, scaleFactor: 0.1m); - var eocReached = data.ParseEocReached(); - - var warnings = new List(); - - if (data.ParseBool(1006, 1)) warnings.Add("TaM1: BMS temperature high"); - if (data.ParseBool(1006, 4)) warnings.Add("TbM1: Battery temperature high"); - if (data.ParseBool(1006, 6)) warnings.Add("VBm1: Bus voltage low"); - if (data.ParseBool(1006, 8)) warnings.Add("VBM1: Bus voltage high"); - if (data.ParseBool(1006, 10)) warnings.Add("IDM1: Discharge current high"); - if (data.ParseBool(1006, 24)) warnings.Add("vsM1: String voltage high"); - if (data.ParseBool(1006, 26)) warnings.Add("iCM1: Charge current high"); - if (data.ParseBool(1006, 28)) warnings.Add("iDM1: Discharge current high"); - if (data.ParseBool(1006, 30)) warnings.Add("MID1: String voltages unbalanced"); - if (data.ParseBool(1006, 32)) warnings.Add("BLPW: Not enough charging power on bus"); - if (data.ParseBool(1006, 35)) warnings.Add("Ah_W: String SOC low"); - if (data.ParseBool(1006, 38)) warnings.Add("MPMM: Midpoint wiring problem"); - if (data.ParseBool(1006, 39)) warnings.Add("TCMM:"); - if (data.ParseBool(1006, 40)) warnings.Add("TCdi: Temperature difference between strings high"); - if (data.ParseBool(1006, 41)) warnings.Add("WMTO:"); - if (data.ParseBool(1006, 44)) warnings.Add("bit44:"); - if (data.ParseBool(1006, 46)) warnings.Add("CELL1:"); - - var alarms = new List(); - - if (data.ParseBool(1010, 0)) alarms.Add("Tam : BMS temperature too low"); - if (data.ParseBool(1010, 2)) alarms.Add("TaM2 : BMS temperature too high"); - if (data.ParseBool(1010, 3)) alarms.Add("Tbm : Battery temperature too low"); - if (data.ParseBool(1010, 5)) alarms.Add("TbM2 : Battery temperature too high"); - if (data.ParseBool(1010, 7)) alarms.Add("VBm2 : Bus voltage too low"); - if (data.ParseBool(1010, 9)) alarms.Add("VBM2 : Bus voltage too high"); - if (data.ParseBool(1010, 11)) alarms.Add("IDM2 : Discharge current too high"); - if (data.ParseBool(1010, 12)) alarms.Add("ISOB : Electrical insulation failure"); - if (data.ParseBool(1010, 13)) alarms.Add("MSWE : Main switch failure"); - if (data.ParseBool(1010, 14)) alarms.Add("FUSE : Main fuse blown"); - if (data.ParseBool(1010, 15)) alarms.Add("HTRE : Battery failed to warm up"); - if (data.ParseBool(1010, 16)) alarms.Add("TCPE : Temperature sensor failure"); - if (data.ParseBool(1010, 17)) alarms.Add("STRE :"); - if (data.ParseBool(1010, 18)) alarms.Add("CME : Current sensor failure"); - if (data.ParseBool(1010, 19)) alarms.Add("HWFL : BMS hardware failure"); - if (data.ParseBool(1010, 20)) alarms.Add("HWEM : Hardware protection tripped"); - if (data.ParseBool(1010, 21)) alarms.Add("ThM : Heatsink temperature too high"); - if (data.ParseBool(1010, 22)) alarms.Add("vsm1 : String voltage too low"); - if (data.ParseBool(1010, 23)) alarms.Add("vsm2 : Low string voltage failure"); - if (data.ParseBool(1010, 25)) alarms.Add("vsM2 : String voltage too high"); - if (data.ParseBool(1010, 27)) alarms.Add("iCM2 : Charge current too high"); - if (data.ParseBool(1010, 29)) alarms.Add("iDM2 : Discharge current too high"); - if (data.ParseBool(1010, 31)) alarms.Add("MID2 : String voltage unbalance too high"); - if (data.ParseBool(1010, 33)) alarms.Add("CCBF : Internal charger hardware failure"); - if (data.ParseBool(1010, 34)) alarms.Add("AhFL :"); - if (data.ParseBool(1010, 36)) alarms.Add("TbCM :"); - if (data.ParseBool(1010, 37)) alarms.Add("BRNF :"); - if (data.ParseBool(1010, 42)) alarms.Add("HTFS : If Heaters Fuse Blown"); - if (data.ParseBool(1010, 43)) alarms.Add("DATA : Parameters out of range"); - if (data.ParseBool(1010, 45)) alarms.Add("CELL2:"); - - -return new Battery48TLStatus( - Dc: new DcConnection - ( - Voltage : data.ReadVoltage(), - Current : data.ReadCurrent()), - - Soc : !eocReached && soc >= 100m ? 99.9m : soc, - Temperature : data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400), - BusVoltage : data.ParseDecimal(register: 1002, scaleFactor: 0.01m), - GreenLed : data.ParseLedState(register: 1005, led: LedColor.Green), - AmberLed : data.ParseLedState(register: 1006, led: LedColor.Amber), - BlueLed : data.ParseLedState(register: 1005, led: LedColor.Blue), - RedLed : data.ParseLedState(register: 1005, led: LedColor.Red), - Warnings : warnings, - Alarms : alarms, - MainSwitchClosed : data.ParseBool(baseRegister: 1014, bit: 0), - AlarmOutActive : data.ParseBool(baseRegister: 1014, bit: 1), - InternalFanActive : data.ParseBool(baseRegister: 1014, bit: 2), - VoltMeasurementAllowed: data.ParseBool(baseRegister: 1014, bit: 3), - AuxRelay : data.ParseBool(baseRegister: 1014, bit: 4), - RemoteState : data.ParseBool(baseRegister: 1014, bit: 5), - HeaterOn : data.ParseBool(baseRegister: 1014, bit: 6), - EocReached : eocReached, - BatteryCold : data.ParseBatteryCold(), - MaxChargingPower : data.CalcMaxChargePower(), - MaxDischargingPower : data.CalcMaxDischargePower() - ); - } } \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs b/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs index 0871f0645..b9feb07d0 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TLStatusRecord.cs @@ -1,39 +1,50 @@ using System.Diagnostics.CodeAnalysis; -using InnovEnergy.Lib.Protocols.Modbus.Conversions; -using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Devices; +using InnovEnergy.Lib.StatusApi; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Devices.Battery48TL; +using T = Battery48TLStatus; + [SuppressMessage("ReSharper", "InconsistentNaming")] -public record Battery48TLStatus -( - DcConnection Dc, - Decimal Soc, - Decimal Temperature, - //Decimal Current, - //Decimal Voltage, - Decimal BusVoltage, - LedState GreenLed, - LedState AmberLed, - LedState BlueLed, - LedState RedLed, - IReadOnlyList Warnings, - IReadOnlyList Alarms, - Boolean MainSwitchClosed, - Boolean AlarmOutActive, - Boolean InternalFanActive, - Boolean VoltMeasurementAllowed, - Boolean AuxRelay, - Boolean RemoteState, - Boolean HeaterOn, - Boolean EocReached, - Boolean BatteryCold, - Decimal MaxChargingPower, - Decimal MaxDischargingPower -) -: Battery(Dc, Soc, Temperature) +public record Battery48TLStatus : BatteryStatus { + public Voltage CellsVoltage { get; init; } + + public Power MaxChargingPower { get; init; } + public Power MaxDischargingPower { get; init; } + + public State GreenLed { get; init; } + public State AmberLed { get; init; } + public State BlueLed { get; init; } + public State RedLed { get; init; } + + public State Warnings { get; init; } + public State Alarms { get; init; } + + public State MainSwitchState { get; init; } // connected to bus | disconnected from bus + public State HeaterState { get; init; } // heating | not heating + public State EocState { get; init; } // EOC reached | EOC not reached + public State TemperatureState { get; init; } // cold | operating temperature | overheated + + + public static T operator |(T left, T right) => OpParallel(left, right); + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + + + + // TODO: strings + // TODO + // public State LimitedBy { get; init; } + + // TODO + // public Boolean AlarmOutActive { get; init; } + // public Boolean InternalFanActive { get; init; } + // public Boolean VoltMeasurementAllowed { get; init; } + // public Boolean AuxRelay { get; init; } + // public Boolean RemoteState { get; init; } + } diff --git a/csharp/Lib/Devices/Battery48TL/BatteryDataParser.cs b/csharp/Lib/Devices/Battery48TL/BatteryDataParser.cs deleted file mode 100644 index 916c8989c..000000000 --- a/csharp/Lib/Devices/Battery48TL/BatteryDataParser.cs +++ /dev/null @@ -1,167 +0,0 @@ -using InnovEnergy.Lib.Protocols.Modbus.Conversions; -using InnovEnergy.Lib.Utils; - -namespace InnovEnergy.Lib.Devices.Battery48TL; - -public static class BatteryDataParser -{ - public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0) - { - var value = data[register].ConvertTo(); // widen to 32bit signed - - if (value >= 0x8000) - value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&! - - return (Decimal)(value + offset) * scaleFactor; - } - - internal static Decimal ReadCurrent(this ModbusRegisters data) - { - return ParseDecimal(data, register: 1001, scaleFactor: 0.01m, offset: -10000); - } - - internal static Decimal ReadVoltage(this ModbusRegisters data) - { - return ParseDecimal(data, register: 1000, scaleFactor: 0.01m); - } - - internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit) - { - var x = bit / 16; - var y = bit % 16; - - var value = (UInt32)data[baseRegister + x]; - - return (value & (1 << y)) > 0; - } - - internal static LedState ParseLedState(this ModbusRegisters data, Int32 register, LedColor led) - { - var lo = ParseBool(data, register, (led.ConvertTo() * 2).ConvertTo()); - var hi = ParseBool(data, register, (led.ConvertTo() * 2 + 1).ConvertTo()); - - if (hi) - { - if (lo) - { - return LedState.BlinkingFast; - } - else - { - return LedState.BlinkingSlow; - } - } - else - { - if (lo) - { - return LedState.On; - } - else - { - return LedState.Off; - } - } - - } - - internal static String ParseRegisters(this ModbusRegisters data, Int32 register, Int16 count) - { - var container = ""; - - var start = register; - var end = register + count; - - for (var i = start; i < end; i++) - { - var binary = Convert.ToString(data[register], 2); - container += binary.PadLeft(16, '0'); - } - return container; - } - - internal static Boolean ParseEocReached(this ModbusRegisters data) - { - return ParseLedState(data, 1005, LedColor.Green) == LedState.On && - ParseLedState(data, 1005, LedColor.Amber) == LedState.Off && - ParseLedState(data, 1005, LedColor.Blue) == LedState.Off; - } - - internal static Boolean ParseBatteryCold(this ModbusRegisters data) - { - return ParseLedState(data, 1005, LedColor.Green) >= LedState.BlinkingSlow && - ParseLedState(data, 1005, LedColor.Blue) >= LedState.BlinkingSlow; - } - - private static Decimal CalcPowerLimitImposedByVoltageLimit(Decimal v,Decimal i,Decimal vLimit,Decimal rInt) - { - var dv = vLimit - v; - var di = dv / rInt; - var pLimit = vLimit * (i + di); - - return pLimit; - } - - private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt) - { - var di = iLimit - i; - var dv = di * rInt; - var pLimit = iLimit * (v + dv); - - return pLimit; - } - - - private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint) - { - // const Int32 holdZone = 300; - // const Int32 maxAllowedTemp = 315; - - var kp = 0.05m; - var error = setpoint - power; - var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m)); - - return controlOutput; - - // var a = holdZone - maxAllowedTemp; - // var b = -a * maxAllowedTemp; - } - - internal static Decimal CalcMaxChargePower(this ModbusRegisters data) - { - var v = ReadVoltage(data); - var i = ReadCurrent(data); - - var pLimits = new[] - { - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin), - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax), - CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin), - CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax) - }; - - var pLimit = pLimits.Min(); - - return Math.Max(pLimit, 0); - } - - internal static Decimal CalcMaxDischargePower(this ModbusRegisters data) - { - var v = ReadVoltage(data); - var i = ReadCurrent(data); - var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400); - - var pLimits = new[] - { - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin), - CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax), - CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin), - CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax), - // CalcPowerLimitImposedByTempLimit(t,315,300) - }; - - var pLimit = pLimits.Max(); - - return Math.Min(pLimit, 0); - } -} \ No newline at end of file diff --git a/csharp/Lib/Devices/Battery48TL/ModbusParser.cs b/csharp/Lib/Devices/Battery48TL/ModbusParser.cs new file mode 100644 index 000000000..d16c3dc5d --- /dev/null +++ b/csharp/Lib/Devices/Battery48TL/ModbusParser.cs @@ -0,0 +1,272 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.Lib.Protocols.Modbus.Conversions; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Devices.Battery48TL; + +public static class ModbusParser +{ + internal static Battery48TLStatus ParseBatteryStatus(this ModbusRegisters data) + { + return new Battery48TLStatus + { + Dc = data.ParseDcBus(), + Alarms = data.ParseAlarms().ToList(), + Warnings = data.ParseWarnings().ToList(), + Soc = data.ParseSoc(), + Temperature = data.ParseTemperature(), + GreenLed = data.ParseGreenLed(), + AmberLed = data.ParseAmberLed(), + BlueLed = data.ParseBlueLed(), + RedLed = data.ParseRedLed(), + MainSwitchState = data.ParseMainSwitchState(), + HeaterState = data.ParseHeaterState(), + EocState = data.ParseEocState(), + TemperatureState = data.ParseTemperatureState(), + MaxChargingPower = data.CalcMaxChargePower(), + MaxDischargingPower = data.CalcMaxDischargePower(), + CellsVoltage = data.ParseCellsVoltage(), + }; + } + + + public static Decimal ParseDecimal(this ModbusRegisters data, Int32 register, Decimal scaleFactor = 1.0m, Double offset = 0.0) + { + var value = data[register].ConvertTo(); // widen to 32bit signed + + if (value >= 0x8000) + value -= 0x10000; // Fiamm stores their integers signed AND with sign-offset @#%^&! + + return (Decimal)(value + offset) * scaleFactor; + } + + internal static Decimal ParseCurrent(this ModbusRegisters data) + { + return data.ParseDecimal(register: 1001, scaleFactor: 0.01m, offset: -10000); + } + + internal static Decimal ParseCellsVoltage(this ModbusRegisters data) + { + return data.ParseDecimal(register: 1000, scaleFactor: 0.01m); + } + + internal static Decimal ParseBusVoltage(this ModbusRegisters data) + { + return data.ParseDecimal(register: 1002, scaleFactor: 0.01m); + } + + internal static Boolean ParseBool(this ModbusRegisters data, Int32 baseRegister, Int16 bit) + { + var x = bit / 16; + var y = bit % 16; + + var value = (UInt32)data[baseRegister + x]; + + return (value & (1 << y)) > 0; + } + + internal static LedState ParseLedState(this ModbusRegisters data, Int32 register, LedColor led) + { + var lo = data.ParseBool(register, (led.ConvertTo() * 2 ).ConvertTo()); + var hi = data.ParseBool(register, (led.ConvertTo() * 2 + 1).ConvertTo()); + + return (hi, lo) switch + { + (false, false) => LedState.Off, + (false, true) => LedState.On, + (true, false) => LedState.BlinkingSlow, + (true, true) => LedState.BlinkingFast, + }; + } + + + private static Boolean ParseEocReached(this ModbusRegisters data) + { + return ParseLedState(data, 1005, LedColor.Green) == LedState.On && + ParseLedState(data, 1005, LedColor.Amber) == LedState.Off && + ParseLedState(data, 1005, LedColor.Blue) == LedState.Off; + } + + internal static State ParseTemperatureState(this ModbusRegisters data) + { + return data.ParseBatteryCold() ? "cold" : "operating temperature"; // TODO: overheated, + } + + internal static Decimal ParseTemperature(this ModbusRegisters data) + { + return data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400); + } + + internal static Decimal ParseSoc(this ModbusRegisters data) + { + return data.ParseDecimal(register: 1054, scaleFactor: 0.1m); + } + + internal static State ParseEocState(this ModbusRegisters data) + { + return data.ParseEocReached() ? "EOC reached" : "EOC not reached"; + } + + internal static State ParseHeaterState(this ModbusRegisters data) + { + return data.ParseBool(baseRegister: 1014, bit: 6) ? "heating" : "not heating"; + } + + internal static State ParseMainSwitchState(this ModbusRegisters data) + { + return data.ParseBool(baseRegister: 1014, bit: 0) ? "connected to bus" : "disconnected from bus"; + } + + internal static Boolean ParseBatteryCold(this ModbusRegisters data) + { + return ParseLedState(data, 1005, LedColor.Green) >= LedState.BlinkingSlow && + ParseLedState(data, 1005, LedColor.Blue) >= LedState.BlinkingSlow; + } + + private static Decimal CalcPowerLimitImposedByVoltageLimit(Decimal v,Decimal i,Decimal vLimit,Decimal rInt) + { + var dv = vLimit - v; + var di = dv / rInt; + var pLimit = vLimit * (i + di); + + return pLimit; + } + + private static Decimal CalcPowerLimitImposedByCurrentLimit(Decimal v, Decimal i, Decimal iLimit, Decimal rInt) + { + var di = iLimit - i; + var dv = di * rInt; + var pLimit = iLimit * (v + dv); + + return pLimit; + } + + + private static Decimal CalcPowerLimitImposedByTempLimit(Decimal t, Decimal maxAllowedTemp, Decimal power , Decimal setpoint) + { + // const Int32 holdZone = 300; + // const Int32 maxAllowedTemp = 315; + + var kp = 0.05m; + var error = setpoint - power; + var controlOutput = (kp * error) *(1 - Math.Abs((t-307.5m)/7.5m)); + + return controlOutput; + + // var a = holdZone - maxAllowedTemp; + // var b = -a * maxAllowedTemp; + } + + internal static Decimal CalcMaxChargePower(this ModbusRegisters data) + { + var v = ParseCellsVoltage(data); + var i = ParseCurrent(data); + + var pLimits = new[] + { + CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMin), + CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMax, Constants.RIntMax), + CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMin), + CalcPowerLimitImposedByCurrentLimit(v, i, Constants.IMax, Constants.RIntMax) + }; + + var pLimit = pLimits.Min(); + + return Math.Max(pLimit, 0); + } + + internal static DcBus ParseDcBus(this ModbusRegisters data) + { + return new() + { + Current = data.ParseCurrent(), + Voltage = data.ParseBusVoltage(), + }; + } + + internal static Decimal CalcMaxDischargePower(this ModbusRegisters data) + { + var v = ParseCellsVoltage(data); + var i = ParseCurrent(data); + var t = data.ParseDecimal(register: 1004, scaleFactor: 0.1m, offset: -400); + + var pLimits = new[] + { + CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMin), + CalcPowerLimitImposedByVoltageLimit(v, i, Constants.VMin, Constants.RIntMax), + CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMin), + CalcPowerLimitImposedByCurrentLimit(v, i, -Constants.IMax, Constants.RIntMax), + // CalcPowerLimitImposedByTempLimit(t,315,300) + }; + + var pLimit = pLimits.Max(); + + return Math.Min(pLimit, 0); + } + + + internal static LedState ParseGreenLed(this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Green); + internal static LedState ParseAmberLed(this ModbusRegisters data) => data.ParseLedState(register: 1006, led: LedColor.Amber); + internal static LedState ParseBlueLed (this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Blue); + internal static LedState ParseRedLed (this ModbusRegisters data) => data.ParseLedState(register: 1005, led: LedColor.Red); + + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static IEnumerable ParseAlarms(this ModbusRegisters data) + { + if (data.ParseBool(1010, 0)) yield return "Tam : BMS temperature too low"; + if (data.ParseBool(1010, 2)) yield return "TaM2 : BMS temperature too high"; + if (data.ParseBool(1010, 3)) yield return "Tbm : Battery temperature too low"; + if (data.ParseBool(1010, 5)) yield return "TbM2 : Battery temperature too high"; + if (data.ParseBool(1010, 7)) yield return "VBm2 : Bus voltage too low"; + if (data.ParseBool(1010, 9)) yield return "VBM2 : Bus voltage too high"; + if (data.ParseBool(1010, 11)) yield return "IDM2 : Discharge current too high"; + if (data.ParseBool(1010, 12)) yield return "ISOB : Electrical insulation failure"; + if (data.ParseBool(1010, 13)) yield return "MSWE : Main switch failure"; + if (data.ParseBool(1010, 14)) yield return "FUSE : Main fuse blown"; + if (data.ParseBool(1010, 15)) yield return "HTRE : Battery failed to warm up"; + if (data.ParseBool(1010, 16)) yield return "TCPE : Temperature sensor failure"; + if (data.ParseBool(1010, 17)) yield return "STRE :"; + if (data.ParseBool(1010, 18)) yield return "CME : Current sensor failure"; + if (data.ParseBool(1010, 19)) yield return "HWFL : BMS hardware failure"; + if (data.ParseBool(1010, 20)) yield return "HWEM : Hardware protection tripped"; + if (data.ParseBool(1010, 21)) yield return "ThM : Heatsink temperature too high"; + if (data.ParseBool(1010, 22)) yield return "vsm1 : String voltage too low"; + if (data.ParseBool(1010, 23)) yield return "vsm2 : Low string voltage failure"; + if (data.ParseBool(1010, 25)) yield return "vsM2 : String voltage too high"; + if (data.ParseBool(1010, 27)) yield return "iCM2 : Charge current too high"; + if (data.ParseBool(1010, 29)) yield return "iDM2 : Discharge current too high"; + if (data.ParseBool(1010, 31)) yield return "MID2 : String voltage unbalance too high"; + if (data.ParseBool(1010, 33)) yield return "CCBF : Internal charger hardware failure"; + if (data.ParseBool(1010, 34)) yield return "AhFL :"; + if (data.ParseBool(1010, 36)) yield return "TbCM :"; + if (data.ParseBool(1010, 37)) yield return "BRNF :"; + if (data.ParseBool(1010, 42)) yield return "HTFS : If Heaters Fuse Blown"; + if (data.ParseBool(1010, 43)) yield return "DATA : Parameters out of range"; + if (data.ParseBool(1010, 45)) yield return "CELL2:"; + } + + [SuppressMessage("ReSharper", "StringLiteralTypo")] + internal static IEnumerable ParseWarnings(this ModbusRegisters data) + { + if (data.ParseBool(1006, 1)) yield return "TaM1: BMS temperature high"; + if (data.ParseBool(1006, 4)) yield return "TbM1: Battery temperature high"; + if (data.ParseBool(1006, 6)) yield return "VBm1: Bus voltage low"; + if (data.ParseBool(1006, 8)) yield return "VBM1: Bus voltage high"; + if (data.ParseBool(1006, 10)) yield return "IDM1: Discharge current high"; + if (data.ParseBool(1006, 24)) yield return "vsM1: String voltage high"; + if (data.ParseBool(1006, 26)) yield return "iCM1: Charge current high"; + if (data.ParseBool(1006, 28)) yield return "iDM1: Discharge current high"; + if (data.ParseBool(1006, 30)) yield return "MID1: String voltages unbalanced"; + if (data.ParseBool(1006, 32)) yield return "BLPW: Not enough charging power on bus"; + if (data.ParseBool(1006, 35)) yield return "Ah_W: String SOC low"; + if (data.ParseBool(1006, 38)) yield return "MPMM: Midpoint wiring problem"; + if (data.ParseBool(1006, 39)) yield return "TCMM:"; + if (data.ParseBool(1006, 40)) yield return "TCdi: Temperature difference between strings high"; + if (data.ParseBool(1006, 41)) yield return "WMTO:"; + if (data.ParseBool(1006, 44)) yield return "bit44:"; + if (data.ParseBool(1006, 46)) yield return "CELL1:"; + } +} \ No newline at end of file diff --git a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs index c219755b3..f26117c3f 100644 --- a/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs +++ b/csharp/Lib/Devices/EmuMeter/EmuMeterDevice.cs @@ -1,8 +1,6 @@ using InnovEnergy.Lib.Protocols.Modbus.Clients; using InnovEnergy.Lib.Protocols.Modbus.Connections; -using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Phases; -using InnovEnergy.Lib.Utils; +using InnovEnergy.Lib.Units.Composite; using static DecimalMath.DecimalEx; namespace InnovEnergy.Lib.Devices.EmuMeter; @@ -29,91 +27,71 @@ public class EmuMeterDevice return null; } } - private static Decimal GetPhi(Decimal cosPhi) => cosPhi.Clamp(-1m, 1m).Apply(ACos); + + + //private static Decimal GetPhi(Decimal cosPhi) => cosPhi.Clamp(-1m, 1m).Apply(ACos); private EmuMeterStatus TryReadStatus() { // Console.WriteLine("Reading Emu Meter Data"); + + + // TODO: get SerialNb, depends on Little/Big Endian support in Modbus Lib + // var registers = Modbus.ReadHoldingRegisters(5001, 4); + // var id = registers.GetInt32(5001); var powerCurrent = Modbus.ReadHoldingRegisters(9000, 108).ToDecimals(); // TODO "ModbusRegisters" var voltageFreq = Modbus.ReadHoldingRegisters(9200, 112).ToDecimals(); // To check with Ivo - var energyTotal = Modbus.ReadHoldingRegisters(6000, 24) .ToUInt64s(); - var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s(); + + // var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s(); - var activePowerL123 = powerCurrent[0]; var activePowerL1 = powerCurrent[1]; var activePowerL2 = powerCurrent[2]; var activePowerL3 = powerCurrent[3]; - var reactivePowerL123 = powerCurrent[5]; var reactivePowerL1 = powerCurrent[6]; var reactivePowerL2 = powerCurrent[7]; var reactivePowerL3 = powerCurrent[8]; - var apparentPowerL123 = powerCurrent[10]; - var apparentPowerL1 = powerCurrent[11]; - var apparentPowerL2 = powerCurrent[12]; - var apparentPowerL3 = powerCurrent[13]; - var currentL123 = powerCurrent[50]; + var currentL1 = powerCurrent[51]; var currentL2 = powerCurrent[52]; var currentL3 = powerCurrent[53]; + var voltageL1N = voltageFreq[0]; var voltageL2N = voltageFreq[1]; var voltageL3N = voltageFreq[2]; - var voltageL1L2 = voltageFreq[3]; - var voltageL2L3 = voltageFreq[4]; - var voltageL3L1 = voltageFreq[5]; - var powerFactorL1 = voltageFreq[50]; - var powerFactorL2 = voltageFreq[51]; - var powerFactorL3 = voltageFreq[52]; var frequency = voltageFreq[55]; - var energyImportL123 = energyTotal[0 / 4] / 1000.0m; - var energyExportL123 = energyTotal[20 / 4] / 1000.0m; - var energyImportL1 = energyPhases[0 / 4] / 1000.0m; - var energyExportL1 = energyPhases[20 / 4] / 1000.0m; - var energyImportL2 = energyPhases[40 / 4] / 1000.0m; - var energyExportL2 = energyPhases[60 / 4] / 1000.0m; - var energyImportL3 = energyPhases[80 / 4] / 1000.0m; - var energyExportL3 = energyPhases[100 / 4] / 1000.0m; + + + var l1 = new AcPhase + { + Current = currentL1, + Voltage = voltageL1N, + Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works + }; + var l2 = new AcPhase + { + Current = currentL2, + Voltage = voltageL2N, + Phi = ATan2(reactivePowerL2, activePowerL2) + }; + var l3 = new AcPhase + { + Current = currentL3, + Voltage = voltageL3N, + Phi = ATan2(reactivePowerL3, activePowerL3) + }; return new EmuMeterStatus - ( - Ac: new ThreePhaseAcConnection - ( - new AcPhase( - voltageL1N, - currentL1, - GetPhi(powerFactorL1) - ), - - new AcPhase( - voltageL2N, - currentL2, - GetPhi(powerFactorL2) - ), - - new AcPhase( - voltageL3N, - currentL3, - GetPhi(powerFactorL3) - ), - frequency - ), - activePowerL123, - reactivePowerL123, - apparentPowerL123, - currentL123, - voltageL1L2, - voltageL2L3, - voltageL3L1, - energyImportL123, - energyImportL1, - energyImportL2, - energyImportL3, - energyExportL123, - energyExportL1, - energyExportL2, - energyExportL3 - ); + { + Ac = new Ac3Bus + { + Frequency = frequency, + L1 = l1, + L2 = l2, + L3 = l3 + } + }; + } } \ No newline at end of file diff --git a/csharp/Lib/Devices/EmuMeter/EmuMeterStatus.cs b/csharp/Lib/Devices/EmuMeter/EmuMeterStatus.cs index 713a4b2de..1290843af 100644 --- a/csharp/Lib/Devices/EmuMeter/EmuMeterStatus.cs +++ b/csharp/Lib/Devices/EmuMeter/EmuMeterStatus.cs @@ -1,26 +1,10 @@ -using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Devices; +using InnovEnergy.Lib.StatusApi; namespace InnovEnergy.Lib.Devices.EmuMeter; -public record EmuMeterStatus -( - ThreePhaseAcConnection Ac, - Decimal ActivePowerL123, - Decimal ReactivePowerL123, - Decimal ApparentPowerL123, - Decimal CurrentL123, - Decimal VoltageL1L2, - Decimal VoltageL2L3, - Decimal VoltageL3L1, - Decimal EnergyImportL123, - Decimal EnergyImportL1, - Decimal EnergyImportL2, - Decimal EnergyImportL3, - Decimal EnergyExportL123, - Decimal EnergyExportL1, - Decimal EnergyExportL2, - Decimal EnergyExportL3 -):GridMeter(Ac) -{} +public record EmuMeterStatus : PowerMeterStatus +{ + // TODO: additional Measurements, device id +} + diff --git a/csharp/Lib/Devices/EmuMeter/doc/ModbusRegisters.pdf b/csharp/Lib/Devices/EmuMeter/doc/ModbusRegisters.pdf new file mode 100644 index 000000000..77bd4acf6 Binary files /dev/null and b/csharp/Lib/Devices/EmuMeter/doc/ModbusRegisters.pdf differ diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDevice.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDevice.cs index 6308553aa..66cc153a7 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDevice.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcDevice.cs @@ -2,8 +2,7 @@ using System.Diagnostics.CodeAnalysis; using InnovEnergy.Lib.Devices.Trumpf.TruConvert; using InnovEnergy.Lib.Protocols.Modbus.Clients; using InnovEnergy.Lib.Protocols.Modbus.Connections; -using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Phases; +using InnovEnergy.Lib.Units.Composite; using InnovEnergy.Lib.Utils; using static DecimalMath.DecimalEx; using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters; @@ -216,7 +215,7 @@ public class TruConvertAcDevice return new TruConvertAcStatus ( - Ac: new ThreePhaseAcConnection + Ac: new Ac3Bus ( new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)), new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)), diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcStatus.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcStatus.cs index 8f2047ebb..4747e0579 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcStatus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/TruConvertAcStatus.cs @@ -1,6 +1,6 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvert; using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Devices; +using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; @@ -9,7 +9,7 @@ using WarningMessages = IReadOnlyList; public record TruConvertAcStatus ( - ThreePhaseAcConnection Ac, + Ac3Bus Ac, DcConnection Dc, String SerialNumber, MainState MainState, diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDcStatus.cs b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDcStatus.cs index 88551dee1..bf78b928b 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDcStatus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/TruConvertDcStatus.cs @@ -1,6 +1,7 @@ -using InnovEnergy.Lib.Devices.Trumpf.TruConvert; -using InnovEnergy.Lib.StatusApi.Connections; -using InnovEnergy.Lib.StatusApi.Devices; +using InnovEnergy.Lib.StatusApi; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; @@ -8,20 +9,24 @@ using AlarmMessages = IReadOnlyList; using WarningMessages = IReadOnlyList; using DcCurrentLimitStates = IReadOnlyList; -public record TruConvertDcStatus +public record TruConvertDcStatus ( - DcConnection Dc, - MainState MainState, - UInt16 NumberOfConnectedSlaves, - UInt16 NumberOfConnectedSubSlaves, - Decimal BatteryVoltage, - Decimal BatteryCurrent, - Decimal TotalDcPower, - DcCurrentLimitStates StatusOfCurrentLimiting, - Decimal OverloadCapacity, - Decimal DcDcInletTemperature, - AlarmMessages Alarms, - WarningMessages Warnings, - Boolean PowerOperation -):DcDevice(Dc) -{} \ No newline at end of file + DcBus DcLeft, + DcBus DcRight, + State MainState, + Power TotalDcPower, // TODO: necessary? + State StatusOfCurrentLimiting, + Decimal OverloadCapacity, + Temperature DcDcInletTemperature, + State Alarms, + State Warnings, + State PowerOperation + + // UInt16 NumberOfConnectedSlaves, // TODO: necessary? + // UInt16 NumberOfConnectedSubSlaves, // TODO: necessary? +) : + DcDcConverterStatus(DcLeft, DcRight) +{ + public static TruConvertDcStatus operator |(TruConvertDcStatus left, TruConvertDcStatus right) => OpParallel(left, right); + private static readonly Func OpParallel = Operators.Op("|"); +} \ No newline at end of file diff --git a/csharp/Lib/Protocols/Modbus/Clients/Endianness.cs b/csharp/Lib/Protocols/Modbus/Clients/Endianness.cs new file mode 100644 index 000000000..d53103dec --- /dev/null +++ b/csharp/Lib/Protocols/Modbus/Clients/Endianness.cs @@ -0,0 +1,10 @@ +namespace InnovEnergy.Lib.Protocols.Modbus.Clients; + +[Flags] +public enum Endianness +{ + LittleEndian32BitIntegers = 0, + LittleEndian32Floats = 0, + BigEndian32BitIntegers = 1, + BigEndian32Floats = 2, +} \ No newline at end of file diff --git a/csharp/Lib/Protocols/Modbus/Clients/ModbusClient.cs b/csharp/Lib/Protocols/Modbus/Clients/ModbusClient.cs index 66576d9d1..1bc736622 100644 --- a/csharp/Lib/Protocols/Modbus/Clients/ModbusClient.cs +++ b/csharp/Lib/Protocols/Modbus/Clients/ModbusClient.cs @@ -1,6 +1,6 @@ -using System.Threading.Channels; using InnovEnergy.Lib.Protocols.Modbus.Connections; using InnovEnergy.Lib.Protocols.Modbus.Conversions; +using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness; namespace InnovEnergy.Lib.Protocols.Modbus.Clients; @@ -13,8 +13,9 @@ public abstract class ModbusClient { protected ModbusConnection Connection { get; } protected Byte SlaveId { get; } - - + protected Endianness Endianness { get; } + + // TODO: add additional functions: coils... public abstract Coils ReadDiscreteInputs (UInt16 readAddress, UInt16 nValues); @@ -40,11 +41,11 @@ public abstract class ModbusClient return WriteRegisters(writeAddress, (IReadOnlyList)values); } - - protected ModbusClient(ModbusConnection connection, Byte slaveId) + protected ModbusClient(ModbusConnection connection, Byte slaveId, Endianness endianness = LittleEndian32BitIntegers | LittleEndian32Floats) { Connection = connection; SlaveId = slaveId; + Endianness = endianness; } public void CloseConnection() => Connection.Close(); diff --git a/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs b/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs index 89824106c..c0506cbf3 100644 --- a/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs +++ b/csharp/Lib/Protocols/Modbus/Clients/ModbusTcpClient.cs @@ -4,13 +4,13 @@ using InnovEnergy.Lib.Protocols.Modbus.Conversions; using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Commands; using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies; using InnovEnergy.Lib.Protocols.Modbus.Tcp; +using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness; namespace InnovEnergy.Lib.Protocols.Modbus.Clients; using UInt16s = IReadOnlyList; using Coils = IReadOnlyList; - public class ModbusTcpClient : ModbusClient { public const UInt16 DefaultPort = 502; @@ -20,7 +20,7 @@ public class ModbusTcpClient : ModbusClient private UInt16 NextId() => unchecked(++_Id); - public ModbusTcpClient(ModbusConnection connection, Byte slaveId) : base(connection, slaveId) + public ModbusTcpClient(ModbusConnection connection, Byte slaveId, Endianness endianness = LittleEndian32BitIntegers | LittleEndian32Floats) : base(connection, slaveId, endianness) { } diff --git a/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Bit.cs b/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Bit.cs index e2e02ed7e..2ab1061dc 100644 --- a/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Bit.cs +++ b/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Bit.cs @@ -1,4 +1,5 @@ using System.Collections; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Protocols.Modbus.Conversions; @@ -8,7 +9,7 @@ public partial class ModbusRegisters { var offset = index - StartRegister; - var byteArray = BitConverter.GetBytes(Registers[offset]).Reverse().ToArray(); + var byteArray = Registers[offset].Apply(BitConverter.GetBytes).Reverse().ToArray(); var bitArray = new BitArray(byteArray); return bitArray.Get(bitIndex); @@ -18,7 +19,7 @@ public partial class ModbusRegisters { var offset = index - StartRegister; - var byteArray = BitConverter.GetBytes(Registers[offset]).Reverse().ToArray(); + var byteArray = Registers[offset].Apply(BitConverter.GetBytes).Reverse().ToArray(); var bitArray = new BitArray(byteArray); bitArray.Set(bitIndex, value); diff --git a/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Single.cs b/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Single.cs index a6408185e..26f5776e6 100644 --- a/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Single.cs +++ b/csharp/Lib/Protocols/Modbus/Conversions/ModbusRegisters.Single.cs @@ -9,7 +9,7 @@ public partial class ModbusRegisters var bytearray = BitConverter.GetBytes(value).Reverse().ToArray(); var value32 = BitConverter.ToUInt32(bytearray); - Registers[index - StartRegister] = (UInt16)(value32 >> 16); + Registers[index - StartRegister ] = (UInt16)(value32 >> 16); Registers[index - StartRegister + 1] = (UInt16)(value32 & 0xFFFF); } diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/Accessors.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/Accessors.cs index 583ad6979..92282070e 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/Accessors.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/Accessors.cs @@ -9,8 +9,8 @@ public static class Accessors public static MbWord WordAt (this ArraySegment data, Byte i) => new MbWord(data, i); public static MbAddress AddressAt(this ArraySegment data, Byte i) => new MbAddress(data, i); - public static MbWords WordsAt(this ArraySegment data, Byte i) => new MbWords(data, i); - public static MbBits BitsAt (this ArraySegment data, Byte i) => new MbBits(data, i); + public static MbRegisters RegistersAt(this ArraySegment data, Byte i) => new MbRegisters(data, i); + public static MbBits BitsAt (this ArraySegment data, Byte i) => new MbBits(data, i); public static MbByte ByteAt(this ArraySegment data,Byte i) where T : struct, IConvertible => new MbByte(data, i); diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbWords.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbRegisters.cs similarity index 79% rename from csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbWords.cs rename to csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbRegisters.cs index 30876dceb..a2124a9fa 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbWords.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Accessors/MbRegisters.cs @@ -3,30 +3,30 @@ using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors; -public readonly struct MbWords : IReadOnlyList +public readonly struct MbRegisters : IReadOnlyList { private readonly ArraySegment _Data; - internal MbWords(ArraySegment data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex)) + internal MbRegisters(ArraySegment data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex)) { } + + + internal MbRegisters(ArraySegment data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount) + { + } + + internal MbRegisters(Byte[] data, Byte startIndex, UInt16 wordCount) + { + _Data = new ArraySegment(data, startIndex, wordCount * 2); + } + private static UInt16 CountWords(ArraySegment data, Byte startIndex) { var wordCount = (data.Count - startIndex) / 2; return wordCount.ConvertTo(); } - internal MbWords(ArraySegment data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount) - { - - } - - - internal MbWords(Byte[] data, Byte startIndex, UInt16 wordCount) - { - _Data = new ArraySegment(data, startIndex, wordCount * 2); - } - internal IReadOnlyCollection Set(IReadOnlyCollection values) { if (values.Count != _Data.Count / 2) diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/ReadWriteRegistersCommandFrame.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/ReadWriteRegistersCommandFrame.cs index 1e4c8366f..57f86d379 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/ReadWriteRegistersCommandFrame.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/ReadWriteRegistersCommandFrame.cs @@ -16,7 +16,7 @@ internal class ReadWriteRegistersCommandFrame : ModbusFrame public MbAddress WriteAddress => Data.AddressAt(6); public MbWord NbToWrite => Data.WordAt(8); public MbByte ByteCount => Data.ByteAt(10); - public MbWords RegistersToWrite => Data.WordsAt(11); + public MbRegisters RegistersToWrite => Data.RegistersAt(11); public Int32 ExpectedResponseSize => ReadWriteRegistersResponseFrame.ExpectedSize(NbToRead); diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/WriteRegistersCommandFrame.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/WriteRegistersCommandFrame.cs index af925ff9e..8eb4ca915 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/WriteRegistersCommandFrame.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Commands/WriteRegistersCommandFrame.cs @@ -14,7 +14,7 @@ internal class WriteRegistersCommandFrame : ModbusFrame public MbAddress WriteAddress => Data.AddressAt(2); public MbWord NbOfRegisters => Data.WordAt(4); public MbByte ByteCount => Data.ByteAt(6); - public MbWords RegistersToWrite => Data.WordsAt(7); + public MbRegisters RegistersToWrite => Data.RegistersAt(7); public Int32 ExpectedResponseSize => WriteRegistersResponseFrame.ExpectedSize(); diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadHoldingRegistersResponseFrame.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadHoldingRegistersResponseFrame.cs index 7fcd77983..ba8b3e707 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadHoldingRegistersResponseFrame.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadHoldingRegistersResponseFrame.cs @@ -11,7 +11,7 @@ internal class ReadHoldingRegistersResponseFrame : ModbusFrame internal new const Int32 MinSize = 3; public MbByte ByteCount => Data.ByteAt(2); - public MbWords RegistersRead => Data.WordsAt(3); + public MbRegisters RegistersRead => Data.RegistersAt(3); public ReadHoldingRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count)) diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadInputRegistersResponseFrame.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadInputRegistersResponseFrame.cs index 29cebda39..bb301e638 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadInputRegistersResponseFrame.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadInputRegistersResponseFrame.cs @@ -12,7 +12,7 @@ internal class ReadInputRegistersResponseFrame : ModbusFrame internal new const Int32 MinSize = 3; public MbByte ByteCount => Data.ByteAt(2); - public MbWords RegistersRead => Data.WordsAt(3); + public MbRegisters RegistersRead => Data.RegistersAt(3); public ReadInputRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count)) { diff --git a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadWriteRegistersCommandFrame.cs b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadWriteRegistersCommandFrame.cs index 43eed5e21..ce7ecf2da 100644 --- a/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadWriteRegistersCommandFrame.cs +++ b/csharp/Lib/Protocols/Modbus/Protocol/Frames/Replies/ReadWriteRegistersCommandFrame.cs @@ -12,7 +12,7 @@ internal class ReadWriteRegistersResponseFrame : ModbusFrame internal new const Int32 MinSize = 3; public MbByte ByteCount => Data.ByteAt(2); - public MbWords RegistersRead => Data.WordsAt(3); + public MbRegisters RegistersRead => Data.RegistersAt(3); public ReadWriteRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count)) { diff --git a/csharp/Lib/StatusApi/BatteryStatus.cs b/csharp/Lib/StatusApi/BatteryStatus.cs index 0b033b420..f8a0124d5 100644 --- a/csharp/Lib/StatusApi/BatteryStatus.cs +++ b/csharp/Lib/StatusApi/BatteryStatus.cs @@ -1,7 +1,17 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; +using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record BatteryStatus(DcPhase Dc) : DeviceStatus, IDcConnection; +using T = BatteryStatus; + +[OpParallel] +public partial record BatteryStatus : DeviceStatus, IDcConnection +{ + public DcBus Dc { get; init; } + public Percent Soc { get; init; } + public Temperature Temperature { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/BatteryStatus.generated.cs b/csharp/Lib/StatusApi/BatteryStatus.generated.cs new file mode 100644 index 000000000..d5f549056 --- /dev/null +++ b/csharp/Lib/StatusApi/BatteryStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = BatteryStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record BatteryStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Connections/IAc1Connection.cs b/csharp/Lib/StatusApi/Connections/IAc1Connection.cs index d90b57938..54060cb20 100644 --- a/csharp/Lib/StatusApi/Connections/IAc1Connection.cs +++ b/csharp/Lib/StatusApi/Connections/IAc1Connection.cs @@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections; public interface IAc1Connection { - Ac1Phase Ac { get; } + Ac1Bus Ac { get; } } \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Connections/IAc3Connection.cs b/csharp/Lib/StatusApi/Connections/IAc3Connection.cs index 231982cf0..a1c0ca1b5 100644 --- a/csharp/Lib/StatusApi/Connections/IAc3Connection.cs +++ b/csharp/Lib/StatusApi/Connections/IAc3Connection.cs @@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections; public interface IAc3Connection { - Ac1Phase Ac3 { get; } + Ac3Bus Ac { get; } } \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Connections/IDcConnection.cs b/csharp/Lib/StatusApi/Connections/IDcConnection.cs index 6074a6750..04d83289f 100644 --- a/csharp/Lib/StatusApi/Connections/IDcConnection.cs +++ b/csharp/Lib/StatusApi/Connections/IDcConnection.cs @@ -5,5 +5,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections; public interface IDcConnection { - DcPhase Dc { get; } + DcBus Dc { get; } } \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Connections/IPvConnection.cs b/csharp/Lib/StatusApi/Connections/IPvConnection.cs index a117f2d17..6dfddccea 100644 --- a/csharp/Lib/StatusApi/Connections/IPvConnection.cs +++ b/csharp/Lib/StatusApi/Connections/IPvConnection.cs @@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections; public interface IPvConnection { - IReadOnlyList Strings { get; } + IReadOnlyList Strings { get; } } \ No newline at end of file diff --git a/csharp/Lib/StatusApi/DcDcConverterStatus.cs b/csharp/Lib/StatusApi/DcDcConverterStatus.cs index cdda395d9..21941d5e6 100644 --- a/csharp/Lib/StatusApi/DcDcConverterStatus.cs +++ b/csharp/Lib/StatusApi/DcDcConverterStatus.cs @@ -1,8 +1,14 @@ +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record DcDcConverterStatus(DcPhase Left, DcPhase Right) : DeviceStatus; +[OpParallel] +public partial record DcDcConverterStatus : DeviceStatus +{ + public DcBus Left { get; init; } + public DcBus Right { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/DcDcConverterStatus.generated.cs b/csharp/Lib/StatusApi/DcDcConverterStatus.generated.cs new file mode 100644 index 000000000..32a133a8a --- /dev/null +++ b/csharp/Lib/StatusApi/DcDcConverterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = DcDcConverterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record DcDcConverterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/DeviceStatus.cs b/csharp/Lib/StatusApi/DeviceStatus.cs index 6cdc1ef3f..380007d85 100644 --- a/csharp/Lib/StatusApi/DeviceStatus.cs +++ b/csharp/Lib/StatusApi/DeviceStatus.cs @@ -5,8 +5,48 @@ namespace InnovEnergy.Lib.StatusApi; public abstract record DeviceStatus { public String DeviceType => GetType() - .Generate(t => t.BaseType!) + .Unfold(t => t.BaseType) .First(t => t.IsAbstract) .Name .Replace("Status", ""); -} \ No newline at end of file +} + + +// public static class Program +// { +// public static void Main(string[] args) +// { +// var x = new ThreePhasePvInverterStatus +// { +// Ac = new() +// { +// Frequency = 50, +// L1 = new() +// { +// Current = 10, +// Voltage = 10, +// Phi = 0, +// }, +// L2 = new() +// { +// Current = 52, +// Voltage = 220, +// Phi = Angle.Pi / 2, +// }, +// L3 = new() +// { +// Current = 158, +// Voltage = 454, +// Phi = Angle.Pi / 3, +// }, +// }, +// Strings = new DcBus[] +// { +// new() { Current = 10, Voltage = 22 }, +// new() { Current = 12, Voltage = 33 }, +// } +// }; +// +// var s = x.ToJson(); +// } +// } \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Generator/OpParallelAttribute.cs b/csharp/Lib/StatusApi/Generator/OpParallelAttribute.cs new file mode 100644 index 000000000..ec94f9d34 --- /dev/null +++ b/csharp/Lib/StatusApi/Generator/OpParallelAttribute.cs @@ -0,0 +1,5 @@ +namespace InnovEnergy.Lib.StatusApi.Generator; + +[AttributeUsage(AttributeTargets.Class)] +internal class OpParallelAttribute : Attribute +{} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Generator/Template.txt b/csharp/Lib/StatusApi/Generator/Template.txt new file mode 100644 index 000000000..43f22557f --- /dev/null +++ b/csharp/Lib/StatusApi/Generator/Template.txt @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = Template; + +[GeneratedCode("generate.sh", "1")] +public partial record Template +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Generator/generate.sh b/csharp/Lib/StatusApi/Generator/generate.sh new file mode 100755 index 000000000..1233bd1b7 --- /dev/null +++ b/csharp/Lib/StatusApi/Generator/generate.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + + +scriptDir=$( dirname -- "$0"; ) +cd "$scriptDir/.." || exit + +for path in $(grep -e '\[OpParallel\]' -l *.cs) +do + file=$(basename -- "$path") + class="${file%.*}" + echo "generating $file" + sed "s/Template/$class/g" "./Generator/Template.txt" > "./$class.generated.cs" +done \ No newline at end of file diff --git a/csharp/Lib/StatusApi/MpptStatus.cs b/csharp/Lib/StatusApi/MpptStatus.cs index bec31ddec..53acdf439 100644 --- a/csharp/Lib/StatusApi/MpptStatus.cs +++ b/csharp/Lib/StatusApi/MpptStatus.cs @@ -1,8 +1,14 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public record MpptStatus(DcPhase Dc, IReadOnlyList Strings) : IDcConnection, IPvConnection; +[OpParallel] +public partial record MpptStatus : IDcConnection, IPvConnection +{ + public DcBus Dc { get; init; } + public IReadOnlyList Strings { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/MpptStatus.generated.cs b/csharp/Lib/StatusApi/MpptStatus.generated.cs new file mode 100644 index 000000000..5e2afe796 --- /dev/null +++ b/csharp/Lib/StatusApi/MpptStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = MpptStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record MpptStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/PowerMeterStatus.cs b/csharp/Lib/StatusApi/PowerMeterStatus.cs index 41038f4dc..8140afef3 100644 --- a/csharp/Lib/StatusApi/PowerMeterStatus.cs +++ b/csharp/Lib/StatusApi/PowerMeterStatus.cs @@ -1,6 +1,11 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record PowerMeterStatus(Ac1Phase Ac3) : DeviceStatus, IAc3Connection; \ No newline at end of file +[OpParallel] +public partial record PowerMeterStatus : DeviceStatus, IAc3Connection +{ + public Ac3Bus Ac { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/PowerMeterStatus.generated.cs b/csharp/Lib/StatusApi/PowerMeterStatus.generated.cs new file mode 100644 index 000000000..9e6dcaf9c --- /dev/null +++ b/csharp/Lib/StatusApi/PowerMeterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = PowerMeterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record PowerMeterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/SinglePhaseInverterStatus.cs b/csharp/Lib/StatusApi/SinglePhaseInverterStatus.cs index 9e1e2c8ba..6688d8886 100644 --- a/csharp/Lib/StatusApi/SinglePhaseInverterStatus.cs +++ b/csharp/Lib/StatusApi/SinglePhaseInverterStatus.cs @@ -1,9 +1,15 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record SinglePhaseInverterStatus(Ac1Phase Ac, DcPhase Dc) : +[OpParallel] +public partial record SinglePhaseInverterStatus : DeviceStatus, IAc1Connection, - IDcConnection; + IDcConnection +{ + public Ac1Bus Ac { get; init; } + public DcBus Dc { get; init; } +} diff --git a/csharp/Lib/StatusApi/SinglePhaseInverterStatus.generated.cs b/csharp/Lib/StatusApi/SinglePhaseInverterStatus.generated.cs new file mode 100644 index 000000000..047f609fc --- /dev/null +++ b/csharp/Lib/StatusApi/SinglePhaseInverterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = SinglePhaseInverterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record SinglePhaseInverterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.cs b/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.cs index 51c113fce..269254b6e 100644 --- a/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.cs +++ b/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.cs @@ -1,9 +1,15 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record SinglePhasePvInverterStatus(Ac1Phase Ac, IReadOnlyList Strings) : +[OpParallel] +public partial record SinglePhasePvInverterStatus : DeviceStatus, IAc1Connection, - IPvConnection; + IPvConnection +{ + public Ac1Bus Ac { get; init; } + public IReadOnlyList Strings { get; init; } +} diff --git a/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.generated.cs b/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.generated.cs new file mode 100644 index 000000000..37340f111 --- /dev/null +++ b/csharp/Lib/StatusApi/SinglePhasePvInverterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = SinglePhasePvInverterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record SinglePhasePvInverterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/StatusApi.csproj b/csharp/Lib/StatusApi/StatusApi.csproj index 88fda982e..b68a8b119 100644 --- a/csharp/Lib/StatusApi/StatusApi.csproj +++ b/csharp/Lib/StatusApi/StatusApi.csproj @@ -1,11 +1,16 @@ + - + + + + + diff --git a/csharp/Lib/StatusApi/ThreePhaseInverterStatus.cs b/csharp/Lib/StatusApi/ThreePhaseInverterStatus.cs index 8c2fc424f..c525a6a67 100644 --- a/csharp/Lib/StatusApi/ThreePhaseInverterStatus.cs +++ b/csharp/Lib/StatusApi/ThreePhaseInverterStatus.cs @@ -1,10 +1,16 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record ThreePhaseInverterStatus(Ac1Phase Ac3, DcPhase Dc) : +[OpParallel] +public partial record ThreePhaseInverterStatus : DeviceStatus, IAc3Connection, - IDcConnection; + IDcConnection +{ + public Ac3Bus Ac { get; init; } + public DcBus Dc { get; init; } +} \ No newline at end of file diff --git a/csharp/Lib/StatusApi/ThreePhaseInverterStatus.generated.cs b/csharp/Lib/StatusApi/ThreePhaseInverterStatus.generated.cs new file mode 100644 index 000000000..989941505 --- /dev/null +++ b/csharp/Lib/StatusApi/ThreePhaseInverterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = ThreePhaseInverterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record ThreePhaseInverterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.cs b/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.cs index c09471d4b..8c122ffb3 100644 --- a/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.cs +++ b/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.cs @@ -1,9 +1,15 @@ using InnovEnergy.Lib.StatusApi.Connections; +using InnovEnergy.Lib.StatusApi.Generator; using InnovEnergy.Lib.Units.Composite; namespace InnovEnergy.Lib.StatusApi; -public abstract record ThreePhasePvInverterStatus(Ac1Phase Ac3, IReadOnlyList Strings) : +[OpParallel] +public partial record ThreePhasePvInverterStatus : DeviceStatus, IAc3Connection, - IPvConnection; + IPvConnection +{ + public Ac3Bus Ac { get; init; } + public IReadOnlyList Strings { get; init; } +} diff --git a/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.generated.cs b/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.generated.cs new file mode 100644 index 000000000..4e7170a21 --- /dev/null +++ b/csharp/Lib/StatusApi/ThreePhasePvInverterStatus.generated.cs @@ -0,0 +1,16 @@ +#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. +using System.CodeDom.Compiler; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.StatusApi; + +using T = ThreePhasePvInverterStatus; + +[GeneratedCode("generate.sh", "1")] +public partial record ThreePhasePvInverterStatus +{ + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); + public static T operator |(T left, T right) => OpParallel(left, right); +} + \ No newline at end of file diff --git a/csharp/Lib/StatusApi/Utils.cs b/csharp/Lib/StatusApi/Utils.cs deleted file mode 100644 index b86db8478..000000000 --- a/csharp/Lib/StatusApi/Utils.cs +++ /dev/null @@ -1,11 +0,0 @@ -using InnovEnergy.Lib.Utils; - -namespace InnovEnergy.Lib.StatusApi; - -public static class Utils -{ - public static Decimal Round3(this Decimal d) - { - return d.RoundToSignificantFigures(3); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/SysTools.csproj b/csharp/Lib/SysTools/SysTools.csproj index 629608dc6..72e17cc9b 100644 --- a/csharp/Lib/SysTools/SysTools.csproj +++ b/csharp/Lib/SysTools/SysTools.csproj @@ -6,7 +6,7 @@ - + diff --git a/csharp/Lib/Time/Unix/UnixTimeDelta.Compare.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.Compare.cs similarity index 100% rename from csharp/Lib/Time/Unix/UnixTimeDelta.Compare.cs rename to csharp/Lib/Time/Unix/UnixTimeSpan.Compare.cs diff --git a/csharp/Lib/Time/Unix/UnixTimeDelta.Constructors.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.Constructors.cs similarity index 100% rename from csharp/Lib/Time/Unix/UnixTimeDelta.Constructors.cs rename to csharp/Lib/Time/Unix/UnixTimeSpan.Constructors.cs diff --git a/csharp/Lib/Time/Unix/UnixTimeDelta.Operators.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.Operators.cs similarity index 94% rename from csharp/Lib/Time/Unix/UnixTimeDelta.Operators.cs rename to csharp/Lib/Time/Unix/UnixTimeSpan.Operators.cs index dc2805e2d..e59d3dfba 100644 --- a/csharp/Lib/Time/Unix/UnixTimeDelta.Operators.cs +++ b/csharp/Lib/Time/Unix/UnixTimeSpan.Operators.cs @@ -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, 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, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b); diff --git a/csharp/Lib/Time/Unix/UnixTimeDelta.Overrides.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.Overrides.cs similarity index 100% rename from csharp/Lib/Time/Unix/UnixTimeDelta.Overrides.cs rename to csharp/Lib/Time/Unix/UnixTimeSpan.Overrides.cs diff --git a/csharp/Lib/Time/Unix/UnixTimeDeltaExtensions.cs b/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs similarity index 100% rename from csharp/Lib/Time/Unix/UnixTimeDeltaExtensions.cs rename to csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs diff --git a/csharp/Lib/Units/Angle.generated.cs b/csharp/Lib/Units/Angle.generated.cs index ad576594a..837f0b2a2 100644 --- a/csharp/Lib/Units/Angle.generated.cs +++ b/csharp/Lib/Units/Angle.generated.cs @@ -2,15 +2,19 @@ #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 = Angle; +[JsonConverter(typeof(AngleConverter))] public readonly partial struct Angle { public Decimal Value { get; } - public override String ToString() => Value + Unit; + public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit; // scalar multiplication @@ -18,25 +22,24 @@ public readonly partial struct Angle 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); - // addition + // parallel #if Sum - public static T operator +(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T left, T right) => new T(left.Value - right.Value); + 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); + 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) + public static T operator |(T left, T right) { var d = Max(Abs(left.Value), Abs(right.Value)); - if (d != 0m) + if (d == 0m) return new T(0m); var relativeError = Abs(left.Value - right.Value) / d; @@ -76,3 +79,19 @@ public readonly partial struct Angle public override Int32 GetHashCode() => Value.GetHashCode(); } + + +internal class AngleConverter : JsonConverter +{ + public override Angle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new Angle(reader.GetDecimal()); + } + + public override void Write(Utf8JsonWriter writer, Angle value, JsonSerializerOptions options) + { + var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits); + + writer.WriteNumberValue(rounded); + } +} \ No newline at end of file diff --git a/csharp/Lib/Units/ApparentPower.generated.cs b/csharp/Lib/Units/ApparentPower.generated.cs index cfa6174ed..a72053726 100644 --- a/csharp/Lib/Units/ApparentPower.generated.cs +++ b/csharp/Lib/Units/ApparentPower.generated.cs @@ -2,15 +2,19 @@ #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 = ApparentPower; +[JsonConverter(typeof(ApparentPowerConverter))] public readonly partial struct ApparentPower { public Decimal Value { get; } - public override String ToString() => Value + Unit; + public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit; // scalar multiplication @@ -18,25 +22,24 @@ public readonly partial struct ApparentPower 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); - // addition + // parallel #if Sum - public static T operator +(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T left, T right) => new T(left.Value - right.Value); + 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); + 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) + public static T operator |(T left, T right) { var d = Max(Abs(left.Value), Abs(right.Value)); - if (d != 0m) + if (d == 0m) return new T(0m); var relativeError = Abs(left.Value - right.Value) / d; @@ -76,3 +79,19 @@ public readonly partial struct ApparentPower public override Int32 GetHashCode() => Value.GetHashCode(); } + + +internal class ApparentPowerConverter : JsonConverter +{ + public override ApparentPower Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new ApparentPower(reader.GetDecimal()); + } + + public override void Write(Utf8JsonWriter writer, ApparentPower value, JsonSerializerOptions options) + { + var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits); + + writer.WriteNumberValue(rounded); + } +} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/Ac1Bus.cs b/csharp/Lib/Units/Composite/Ac1Bus.cs new file mode 100644 index 000000000..f2c769f24 --- /dev/null +++ b/csharp/Lib/Units/Composite/Ac1Bus.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; + +namespace InnovEnergy.Lib.Units.Composite; + +public record Ac1Bus : AcPhase +{ + public Frequency Frequency { get; init; } + + [SuppressMessage("ReSharper", "RedundantCast")] + public static Ac1Bus operator |(Ac1Bus left, Ac1Bus right) + { + var f = left.Frequency | right.Frequency; + var p = (AcPhase)left | (AcPhase)right; + + return new Ac1Bus + { + Frequency = f, + Current = p.Current, + Voltage = p.Voltage, + Phi = p.Phi + }; + } + +} + + + diff --git a/csharp/Lib/Units/Composite/Ac1Phase.cs b/csharp/Lib/Units/Composite/Ac1Phase.cs deleted file mode 100644 index e48807866..000000000 --- a/csharp/Lib/Units/Composite/Ac1Phase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace InnovEnergy.Lib.Units.Composite; - -public record Ac1Phase -( - Voltage Voltage, - Current Current, - Angle Phi, - Frequency Frequency -) - : AcPhase(Voltage, Current, Phi) -{ - - [SuppressMessage("ReSharper", "RedundantCast")] - public static Ac1Phase operator +(Ac1Phase left, Ac1Phase right) - { - var f = (left.Frequency + right.Frequency) / 2m; // TODO: check that l & r approximately equal - var acPhase = (AcPhase)left + (AcPhase)right; - return new Ac1Phase(acPhase.Voltage, acPhase.Current, acPhase.Phi, f); - } - -} - - - diff --git a/csharp/Lib/Units/Composite/Ac3Bus.cs b/csharp/Lib/Units/Composite/Ac3Bus.cs new file mode 100644 index 000000000..e35c73cfc --- /dev/null +++ b/csharp/Lib/Units/Composite/Ac3Bus.cs @@ -0,0 +1,20 @@ +using InnovEnergy.Lib.Utils; +using static DecimalMath.DecimalEx; + +namespace InnovEnergy.Lib.Units.Composite; + +public record Ac3Bus +{ + public AcPhase L1 { get; init; } + public AcPhase L2 { get; init; } + public AcPhase L3 { get; init; } + public Frequency Frequency { get; init; } + + public ApparentPower ApparentPower => L1.ApparentPower + L2.ApparentPower + L3.ApparentPower; + public ReactivePower ReactivePower => L1.ReactivePower + L2.ReactivePower + L3.ReactivePower; + public Power ActivePower => L1.ActivePower + L2.ActivePower + L3.ActivePower; + public Angle Phi => ATan2(ReactivePower, ActivePower); + + public static Ac3Bus operator |(Ac3Bus left, Ac3Bus right) => OpParallel(left, right); + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); +} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/Ac3Phase.cs b/csharp/Lib/Units/Composite/Ac3Phase.cs deleted file mode 100644 index a5788e452..000000000 --- a/csharp/Lib/Units/Composite/Ac3Phase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using static DecimalMath.DecimalEx; - -namespace InnovEnergy.Lib.Units.Composite; - -public record Ac3Phase(AcPhase L1, AcPhase L2, AcPhase L3, Frequency Frequency) -{ - public ApparentPower ApparentPower => L1.ApparentPower + L2.ApparentPower + L3.ApparentPower; - public ReactivePower ReactivePower => L1.ReactivePower + L2.ReactivePower + L3.ReactivePower; - public Power ActivePower => L1.ActivePower + L2.ActivePower + L3.ActivePower; - - public Angle Phi => ATan2(ReactivePower, ActivePower); - - - public static Ac3Phase operator +(Ac3Phase left, Ac3Phase right) - { - var f = (left.Frequency + right.Frequency) / 2m; // TODO: check that l & r approximately equal - - var l1 = left.L1 + right.L1; - var l2 = left.L2 + right.L2; - var l3 = left.L3 + right.L3; - - return new Ac3Phase(l1, l2, l3, f); - } - -} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/AcPhase.cs b/csharp/Lib/Units/Composite/AcPhase.cs index 70cd952b2..d590fbdee 100644 --- a/csharp/Lib/Units/Composite/AcPhase.cs +++ b/csharp/Lib/Units/Composite/AcPhase.cs @@ -3,52 +3,64 @@ using static DecimalMath.DecimalEx; namespace InnovEnergy.Lib.Units.Composite; -public record AcPhase : Phase +public record AcPhase : IBus { - public AcPhase(Voltage voltage, Current current, Angle phi) : base(voltage, current) + private readonly Voltage _Voltage; + public Voltage Voltage { - if (voltage < 0) throw new ArgumentException("RMS value cannot be negative", nameof(voltage)); - if (current < 0) throw new ArgumentException("RMS value cannot be negative", nameof(current)); - - Phi = phi; + get => _Voltage; + init => _Voltage = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative"); } - public Angle Phi { get; } + private readonly Current _Current; + public Current Current + { + get => _Current; + init => _Current = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative"); + } + + public Angle Phi { get; init; } public ApparentPower ApparentPower => Voltage.Value * Current.Value ; - public Power ActivePower => ApparentPower.Value * PowerFactor; + public Power ActivePower => ApparentPower.Value * PowerFactor.Value; public ReactivePower ReactivePower => ApparentPower.Value * Sin(Phi); - public Decimal PowerFactor => Cos(Phi); + public Number PowerFactor => Cos(Phi); - public static AcPhase operator +(AcPhase left, AcPhase right) + public static AcPhase operator |(AcPhase left, AcPhase right) { // the Voltages of two phases are expected to be in phase and equal - var v = (left.Voltage + right.Voltage) / 2m; // TODO: check that l & r approximately equal + var v = left.Voltage | right.Voltage; // currents (RMS) can be different and out of phase // https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/ // IF - // left(t) = ILeft sin(ωt) + // left(t) = ILeft sin(ωt) // right(t) = IRight sin(ωt + φ). - // sum(t) = left(t) + right(t) = ISum sin(ωt + ψ). - // THEN + // sum(t) = left(t) + right(t) = ISum sin(ωt + ψ). + // THEN // ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ). // C = IRight * sin(φ) / sin(ψ). - // in this calc left(t) has zero phase shift. + // in this calculation left(t) has zero phase shift. // we can shift both waves by -left.Phi, so // φ := right.phi - left.phi - var phi = right.Phi - left.Phi; - + var phi = right.Phi - left.Phi; var phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi)); var iSum = right.Current * Sin(phi) / Sin(phiSum); - return new AcPhase(v, iSum, phiSum); + return new AcPhase + { + Voltage = v, + Current = iSum, + Phi = phiSum + }; } + + } \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/DcBus.cs b/csharp/Lib/Units/Composite/DcBus.cs new file mode 100644 index 000000000..9bab0143a --- /dev/null +++ b/csharp/Lib/Units/Composite/DcBus.cs @@ -0,0 +1,14 @@ +using InnovEnergy.Lib.Utils; + +namespace InnovEnergy.Lib.Units.Composite; + +public record DcBus : IBus +{ + public Voltage Voltage { get; init; } + public Current Current { get; init; } + + public Power Power => Current * Voltage; + + public static DcBus operator |(DcBus left, DcBus right) => OpParallel(left, right); + private static readonly Func OpParallel = "|".CreateBinaryOpForProps(); +} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/DcPhase.cs b/csharp/Lib/Units/Composite/DcPhase.cs deleted file mode 100644 index af6d1049a..000000000 --- a/csharp/Lib/Units/Composite/DcPhase.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace InnovEnergy.Lib.Units.Composite; - -public record DcPhase(Voltage Voltage, Current Current) : Phase(Voltage, Current) -{ - public Power Power => Current * Voltage; - - public static DcPhase operator +(DcPhase left, DcPhase right) - { - var v = (left.Voltage + right.Voltage) / 2m; - var i = left.Current + right.Current; - - return new DcPhase(v, i); - } - -} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/IBus.cs b/csharp/Lib/Units/Composite/IBus.cs new file mode 100644 index 000000000..8b9694972 --- /dev/null +++ b/csharp/Lib/Units/Composite/IBus.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace InnovEnergy.Lib.Units.Composite; + +[SuppressMessage("ReSharper", "MemberCanBeProtected.Global")] + +public interface IBus +{ + public Voltage Voltage { get; } + public Current Current { get; } +} \ No newline at end of file diff --git a/csharp/Lib/Units/Composite/Phase.cs b/csharp/Lib/Units/Composite/Phase.cs deleted file mode 100644 index 45a3ef86d..000000000 --- a/csharp/Lib/Units/Composite/Phase.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace InnovEnergy.Lib.Units.Composite; - -public abstract record Phase -( - Voltage Voltage, - Current Current -); \ No newline at end of file diff --git a/csharp/Lib/Units/Current.generated.cs b/csharp/Lib/Units/Current.generated.cs index cda7eeadf..c06cb3ff0 100644 --- a/csharp/Lib/Units/Current.generated.cs +++ b/csharp/Lib/Units/Current.generated.cs @@ -2,15 +2,19 @@ #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 = Current; +[JsonConverter(typeof(CurrentConverter))] public readonly partial struct Current { public Decimal Value { get; } - public override String ToString() => Value + Unit; + public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit; // scalar multiplication @@ -18,25 +22,24 @@ public readonly partial struct Current 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); - // addition + // parallel #if Sum - public static T operator +(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T left, T right) => new T(left.Value - right.Value); + 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); + 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) + public static T operator |(T left, T right) { var d = Max(Abs(left.Value), Abs(right.Value)); - if (d != 0m) + if (d == 0m) return new T(0m); var relativeError = Abs(left.Value - right.Value) / d; @@ -76,3 +79,19 @@ public readonly partial struct Current public override Int32 GetHashCode() => Value.GetHashCode(); } + + +internal class CurrentConverter : JsonConverter +{ + public override Current Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new Current(reader.GetDecimal()); + } + + public override void Write(Utf8JsonWriter writer, Current value, JsonSerializerOptions options) + { + var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits); + + writer.WriteNumberValue(rounded); + } +} \ No newline at end of file diff --git a/csharp/Lib/Units/Energy.cs b/csharp/Lib/Units/Energy.cs new file mode 100644 index 000000000..006568b06 --- /dev/null +++ b/csharp/Lib/Units/Energy.cs @@ -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; +} \ No newline at end of file diff --git a/csharp/Lib/Units/Energy.generated.cs b/csharp/Lib/Units/Energy.generated.cs new file mode 100644 index 000000000..90496238b --- /dev/null +++ b/csharp/Lib/Units/Energy.generated.cs @@ -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 +{ + 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); + } +} \ No newline at end of file diff --git a/csharp/Lib/Units/Frequency.generated.cs b/csharp/Lib/Units/Frequency.generated.cs index 015d67536..576e7b1a4 100644 --- a/csharp/Lib/Units/Frequency.generated.cs +++ b/csharp/Lib/Units/Frequency.generated.cs @@ -2,15 +2,19 @@ #define Equal using static System.Math; +using System.Text.Json; +using System.Text.Json.Serialization; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Units; using T = Frequency; +[JsonConverter(typeof(FrequencyConverter))] public readonly partial struct Frequency { public Decimal Value { get; } - public override String ToString() => Value + Unit; + public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit; // scalar multiplication @@ -18,25 +22,24 @@ public readonly partial struct Frequency 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); - // addition + // parallel #if Sum - public static T operator +(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T left, T right) => new T(left.Value - right.Value); + 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); + 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) + public static T operator |(T left, T right) { var d = Max(Abs(left.Value), Abs(right.Value)); - if (d != 0m) + if (d == 0m) return new T(0m); var relativeError = Abs(left.Value - right.Value) / d; @@ -76,3 +79,19 @@ public readonly partial struct Frequency public override Int32 GetHashCode() => Value.GetHashCode(); } + + +internal class FrequencyConverter : JsonConverter +{ + public override Frequency Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new Frequency(reader.GetDecimal()); + } + + public override void Write(Utf8JsonWriter writer, Frequency value, JsonSerializerOptions options) + { + var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits); + + writer.WriteNumberValue(rounded); + } +} \ No newline at end of file diff --git a/csharp/Lib/Units/Generator/Template.txt b/csharp/Lib/Units/Generator/Template.txt index c656f51df..38e30aa04 100644 --- a/csharp/Lib/Units/Generator/Template.txt +++ b/csharp/Lib/Units/Generator/Template.txt @@ -1,16 +1,20 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Type +#define AggregationType using static System.Math; +using System.Text.Json; +using System.Text.Json.Serialization; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Units; using T = Template; +[JsonConverter(typeof(TemplateConverter))] public readonly partial struct Template { public Decimal Value { get; } - public override String ToString() => Value + Unit; + public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit; // scalar multiplication @@ -18,25 +22,24 @@ public readonly partial struct Template 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); - // addition + // parallel #if Sum - public static T operator +(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T left, T right) => new T(left.Value - right.Value); + 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); + 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) + public static T operator |(T left, T right) { var d = Max(Abs(left.Value), Abs(right.Value)); - if (d != 0m) + if (d == 0m) return new T(0m); var relativeError = Abs(left.Value - right.Value) / d; @@ -76,3 +79,19 @@ public readonly partial struct Template public override Int32 GetHashCode() => Value.GetHashCode(); } + + +internal class TemplateConverter : JsonConverter