Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
683ef02704
|
@ -1,13 +1,14 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Innovenergy.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using Innovenergy.Backend.Model;
|
using InnovEnergy.App.Backend.Model;
|
||||||
using Innovenergy.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Model.Relations;
|
||||||
using Innovenergy.Backend.Utils;
|
using InnovEnergy.App.Backend.Utils;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Controllers;
|
namespace InnovEnergy.App.Backend.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/")]
|
[Route("api/")]
|
||||||
|
@ -173,7 +174,7 @@ public class Controller
|
||||||
using var db = Db.Connect();
|
using var db = Db.Connect();
|
||||||
|
|
||||||
var folders = db
|
var folders = db
|
||||||
.GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure
|
.GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure
|
||||||
.Select(f => PopulateChildren(db, f));
|
.Select(f => PopulateChildren(db, f));
|
||||||
|
|
||||||
var installations = db.GetDirectlyAccessibleInstallations(caller);
|
var installations = db.GetDirectlyAccessibleInstallations(caller);
|
||||||
|
@ -183,6 +184,25 @@ public class Controller
|
||||||
.ToList(); // important!
|
.ToList(); // important!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Returns<TreeNode[]>] // 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<TreeNode>;
|
||||||
|
var installations = db.GetAllAccessibleInstallations(caller);
|
||||||
|
|
||||||
|
return folders
|
||||||
|
.Concat(installations)
|
||||||
|
.ToList(); // important!
|
||||||
|
}
|
||||||
|
|
||||||
private static Folder PopulateChildren(Db db, Folder folder, HashSet<Int64>? hs = null)
|
private static Folder PopulateChildren(Db db, Folder folder, HashSet<Int64>? hs = null)
|
||||||
{
|
{
|
||||||
// TODO: remove cycle detector
|
// TODO: remove cycle detector
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Controllers;
|
namespace InnovEnergy.App.Backend.Controllers;
|
||||||
|
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||||
public record Credentials(String Username, String Password);
|
public record Credentials(String Username, String Password);
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Controllers;
|
namespace InnovEnergy.App.Backend.Controllers;
|
||||||
|
|
||||||
public class ReturnsAttribute : ProducesResponseTypeAttribute
|
public class ReturnsAttribute : ProducesResponseTypeAttribute
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Innovenergy.Backend.Model;
|
using InnovEnergy.App.Backend.Model;
|
||||||
using Innovenergy.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Model.Relations;
|
||||||
using Innovenergy.Backend.Utils;
|
using InnovEnergy.App.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db : IDisposable
|
public partial class Db : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -89,21 +89,23 @@ public partial class Db : IDisposable
|
||||||
|
|
||||||
public IEnumerable<Installation> GetAllAccessibleInstallations(User user)
|
public IEnumerable<Installation> GetAllAccessibleInstallations(User user)
|
||||||
{
|
{
|
||||||
var direct = GetDirectlyAccessibleInstallations(user).ToList();
|
var direct = GetDirectlyAccessibleInstallations(user);
|
||||||
var fromFolders = GetAllAccessibleFolders(user)
|
var fromFolders = GetAllAccessibleFolders(user)
|
||||||
.SelectMany(GetChildInstallations)
|
.SelectMany(GetChildInstallations);
|
||||||
.Except(direct);
|
|
||||||
|
|
||||||
return direct.Concat(fromFolders);
|
return direct
|
||||||
|
.Concat(fromFolders)
|
||||||
|
.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
public IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
||||||
{
|
{
|
||||||
return GetDirectlyAccessibleFolders(user)
|
return GetDirectlyAccessibleFolders(user)
|
||||||
.SelectMany(GetDescendantFolders);
|
.SelectMany(GetDescendantFolders)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
// Distinct because the user might have direct access
|
||||||
|
// to a child folder of a folder he has already access to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,7 +115,8 @@ public partial class Db : IDisposable
|
||||||
.Where(r => r.UserId == user.Id)
|
.Where(r => r.UserId == user.Id)
|
||||||
.Select(r => r.InstallationId)
|
.Select(r => r.InstallationId)
|
||||||
.Select(GetInstallationById)
|
.Select(GetInstallationById)
|
||||||
.NotNull();
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Folder> GetDirectlyAccessibleFolders(User user)
|
public IEnumerable<Folder> GetDirectlyAccessibleFolders(User user)
|
||||||
|
@ -122,7 +125,8 @@ public partial class Db : IDisposable
|
||||||
.Where(r => r.UserId == user.Id)
|
.Where(r => r.UserId == user.Id)
|
||||||
.Select(r => r.FolderId)
|
.Select(r => r.FolderId)
|
||||||
.Select(GetFolderById)
|
.Select(GetFolderById)
|
||||||
.NotNull();
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId)
|
public Result AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId)
|
||||||
|
|
|
@ -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
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using Innovenergy.Backend.Model;
|
using InnovEnergy.App.Backend.Model;
|
||||||
using Innovenergy.Backend.Utils;
|
using InnovEnergy.App.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using Innovenergy.Backend.Model;
|
using InnovEnergy.App.Backend.Model;
|
||||||
using Innovenergy.Backend.Utils;
|
using InnovEnergy.App.Backend.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,12 +3,11 @@ using System.Net.Http.Headers;
|
||||||
using System.Net.Mail;
|
using System.Net.Mail;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Flurl.Http;
|
using Flurl.Http;
|
||||||
using Innovenergy.Backend.Model;
|
using InnovEnergy.App.Backend.Model;
|
||||||
using Innovenergy.Backend.Utils;
|
using InnovEnergy.App.Backend.Utils;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
using ResponseExtensions = Flurl.Http.ResponseExtensions;
|
using ResponseExtensions = Flurl.Http.ResponseExtensions;
|
||||||
|
@ -16,7 +15,7 @@ using ResponseExtensions = Flurl.Http.ResponseExtensions;
|
||||||
#pragma warning disable CS0472
|
#pragma warning disable CS0472
|
||||||
#pragma warning disable CS8602
|
#pragma warning disable CS8602
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Innovenergy.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Model.Relations;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Innovenergy.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Model.Relations;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public partial class Db
|
public partial class Db
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
namespace Innovenergy.Backend;
|
namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is for convenient testing! Todo throw me out?
|
/// This is for convenient testing! Todo throw me out?
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Innovenergy.Backend.Model;
|
namespace InnovEnergy.App.Backend.Model;
|
||||||
|
|
||||||
public class Folder : TreeNode
|
public class Folder : TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Innovenergy.Backend.Model;
|
namespace InnovEnergy.App.Backend.Model;
|
||||||
|
|
||||||
|
|
||||||
public class Installation : TreeNode
|
public class Installation : TreeNode
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Model.Relations;
|
||||||
|
|
||||||
public abstract class Relation<L,R>
|
public abstract class Relation<L,R>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Model.Relations;
|
||||||
|
|
||||||
public class Session : Relation<String, Int64>
|
public class Session : Relation<String, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Model.Relations;
|
||||||
|
|
||||||
internal class User2Folder : Relation<Int64, Int64>
|
internal class User2Folder : Relation<Int64, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Model.Relations;
|
||||||
|
|
||||||
internal class User2Installation : Relation<Int64, Int64>
|
internal class User2Installation : Relation<Int64, Int64>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model;
|
namespace InnovEnergy.App.Backend.Model;
|
||||||
|
|
||||||
public abstract partial class TreeNode
|
public abstract partial class TreeNode
|
||||||
{
|
{
|
||||||
|
// Note: Only consider Id, but not ParentId for TreeNode equality checks
|
||||||
protected Boolean Equals(TreeNode other)
|
protected Boolean Equals(TreeNode other)
|
||||||
{
|
{
|
||||||
return Id == other.Id && ParentId == other.ParentId;
|
return Id == other.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Boolean Equals(Object? obj)
|
public override Boolean Equals(Object? obj)
|
||||||
|
@ -17,8 +18,5 @@ public abstract partial class TreeNode
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
||||||
public override Int32 GetHashCode()
|
public override Int32 GetHashCode() => Id.GetHashCode();
|
||||||
{
|
|
||||||
return HashCode.Combine(Id, ParentId);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model;
|
namespace InnovEnergy.App.Backend.Model;
|
||||||
|
|
||||||
public abstract partial class TreeNode
|
public abstract partial class TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Model;
|
namespace InnovEnergy.App.Backend.Model;
|
||||||
|
|
||||||
public class User : TreeNode
|
public class User : TreeNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Innovenergy.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
namespace Innovenergy.Backend;
|
namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Innovenergy.Backend.Utils;
|
namespace InnovEnergy.App.Backend.Utils;
|
||||||
|
|
||||||
public static class Crypto
|
public static class Crypto
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Innovenergy.Backend.Utils;
|
namespace InnovEnergy.App.Backend.Utils;
|
||||||
|
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,32 +20,32 @@ public static class Config
|
||||||
|
|
||||||
public static readonly IReadOnlyList<Signal> Signals = new Signal[]
|
public static readonly IReadOnlyList<Signal> Signals = new Signal[]
|
||||||
{
|
{
|
||||||
new(s => s.CurrentL1, "/Ac/L1/Current", "0.0 A"),
|
new(s => s.Ac.L1.Current, "/Ac/L1/Current", "0.0 A"),
|
||||||
new(s => s.CurrentL2, "/Ac/L2/Current", "0.0 A"),
|
new(s => s.Ac.L2.Current, "/Ac/L2/Current", "0.0 A"),
|
||||||
new(s => s.CurrentL3, "/Ac/L3/Current", "0.0 A"),
|
new(s => s.Ac.L3.Current, "/Ac/L3/Current", "0.0 A"),
|
||||||
new(s => s.CurrentL123, "/Ac/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.Ac.L1.Voltage, "/Ac/L1/Voltage", "0.0 A"),
|
||||||
new(s => s.VoltageL2N, "/Ac/L2/Voltage", "0.0 A"),
|
new(s => s.Ac.L2.Voltage, "/Ac/L2/Voltage", "0.0 A"),
|
||||||
new(s => s.VoltageL3N, "/Ac/L3/Voltage", "0.0 A"),
|
new(s => s.Ac.L3.Voltage, "/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 + 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.Ac.L1.ActivePower, "/Ac/L1/Power", "0 W"),
|
||||||
new(s => s.ActivePowerL2, "/Ac/L2/Power", "0 W"),
|
new(s => s.Ac.L2.ActivePower, "/Ac/L2/Power", "0 W"),
|
||||||
new(s => s.ActivePowerL3, "/Ac/L3/Power", "0 W"),
|
new(s => s.Ac.L3.ActivePower, "/Ac/L3/Power", "0 W"),
|
||||||
new(s => s.ActivePowerL123, "/Ac/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.EnergyImportL123, "Ac/Energy/Forward", "0.00 kWh"),
|
||||||
new(s => s.EnergyExportL123, "Ac/Energy/Reverse", "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.EnergyImportL1, "Ac/L1/Energy/Forward", "0.00 kWh"),
|
||||||
new(s => s.EnergyExportL1, "Ac/L1/Energy/Reverse", "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.EnergyImportL2, "Ac/L2/Energy/Forward", "0.00 kWh"),
|
||||||
new(s => s.EnergyExportL2, "Ac/L2/Energy/Reverse", "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.EnergyImportL3, "Ac/L3/Energy/Forward", "0.00 kWh"),
|
||||||
new(s => s.EnergyExportL3, "Ac/L3/Energy/Reverse", "0.00 kWh"),
|
// new(s => s.EnergyExportL3, "Ac/L3/Energy/Reverse", "0.00 kWh"),
|
||||||
};
|
};
|
||||||
|
|
||||||
public static VeProperties DefaultProperties => new VeProperties
|
public static VeProperties DefaultProperties => new VeProperties
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>InnovEnergy.App.$(AssemblyName)</RootNamespace>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
<ProjectReference Include="../../Lib/Victron/VictronVRM/VictronVRM.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="RemoteSupportConsole.csproj.DotSettings" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliWrap" Version="3.6.0" />
|
<PackageReference Include="CliWrap" Version="3.6.0" />
|
||||||
|
|
|
@ -6,6 +6,6 @@ public static class Utils
|
||||||
{
|
{
|
||||||
public static Decimal Round3(this Decimal d)
|
public static Decimal Round3(this Decimal d)
|
||||||
{
|
{
|
||||||
return DecimalUtils.RoundToSignificantFigures(d, 3);
|
return DecimalUtils.RoundToSignificantDigits(d, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<InvariantGlobalization>true</InvariantGlobalization>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
|
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
|
||||||
<RootNamespace>$(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("src/", "").Replace("/","."))</RootNamespace>
|
<RootNamespace>$(Company).$(MSBuildProjectDirectory.Replace($(SolutionDir), "").Replace("src/", "").Replace("/",".").Replace("\","."))</RootNamespace>
|
||||||
<Authors>$(Company) Team</Authors>
|
<Authors>$(Company) Team</Authors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
<s:Boolean x:Key="/Default/CodeEditing/SuppressNullableWarningFix/Enabled/@EntryValue">False</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeEditing/SuppressNullableWarningFix/Enabled/@EntryValue">False</s:Boolean>
|
||||||
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=AMPT/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=AMPT/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=arctan/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=backfill/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=backfill/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=beaglebone/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beaglebone/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=CCGX/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=CCGX/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.AMPT;
|
namespace InnovEnergy.Lib.Devices.AMPT;
|
||||||
|
|
||||||
|
@ -11,5 +11,5 @@ public record AmptDeviceStatus
|
||||||
UInt32 Timestamp, // The UTC timestamp of the measurements
|
UInt32 Timestamp, // The UTC timestamp of the measurements
|
||||||
Decimal ProductionToday, // converted to kW in AmptCU class
|
Decimal ProductionToday, // converted to kW in AmptCU class
|
||||||
IReadOnlyList<DcConnection> Strings
|
IReadOnlyList<DcConnection> Strings
|
||||||
): Mppt(Dc, Strings)
|
): MpptStatus(Dc, Strings)
|
||||||
{}
|
{}
|
|
@ -1,7 +1,5 @@
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
|
@ -33,18 +31,11 @@ public class Battery48TlDevice
|
||||||
|
|
||||||
public Battery48TLStatus? ReadStatus() //Already try catch is implemented
|
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
|
try
|
||||||
{
|
{
|
||||||
var registers = Modbus.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters);
|
return Modbus
|
||||||
return TryReadStatus(registers);
|
.ReadInputRegisters(Constants.BaseAddress, Constants.NoOfRegisters)
|
||||||
|
.ParseBatteryStatus();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -53,92 +44,4 @@ public class Battery48TlDevice
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Battery48TLStatus? TryReadStatus(ModbusRegisters data)
|
|
||||||
{
|
|
||||||
var soc = data.ParseDecimal(register: 1054, scaleFactor: 0.1m);
|
|
||||||
var eocReached = data.ParseEocReached();
|
|
||||||
|
|
||||||
var warnings = new List<String>();
|
|
||||||
|
|
||||||
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<String>();
|
|
||||||
|
|
||||||
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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,39 +1,50 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
namespace InnovEnergy.Lib.Devices.Battery48TL;
|
||||||
|
|
||||||
|
using T = Battery48TLStatus;
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public record Battery48TLStatus
|
public record Battery48TLStatus : BatteryStatus
|
||||||
(
|
|
||||||
DcConnection Dc,
|
|
||||||
Decimal Soc,
|
|
||||||
Decimal Temperature,
|
|
||||||
//Decimal Current,
|
|
||||||
//Decimal Voltage,
|
|
||||||
Decimal BusVoltage,
|
|
||||||
LedState GreenLed,
|
|
||||||
LedState AmberLed,
|
|
||||||
LedState BlueLed,
|
|
||||||
LedState RedLed,
|
|
||||||
IReadOnlyList<String> Warnings,
|
|
||||||
IReadOnlyList<String> 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 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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<Int32>(); // 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<Int16>() * 2).ConvertTo<Int16>());
|
|
||||||
var hi = ParseBool(data, register, (led.ConvertTo<Int16>() * 2 + 1).ConvertTo<Int16>());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Int32>(); // 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<Int16>() * 2 ).ConvertTo<Int16>());
|
||||||
|
var hi = data.ParseBool(register, (led.ConvertTo<Int16>() * 2 + 1).ConvertTo<Int16>());
|
||||||
|
|
||||||
|
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<String> 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<String> 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:";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
using InnovEnergy.Lib.StatusApi.Phases;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
using static DecimalMath.DecimalEx;
|
using static DecimalMath.DecimalEx;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
|
@ -29,91 +27,71 @@ public class EmuMeterDevice
|
||||||
return null;
|
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()
|
private EmuMeterStatus TryReadStatus()
|
||||||
{
|
{
|
||||||
// Console.WriteLine("Reading Emu Meter Data");
|
// Console.WriteLine("Reading Emu Meter Data");
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: get SerialNb, depends on Little/Big Endian support in Modbus Lib
|
||||||
|
// var registers = Modbus.ReadHoldingRegisters(5001, 4);
|
||||||
|
// var id = registers.GetInt32(5001);
|
||||||
|
|
||||||
var powerCurrent = Modbus.ReadHoldingRegisters(9000, 108).ToDecimals(); // TODO "ModbusRegisters"
|
var powerCurrent = Modbus.ReadHoldingRegisters(9000, 108).ToDecimals(); // TODO "ModbusRegisters"
|
||||||
var voltageFreq = Modbus.ReadHoldingRegisters(9200, 112).ToDecimals(); // To check with Ivo
|
var voltageFreq = Modbus.ReadHoldingRegisters(9200, 112).ToDecimals(); // To check with Ivo
|
||||||
var energyTotal = Modbus.ReadHoldingRegisters(6000, 24) .ToUInt64s();
|
|
||||||
var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s();
|
// var energyPhases = Modbus.ReadHoldingRegisters(6100, 104).ToUInt64s();
|
||||||
|
|
||||||
var activePowerL123 = powerCurrent[0];
|
|
||||||
var activePowerL1 = powerCurrent[1];
|
var activePowerL1 = powerCurrent[1];
|
||||||
var activePowerL2 = powerCurrent[2];
|
var activePowerL2 = powerCurrent[2];
|
||||||
var activePowerL3 = powerCurrent[3];
|
var activePowerL3 = powerCurrent[3];
|
||||||
var reactivePowerL123 = powerCurrent[5];
|
|
||||||
var reactivePowerL1 = powerCurrent[6];
|
var reactivePowerL1 = powerCurrent[6];
|
||||||
var reactivePowerL2 = powerCurrent[7];
|
var reactivePowerL2 = powerCurrent[7];
|
||||||
var reactivePowerL3 = powerCurrent[8];
|
var reactivePowerL3 = powerCurrent[8];
|
||||||
var apparentPowerL123 = powerCurrent[10];
|
|
||||||
var apparentPowerL1 = powerCurrent[11];
|
|
||||||
var apparentPowerL2 = powerCurrent[12];
|
|
||||||
var apparentPowerL3 = powerCurrent[13];
|
|
||||||
var currentL123 = powerCurrent[50];
|
|
||||||
var currentL1 = powerCurrent[51];
|
var currentL1 = powerCurrent[51];
|
||||||
var currentL2 = powerCurrent[52];
|
var currentL2 = powerCurrent[52];
|
||||||
var currentL3 = powerCurrent[53];
|
var currentL3 = powerCurrent[53];
|
||||||
|
|
||||||
var voltageL1N = voltageFreq[0];
|
var voltageL1N = voltageFreq[0];
|
||||||
var voltageL2N = voltageFreq[1];
|
var voltageL2N = voltageFreq[1];
|
||||||
var voltageL3N = voltageFreq[2];
|
var voltageL3N = voltageFreq[2];
|
||||||
var voltageL1L2 = voltageFreq[3];
|
|
||||||
var voltageL2L3 = voltageFreq[4];
|
|
||||||
var voltageL3L1 = voltageFreq[5];
|
|
||||||
var powerFactorL1 = voltageFreq[50];
|
|
||||||
var powerFactorL2 = voltageFreq[51];
|
|
||||||
var powerFactorL3 = voltageFreq[52];
|
|
||||||
var frequency = voltageFreq[55];
|
var frequency = voltageFreq[55];
|
||||||
var energyImportL123 = energyTotal[0 / 4] / 1000.0m;
|
|
||||||
var energyExportL123 = energyTotal[20 / 4] / 1000.0m;
|
|
||||||
var energyImportL1 = energyPhases[0 / 4] / 1000.0m;
|
var l1 = new AcPhase
|
||||||
var energyExportL1 = energyPhases[20 / 4] / 1000.0m;
|
{
|
||||||
var energyImportL2 = energyPhases[40 / 4] / 1000.0m;
|
Current = currentL1,
|
||||||
var energyExportL2 = energyPhases[60 / 4] / 1000.0m;
|
Voltage = voltageL1N,
|
||||||
var energyImportL3 = energyPhases[80 / 4] / 1000.0m;
|
Phi = ATan2(reactivePowerL1, activePowerL1) // TODO: check that this works
|
||||||
var energyExportL3 = energyPhases[100 / 4] / 1000.0m;
|
};
|
||||||
|
var l2 = new AcPhase
|
||||||
|
{
|
||||||
|
Current = currentL2,
|
||||||
|
Voltage = voltageL2N,
|
||||||
|
Phi = ATan2(reactivePowerL2, activePowerL2)
|
||||||
|
};
|
||||||
|
var l3 = new AcPhase
|
||||||
|
{
|
||||||
|
Current = currentL3,
|
||||||
|
Voltage = voltageL3N,
|
||||||
|
Phi = ATan2(reactivePowerL3, activePowerL3)
|
||||||
|
};
|
||||||
|
|
||||||
return new EmuMeterStatus
|
return new EmuMeterStatus
|
||||||
(
|
{
|
||||||
Ac: new ThreePhaseAcConnection
|
Ac = new Ac3Bus
|
||||||
(
|
{
|
||||||
new AcPhase(
|
Frequency = frequency,
|
||||||
voltageL1N,
|
L1 = l1,
|
||||||
currentL1,
|
L2 = l2,
|
||||||
GetPhi(powerFactorL1)
|
L3 = l3
|
||||||
),
|
}
|
||||||
|
};
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,26 +1,10 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
namespace InnovEnergy.Lib.Devices.EmuMeter;
|
||||||
|
|
||||||
public record EmuMeterStatus
|
public record EmuMeterStatus : PowerMeterStatus
|
||||||
(
|
{
|
||||||
ThreePhaseAcConnection Ac,
|
// TODO: additional Measurements, device id
|
||||||
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)
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -2,8 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
using InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
using InnovEnergy.Lib.StatusApi.Phases;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using static DecimalMath.DecimalEx;
|
using static DecimalMath.DecimalEx;
|
||||||
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters;
|
using static InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.AcControlRegisters;
|
||||||
|
@ -216,7 +215,7 @@ public class TruConvertAcDevice
|
||||||
|
|
||||||
return new TruConvertAcStatus
|
return new TruConvertAcStatus
|
||||||
(
|
(
|
||||||
Ac: new ThreePhaseAcConnection
|
Ac: new Ac3Bus
|
||||||
(
|
(
|
||||||
new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
|
new AcPhase(gridVoltageL1,phaseCurrentL1, ACos(powerAcL1/apparentPowerAcL1)),
|
||||||
new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
|
new AcPhase(gridVoltageL2,phaseCurrentL2, ACos(powerAcL2/apparentPowerAcL2)),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
|
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ using WarningMessages = IReadOnlyList<WarningMessage>;
|
||||||
|
|
||||||
public record TruConvertAcStatus
|
public record TruConvertAcStatus
|
||||||
(
|
(
|
||||||
ThreePhaseAcConnection Ac,
|
Ac3Bus Ac,
|
||||||
DcConnection Dc,
|
DcConnection Dc,
|
||||||
String SerialNumber,
|
String SerialNumber,
|
||||||
MainState MainState,
|
MainState MainState,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using InnovEnergy.Lib.Devices.Trumpf.TruConvert;
|
using InnovEnergy.Lib.StatusApi;
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.StatusApi.Devices;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertDc;
|
||||||
|
|
||||||
|
@ -8,20 +9,24 @@ using AlarmMessages = IReadOnlyList<AlarmMessage>;
|
||||||
using WarningMessages = IReadOnlyList<WarningMessage>;
|
using WarningMessages = IReadOnlyList<WarningMessage>;
|
||||||
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
|
using DcCurrentLimitStates = IReadOnlyList<DcCurrentLimitState>;
|
||||||
|
|
||||||
public record TruConvertDcStatus
|
public record TruConvertDcStatus
|
||||||
(
|
(
|
||||||
DcConnection Dc,
|
DcBus DcLeft,
|
||||||
MainState MainState,
|
DcBus DcRight,
|
||||||
UInt16 NumberOfConnectedSlaves,
|
State MainState,
|
||||||
UInt16 NumberOfConnectedSubSlaves,
|
Power TotalDcPower, // TODO: necessary?
|
||||||
Decimal BatteryVoltage,
|
State StatusOfCurrentLimiting,
|
||||||
Decimal BatteryCurrent,
|
Decimal OverloadCapacity,
|
||||||
Decimal TotalDcPower,
|
Temperature DcDcInletTemperature,
|
||||||
DcCurrentLimitStates StatusOfCurrentLimiting,
|
State Alarms,
|
||||||
Decimal OverloadCapacity,
|
State Warnings,
|
||||||
Decimal DcDcInletTemperature,
|
State PowerOperation
|
||||||
AlarmMessages Alarms,
|
|
||||||
WarningMessages Warnings,
|
// UInt16 NumberOfConnectedSlaves, // TODO: necessary?
|
||||||
Boolean PowerOperation
|
// UInt16 NumberOfConnectedSubSlaves, // TODO: necessary?
|
||||||
):DcDevice(Dc)
|
) :
|
||||||
{}
|
DcDcConverterStatus(DcLeft, DcRight)
|
||||||
|
{
|
||||||
|
public static TruConvertDcStatus operator |(TruConvertDcStatus left, TruConvertDcStatus right) => OpParallel(left, right);
|
||||||
|
private static readonly Func<TruConvertDcStatus, TruConvertDcStatus, TruConvertDcStatus> OpParallel = Operators.Op<TruConvertDcStatus>("|");
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum Endianness
|
||||||
|
{
|
||||||
|
LittleEndian32BitIntegers = 0,
|
||||||
|
LittleEndian32Floats = 0,
|
||||||
|
BigEndian32BitIntegers = 1,
|
||||||
|
BigEndian32Floats = 2,
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Threading.Channels;
|
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
using InnovEnergy.Lib.Protocols.Modbus.Connections;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
using InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
|
using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
|
@ -13,8 +13,9 @@ public abstract class ModbusClient
|
||||||
{
|
{
|
||||||
protected ModbusConnection Connection { get; }
|
protected ModbusConnection Connection { get; }
|
||||||
protected Byte SlaveId { get; }
|
protected Byte SlaveId { get; }
|
||||||
|
protected Endianness Endianness { get; }
|
||||||
|
|
||||||
|
|
||||||
// TODO: add additional functions: coils...
|
// TODO: add additional functions: coils...
|
||||||
|
|
||||||
public abstract Coils ReadDiscreteInputs (UInt16 readAddress, UInt16 nValues);
|
public abstract Coils ReadDiscreteInputs (UInt16 readAddress, UInt16 nValues);
|
||||||
|
@ -40,11 +41,11 @@ public abstract class ModbusClient
|
||||||
return WriteRegisters(writeAddress, (IReadOnlyList<UInt16>)values);
|
return WriteRegisters(writeAddress, (IReadOnlyList<UInt16>)values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ModbusClient(ModbusConnection connection, Byte slaveId, Endianness endianness = LittleEndian32BitIntegers | LittleEndian32Floats)
|
||||||
protected ModbusClient(ModbusConnection connection, Byte slaveId)
|
|
||||||
{
|
{
|
||||||
Connection = connection;
|
Connection = connection;
|
||||||
SlaveId = slaveId;
|
SlaveId = slaveId;
|
||||||
|
Endianness = endianness;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseConnection() => Connection.Close();
|
public void CloseConnection() => Connection.Close();
|
||||||
|
|
|
@ -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.Commands;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies;
|
using InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Replies;
|
||||||
using InnovEnergy.Lib.Protocols.Modbus.Tcp;
|
using InnovEnergy.Lib.Protocols.Modbus.Tcp;
|
||||||
|
using static InnovEnergy.Lib.Protocols.Modbus.Clients.Endianness;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Clients;
|
||||||
|
|
||||||
using UInt16s = IReadOnlyList<UInt16>;
|
using UInt16s = IReadOnlyList<UInt16>;
|
||||||
using Coils = IReadOnlyList<Boolean>;
|
using Coils = IReadOnlyList<Boolean>;
|
||||||
|
|
||||||
|
|
||||||
public class ModbusTcpClient : ModbusClient
|
public class ModbusTcpClient : ModbusClient
|
||||||
{
|
{
|
||||||
public const UInt16 DefaultPort = 502;
|
public const UInt16 DefaultPort = 502;
|
||||||
|
@ -20,7 +20,7 @@ public class ModbusTcpClient : ModbusClient
|
||||||
private UInt16 NextId() => unchecked(++_Id);
|
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Conversions;
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ public partial class ModbusRegisters
|
||||||
{
|
{
|
||||||
var offset = index - StartRegister;
|
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);
|
var bitArray = new BitArray(byteArray);
|
||||||
|
|
||||||
return bitArray.Get(bitIndex);
|
return bitArray.Get(bitIndex);
|
||||||
|
@ -18,7 +19,7 @@ public partial class ModbusRegisters
|
||||||
{
|
{
|
||||||
var offset = index - StartRegister;
|
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);
|
var bitArray = new BitArray(byteArray);
|
||||||
|
|
||||||
bitArray.Set(bitIndex, value);
|
bitArray.Set(bitIndex, value);
|
||||||
|
|
|
@ -9,7 +9,7 @@ public partial class ModbusRegisters
|
||||||
var bytearray = BitConverter.GetBytes(value).Reverse().ToArray();
|
var bytearray = BitConverter.GetBytes(value).Reverse().ToArray();
|
||||||
var value32 = BitConverter.ToUInt32(bytearray);
|
var value32 = BitConverter.ToUInt32(bytearray);
|
||||||
|
|
||||||
Registers[index - StartRegister] = (UInt16)(value32 >> 16);
|
Registers[index - StartRegister ] = (UInt16)(value32 >> 16);
|
||||||
Registers[index - StartRegister + 1] = (UInt16)(value32 & 0xFFFF);
|
Registers[index - StartRegister + 1] = (UInt16)(value32 & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ public static class Accessors
|
||||||
public static MbWord WordAt (this ArraySegment<Byte> data, Byte i) => new MbWord(data, i);
|
public static MbWord WordAt (this ArraySegment<Byte> data, Byte i) => new MbWord(data, i);
|
||||||
public static MbAddress AddressAt(this ArraySegment<Byte> data, Byte i) => new MbAddress(data, i);
|
public static MbAddress AddressAt(this ArraySegment<Byte> data, Byte i) => new MbAddress(data, i);
|
||||||
|
|
||||||
public static MbWords WordsAt(this ArraySegment<Byte> data, Byte i) => new MbWords(data, i);
|
public static MbRegisters RegistersAt(this ArraySegment<Byte> data, Byte i) => new MbRegisters(data, i);
|
||||||
public static MbBits BitsAt (this ArraySegment<Byte> data, Byte i) => new MbBits(data, i);
|
public static MbBits BitsAt (this ArraySegment<Byte> data, Byte i) => new MbBits(data, i);
|
||||||
|
|
||||||
|
|
||||||
public static MbByte<T> ByteAt<T>(this ArraySegment<Byte> data,Byte i) where T : struct, IConvertible => new MbByte<T>(data, i);
|
public static MbByte<T> ByteAt<T>(this ArraySegment<Byte> data,Byte i) where T : struct, IConvertible => new MbByte<T>(data, i);
|
||||||
|
|
|
@ -3,30 +3,30 @@ using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
|
namespace InnovEnergy.Lib.Protocols.Modbus.Protocol.Frames.Accessors;
|
||||||
|
|
||||||
public readonly struct MbWords : IReadOnlyList<UInt16>
|
public readonly struct MbRegisters : IReadOnlyList<UInt16>
|
||||||
{
|
{
|
||||||
private readonly ArraySegment<Byte> _Data;
|
private readonly ArraySegment<Byte> _Data;
|
||||||
|
|
||||||
internal MbWords(ArraySegment<Byte> data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex))
|
internal MbRegisters(ArraySegment<Byte> data, Byte startIndex) : this(data, startIndex, CountWords(data, startIndex))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal MbRegisters(ArraySegment<Byte> data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal MbRegisters(Byte[] data, Byte startIndex, UInt16 wordCount)
|
||||||
|
{
|
||||||
|
_Data = new ArraySegment<Byte>(data, startIndex, wordCount * 2);
|
||||||
|
}
|
||||||
|
|
||||||
private static UInt16 CountWords(ArraySegment<Byte> data, Byte startIndex)
|
private static UInt16 CountWords(ArraySegment<Byte> data, Byte startIndex)
|
||||||
{
|
{
|
||||||
var wordCount = (data.Count - startIndex) / 2;
|
var wordCount = (data.Count - startIndex) / 2;
|
||||||
return wordCount.ConvertTo<UInt16>();
|
return wordCount.ConvertTo<UInt16>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal MbWords(ArraySegment<Byte> data, Byte startIndex, UInt16 wordCount) : this(data.Array!, startIndex, wordCount)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal MbWords(Byte[] data, Byte startIndex, UInt16 wordCount)
|
|
||||||
{
|
|
||||||
_Data = new ArraySegment<Byte>(data, startIndex, wordCount * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IReadOnlyCollection<UInt16> Set(IReadOnlyCollection<UInt16> values)
|
internal IReadOnlyCollection<UInt16> Set(IReadOnlyCollection<UInt16> values)
|
||||||
{
|
{
|
||||||
if (values.Count != _Data.Count / 2)
|
if (values.Count != _Data.Count / 2)
|
|
@ -16,7 +16,7 @@ internal class ReadWriteRegistersCommandFrame : ModbusFrame
|
||||||
public MbAddress WriteAddress => Data.AddressAt(6);
|
public MbAddress WriteAddress => Data.AddressAt(6);
|
||||||
public MbWord NbToWrite => Data.WordAt(8);
|
public MbWord NbToWrite => Data.WordAt(8);
|
||||||
public MbByte ByteCount => Data.ByteAt(10);
|
public MbByte ByteCount => Data.ByteAt(10);
|
||||||
public MbWords RegistersToWrite => Data.WordsAt(11);
|
public MbRegisters RegistersToWrite => Data.RegistersAt(11);
|
||||||
|
|
||||||
public Int32 ExpectedResponseSize => ReadWriteRegistersResponseFrame.ExpectedSize(NbToRead);
|
public Int32 ExpectedResponseSize => ReadWriteRegistersResponseFrame.ExpectedSize(NbToRead);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ internal class WriteRegistersCommandFrame : ModbusFrame
|
||||||
public MbAddress WriteAddress => Data.AddressAt(2);
|
public MbAddress WriteAddress => Data.AddressAt(2);
|
||||||
public MbWord NbOfRegisters => Data.WordAt(4);
|
public MbWord NbOfRegisters => Data.WordAt(4);
|
||||||
public MbByte ByteCount => Data.ByteAt(6);
|
public MbByte ByteCount => Data.ByteAt(6);
|
||||||
public MbWords RegistersToWrite => Data.WordsAt(7);
|
public MbRegisters RegistersToWrite => Data.RegistersAt(7);
|
||||||
|
|
||||||
public Int32 ExpectedResponseSize => WriteRegistersResponseFrame.ExpectedSize();
|
public Int32 ExpectedResponseSize => WriteRegistersResponseFrame.ExpectedSize();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ internal class ReadHoldingRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
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))
|
public ReadHoldingRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
|
|
|
@ -12,7 +12,7 @@ internal class ReadInputRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
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))
|
public ReadInputRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ internal class ReadWriteRegistersResponseFrame : ModbusFrame
|
||||||
internal new const Int32 MinSize = 3;
|
internal new const Int32 MinSize = 3;
|
||||||
|
|
||||||
public MbByte ByteCount => Data.ByteAt(2);
|
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))
|
public ReadWriteRegistersResponseFrame(Byte slave, UInt16s registersRead) : base(ExpectedSize(registersRead.Count))
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
|
using InnovEnergy.Lib.Units;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
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; }
|
||||||
|
}
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IAc1Connection
|
public interface IAc1Connection
|
||||||
{
|
{
|
||||||
Ac1Phase Ac { get; }
|
Ac1Bus Ac { get; }
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IAc3Connection
|
public interface IAc3Connection
|
||||||
{
|
{
|
||||||
Ac1Phase Ac3 { get; }
|
Ac3Bus Ac { get; }
|
||||||
}
|
}
|
|
@ -5,5 +5,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IDcConnection
|
public interface IDcConnection
|
||||||
{
|
{
|
||||||
DcPhase Dc { get; }
|
DcBus Dc { get; }
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ namespace InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
|
||||||
public interface IPvConnection
|
public interface IPvConnection
|
||||||
{
|
{
|
||||||
IReadOnlyList<DcPhase> Strings { get; }
|
IReadOnlyList<DcBus> Strings { get; }
|
||||||
}
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -5,8 +5,48 @@ namespace InnovEnergy.Lib.StatusApi;
|
||||||
public abstract record DeviceStatus
|
public abstract record DeviceStatus
|
||||||
{
|
{
|
||||||
public String DeviceType => GetType()
|
public String DeviceType => GetType()
|
||||||
.Generate(t => t.BaseType!)
|
.Unfold(t => t.BaseType)
|
||||||
.First(t => t.IsAbstract)
|
.First(t => t.IsAbstract)
|
||||||
.Name
|
.Name
|
||||||
.Replace("Status", "");
|
.Replace("Status", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,5 @@
|
||||||
|
namespace InnovEnergy.Lib.StatusApi.Generator;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
internal class OpParallelAttribute : Attribute
|
||||||
|
{}
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -1,8 +1,14 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public record MpptStatus(DcPhase Dc, IReadOnlyList<DcPhase> Strings) : IDcConnection, IPvConnection;
|
[OpParallel]
|
||||||
|
public partial record MpptStatus : IDcConnection, IPvConnection
|
||||||
|
{
|
||||||
|
public DcBus Dc { get; init; }
|
||||||
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record PowerMeterStatus(Ac1Phase Ac3) : DeviceStatus, IAc3Connection;
|
[OpParallel]
|
||||||
|
public partial record PowerMeterStatus : DeviceStatus, IAc3Connection
|
||||||
|
{
|
||||||
|
public Ac3Bus Ac { get; init; }
|
||||||
|
}
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record SinglePhaseInverterStatus(Ac1Phase Ac, DcPhase Dc) :
|
[OpParallel]
|
||||||
|
public partial record SinglePhaseInverterStatus :
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
IAc1Connection,
|
IAc1Connection,
|
||||||
IDcConnection;
|
IDcConnection
|
||||||
|
{
|
||||||
|
public Ac1Bus Ac { get; init; }
|
||||||
|
public DcBus Dc { get; init; }
|
||||||
|
}
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record SinglePhasePvInverterStatus(Ac1Phase Ac, IReadOnlyList<DcPhase> Strings) :
|
[OpParallel]
|
||||||
|
public partial record SinglePhasePvInverterStatus :
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
IAc1Connection,
|
IAc1Connection,
|
||||||
IPvConnection;
|
IPvConnection
|
||||||
|
{
|
||||||
|
public Ac1Bus Ac { get; init; }
|
||||||
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
|
}
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="../InnovEnergy.Lib.props" />
|
<Import Project="../InnovEnergy.Lib.props" />
|
||||||
|
<!-- <Import Project="../../App/InnovEnergy.App.props" />-->
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Protocols/Modbus/Modbus.csproj" />
|
<ProjectReference Include="../Protocols/Modbus/Modbus.csproj" />
|
||||||
<ProjectReference Include="..\Units\Units.csproj" />
|
<ProjectReference Include="../Units/Units.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
|
<Exec Command="./Generator/generate.sh" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record ThreePhaseInverterStatus(Ac1Phase Ac3, DcPhase Dc) :
|
[OpParallel]
|
||||||
|
public partial record ThreePhaseInverterStatus :
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
IAc3Connection,
|
IAc3Connection,
|
||||||
IDcConnection;
|
IDcConnection
|
||||||
|
{
|
||||||
|
public Ac3Bus Ac { get; init; }
|
||||||
|
public DcBus Dc { get; init; }
|
||||||
|
}
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
using InnovEnergy.Lib.StatusApi.Connections;
|
using InnovEnergy.Lib.StatusApi.Connections;
|
||||||
|
using InnovEnergy.Lib.StatusApi.Generator;
|
||||||
using InnovEnergy.Lib.Units.Composite;
|
using InnovEnergy.Lib.Units.Composite;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.StatusApi;
|
namespace InnovEnergy.Lib.StatusApi;
|
||||||
|
|
||||||
public abstract record ThreePhasePvInverterStatus(Ac1Phase Ac3, IReadOnlyList<DcPhase> Strings) :
|
[OpParallel]
|
||||||
|
public partial record ThreePhasePvInverterStatus :
|
||||||
DeviceStatus,
|
DeviceStatus,
|
||||||
IAc3Connection,
|
IAc3Connection,
|
||||||
IPvConnection;
|
IPvConnection
|
||||||
|
{
|
||||||
|
public Ac3Bus Ac { get; init; }
|
||||||
|
public IReadOnlyList<DcBus> Strings { get; init; }
|
||||||
|
}
|
||||||
|
|
|
@ -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<T, T, T> OpParallel = "|".CreateBinaryOpForProps<T>();
|
||||||
|
public static T operator |(T left, T right) => OpParallel(left, right);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Utils\Utils.csproj" />
|
<ProjectReference Include="../Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -20,7 +20,7 @@ public readonly partial struct UnixTimeSpan
|
||||||
|
|
||||||
public static UnixTimeSpan operator /(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks / b);
|
public static UnixTimeSpan operator /(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks / b);
|
||||||
public static UnixTimeSpan operator /(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks / (UInt32)b);
|
public static UnixTimeSpan operator /(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks / (UInt32)b);
|
||||||
public static UInt32 operator /(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks / b.Ticks;
|
public static UInt32 operator /(UnixTimeSpan a, UnixTimeSpan b) => a.Ticks / b.Ticks;
|
||||||
|
|
||||||
public static UnixTimeSpan operator %(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks % b);
|
public static UnixTimeSpan operator %(UnixTimeSpan a, UInt32 b) => new UnixTimeSpan(a.Ticks % b);
|
||||||
public static UnixTimeSpan operator %(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b);
|
public static UnixTimeSpan operator %(UnixTimeSpan a, Int32 b) => new UnixTimeSpan(a.Ticks % (UInt32)b);
|
|
@ -2,15 +2,19 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Angle;
|
using T = Angle;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(AngleConverter))]
|
||||||
public readonly partial struct Angle
|
public readonly partial struct Angle
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// 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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// addition
|
// parallel
|
||||||
|
|
||||||
#if Sum
|
#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);
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
#elif Mean
|
#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
|
#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));
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
if (d != 0m)
|
if (d == 0m)
|
||||||
return new T(0m);
|
return new T(0m);
|
||||||
|
|
||||||
var relativeError = Abs(left.Value - right.Value) / d;
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
@ -76,3 +79,19 @@ public readonly partial struct Angle
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class AngleConverter : JsonConverter<Angle>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,19 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = ApparentPower;
|
using T = ApparentPower;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(ApparentPowerConverter))]
|
||||||
public readonly partial struct ApparentPower
|
public readonly partial struct ApparentPower
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// 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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// addition
|
// parallel
|
||||||
|
|
||||||
#if Sum
|
#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);
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
#elif Mean
|
#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
|
#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));
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
if (d != 0m)
|
if (d == 0m)
|
||||||
return new T(0m);
|
return new T(0m);
|
||||||
|
|
||||||
var relativeError = Abs(left.Value - right.Value) / d;
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
@ -76,3 +79,19 @@ public readonly partial struct ApparentPower
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ApparentPowerConverter : JsonConverter<ApparentPower>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<Ac3Bus, Ac3Bus, Ac3Bus> OpParallel = "|".CreateBinaryOpForProps<Ac3Bus>();
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,52 +3,64 @@ using static DecimalMath.DecimalEx;
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
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));
|
get => _Voltage;
|
||||||
if (current < 0) throw new ArgumentException("RMS value cannot be negative", nameof(current));
|
init => _Voltage = value >= 0m ? value : throw new ArgumentException("RMS value cannot be negative");
|
||||||
|
|
||||||
Phi = phi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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 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
|
// 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
|
// currents (RMS) can be different and out of phase
|
||||||
// https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/
|
// https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/
|
||||||
|
|
||||||
// IF
|
// IF
|
||||||
// left(t) = ILeft sin(ωt)
|
// left(t) = ILeft sin(ωt)
|
||||||
// right(t) = IRight sin(ωt + φ).
|
// right(t) = IRight sin(ωt + φ).
|
||||||
// sum(t) = left(t) + right(t) = ISum sin(ωt + ψ).
|
// sum(t) = left(t) + right(t) = ISum sin(ωt + ψ).
|
||||||
// THEN
|
|
||||||
|
|
||||||
|
// THEN
|
||||||
// ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ).
|
// ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ).
|
||||||
// C = IRight * sin(φ) / sin(ψ).
|
// 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
|
// we can shift both waves by -left.Phi, so
|
||||||
// φ := right.phi - left.phi
|
// φ := 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 phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi));
|
||||||
var iSum = right.Current * Sin(phi) / Sin(phiSum);
|
var iSum = right.Current * Sin(phi) / Sin(phiSum);
|
||||||
|
|
||||||
return new AcPhase(v, iSum, phiSum);
|
return new AcPhase
|
||||||
|
{
|
||||||
|
Voltage = v,
|
||||||
|
Current = iSum,
|
||||||
|
Phi = phiSum
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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<DcBus, DcBus, DcBus> OpParallel = "|".CreateBinaryOpForProps<DcBus>();
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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; }
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.Units.Composite;
|
|
||||||
|
|
||||||
public abstract record Phase
|
|
||||||
(
|
|
||||||
Voltage Voltage,
|
|
||||||
Current Current
|
|
||||||
);
|
|
|
@ -2,15 +2,19 @@
|
||||||
#define Sum
|
#define Sum
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Current;
|
using T = Current;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(CurrentConverter))]
|
||||||
public readonly partial struct Current
|
public readonly partial struct Current
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// 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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// addition
|
// parallel
|
||||||
|
|
||||||
#if Sum
|
#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);
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
#elif Mean
|
#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
|
#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));
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
if (d != 0m)
|
if (d == 0m)
|
||||||
return new T(0m);
|
return new T(0m);
|
||||||
|
|
||||||
var relativeError = Abs(left.Value - right.Value) / d;
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
@ -76,3 +79,19 @@ public readonly partial struct Current
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class CurrentConverter : JsonConverter<Current>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using InnovEnergy.Lib.Time.Unix;
|
||||||
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
[Sum]
|
||||||
|
public readonly partial struct Energy
|
||||||
|
{
|
||||||
|
public static String Unit => "kWh";
|
||||||
|
public static String Symbol => "E";
|
||||||
|
|
||||||
|
public Energy(Decimal value) => Value = value;
|
||||||
|
|
||||||
|
public static Power operator /(Energy energy, TimeSpan timeSpan) => energy.Value * 1000m / (Decimal) timeSpan.TotalHours ;
|
||||||
|
public static Power operator /(Energy energy, UnixTimeSpan timeSpan) => energy.Value * 3_600_000m / timeSpan.Ticks;
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
|
#define Sum
|
||||||
|
|
||||||
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
using T = Energy;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(EnergyConverter))]
|
||||||
|
public readonly partial struct Energy
|
||||||
|
{
|
||||||
|
public Decimal Value { get; }
|
||||||
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
|
// scalar multiplication
|
||||||
|
|
||||||
|
public static T operator *(Decimal scalar, T t) => new T(scalar * t.Value);
|
||||||
|
public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value);
|
||||||
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
|
// parallel
|
||||||
|
|
||||||
|
#if Sum
|
||||||
|
|
||||||
|
public static T operator |(T left, T right) => new T(left.Value + right.Value);
|
||||||
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
|
#elif Mean
|
||||||
|
|
||||||
|
public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m);
|
||||||
|
|
||||||
|
#elif Equal
|
||||||
|
|
||||||
|
public static T operator |(T left, T right)
|
||||||
|
{
|
||||||
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
|
if (d == 0m)
|
||||||
|
return new T(0m);
|
||||||
|
|
||||||
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
|
||||||
|
const Decimal maxRelativeError = 0.05m;
|
||||||
|
|
||||||
|
if (relativeError > maxRelativeError)
|
||||||
|
throw new Exception($"{nameof(left)} and {nameof(right)} must be approximately equal.\n" +
|
||||||
|
$"Difference > {maxRelativeError * 100}% detected\n" +
|
||||||
|
$"{nameof(left)} : {left}\n" +
|
||||||
|
$"{nameof(right)}: {right}");
|
||||||
|
|
||||||
|
return new T((left.Value + right.Value) / 2m);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compare
|
||||||
|
|
||||||
|
public static Boolean operator ==(T left, T right) => left.Value == right.Value;
|
||||||
|
public static Boolean operator !=(T left, T right) => left.Value != right.Value;
|
||||||
|
public static Boolean operator > (T left, T right) => left.Value > right.Value;
|
||||||
|
public static Boolean operator < (T left, T right) => left.Value < right.Value;
|
||||||
|
public static Boolean operator >=(T left, T right) => left.Value >= right.Value;
|
||||||
|
public static Boolean operator <=(T left, T right) => left.Value <= right.Value;
|
||||||
|
|
||||||
|
// conversion
|
||||||
|
|
||||||
|
public static implicit operator T(Decimal d) => new T(d);
|
||||||
|
public static implicit operator T(Double d) => new T((Decimal)d);
|
||||||
|
public static implicit operator T(Int32 i) => new T(i);
|
||||||
|
public static implicit operator Decimal(T t) => t.Value;
|
||||||
|
|
||||||
|
// equality
|
||||||
|
|
||||||
|
public Boolean Equals(T other) => Value == other.Value;
|
||||||
|
public override Boolean Equals(Object? obj) => obj is T other && Equals(other);
|
||||||
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class EnergyConverter : JsonConverter<Energy>
|
||||||
|
{
|
||||||
|
public override Energy Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Energy(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Energy value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,19 @@
|
||||||
#define Equal
|
#define Equal
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Frequency;
|
using T = Frequency;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(FrequencyConverter))]
|
||||||
public readonly partial struct Frequency
|
public readonly partial struct Frequency
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// 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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// addition
|
// parallel
|
||||||
|
|
||||||
#if Sum
|
#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);
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
#elif Mean
|
#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
|
#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));
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
if (d != 0m)
|
if (d == 0m)
|
||||||
return new T(0m);
|
return new T(0m);
|
||||||
|
|
||||||
var relativeError = Abs(left.Value - right.Value) / d;
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
@ -76,3 +79,19 @@ public readonly partial struct Frequency
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class FrequencyConverter : JsonConverter<Frequency>
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,20 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Type
|
#define AggregationType
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Template;
|
using T = Template;
|
||||||
|
|
||||||
|
[JsonConverter(typeof(TemplateConverter))]
|
||||||
public readonly partial struct Template
|
public readonly partial struct Template
|
||||||
{
|
{
|
||||||
public Decimal Value { get; }
|
public Decimal Value { get; }
|
||||||
public override String ToString() => Value + Unit;
|
public override String ToString() => Value.RoundToSignificantDigits(Units.DisplaySignificantDigits) + Unit;
|
||||||
|
|
||||||
// scalar multiplication
|
// 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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// addition
|
// parallel
|
||||||
|
|
||||||
#if Sum
|
#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);
|
public static T operator -(T t) => new T(-t.Value);
|
||||||
|
|
||||||
#elif Mean
|
#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
|
#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));
|
var d = Max(Abs(left.Value), Abs(right.Value));
|
||||||
|
|
||||||
if (d != 0m)
|
if (d == 0m)
|
||||||
return new T(0m);
|
return new T(0m);
|
||||||
|
|
||||||
var relativeError = Abs(left.Value - right.Value) / d;
|
var relativeError = Abs(left.Value - right.Value) / d;
|
||||||
|
@ -76,3 +79,19 @@ public readonly partial struct Template
|
||||||
public override Int32 GetHashCode() => Value.GetHashCode();
|
public override Int32 GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class TemplateConverter : JsonConverter<Template>
|
||||||
|
{
|
||||||
|
public override Template Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return new Template(reader.GetDecimal());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Template value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits);
|
||||||
|
|
||||||
|
writer.WriteNumberValue(rounded);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue