diff --git a/csharp/App/Backend/Backend.csproj b/csharp/App/Backend/Backend.csproj index a63522893..f990e5c8a 100644 --- a/csharp/App/Backend/Backend.csproj +++ b/csharp/App/Backend/Backend.csproj @@ -22,21 +22,16 @@ - - - - - - + PreserveNewest diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index d9228f964..1995fbd99 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -16,7 +16,7 @@ public class Controller : ControllerBase [HttpPost(nameof(Login))] public ActionResult Login(String username, String password) { - var user = Db.GetUserByEmail(username); + var user = Db.GetUserByName(username); if (user is null || !user.VerifyPassword(password)) return Unauthorized(); @@ -162,14 +162,19 @@ public class Controller : ControllerBase [HttpGet(nameof(GetAllFoldersAndInstallations))] - public ActionResult> GetAllFoldersAndInstallations(Token authToken) + public ActionResult> GetAllFoldersAndInstallations(Token authToken) { var user = Db.GetSession(authToken)?.User; if (user is null) return Unauthorized(); + + var foldersAndInstallations = user + .AccessibleFoldersAndInstallations() + .OfType(); // Important! JSON serializer must see Objects otherwise + // it will just serialize the members of TreeNode %&@#!!! - return new (user.AccessibleFoldersAndInstallations()); + return new (foldersAndInstallations); } diff --git a/csharp/App/Backend/DataTypes/Methods/Folder.cs b/csharp/App/Backend/DataTypes/Methods/Folder.cs index 68c12c68b..ee93eeab8 100644 --- a/csharp/App/Backend/DataTypes/Methods/Folder.cs +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -68,36 +68,17 @@ public static class FolderMethods public static Folder? Parent(this Folder folder) { - return IsAbsoluteRoot(folder) + return IsRoot(folder) ? null : Db.GetFolderById(folder.ParentId); } - public static Boolean IsAbsoluteRoot(this Folder folder) + public static Boolean IsRoot(this Folder folder) { - return folder.ParentId == 0; // root has ParentId 0 by definition + return folder.ParentId <= 0 + && Db.GetFolderById(folder.Id)?.Id == 0; // might have been 0 because it is a relative root } - public static Boolean IsRelativeRoot(this Folder folder) - { - return folder.ParentId < 0; // TODO - } - - public static Boolean WasMoved(this Folder folder) - { - if (folder.IsRelativeRoot()) - return false; - - var existingFolder = Db.GetFolderById(folder.Id); - - return existingFolder is not null - && existingFolder.ParentId != folder.ParentId; - } - - public static Boolean Exists(this Folder folder) - { - return Db.Folders.Any(f => f.Id == folder.Id); - } } diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs index 134db9c11..d9fc72728 100644 --- a/csharp/App/Backend/DataTypes/Methods/Installation.cs +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -76,21 +76,20 @@ public static class InstallationMethods public static Folder? Parent(this Installation installation) { - return installation.IsRelativeRoot() - ? null - : Db.GetFolderById(installation.ParentId); + if (installation.ParentId <= 0) // relative root + { + var i = Db.GetInstallationById(installation.Id); + if (i is null) + return null; + + installation = i; + } + + return Db.GetFolderById(installation.ParentId); } - public static Boolean IsRelativeRoot(this Installation i) - { - return i.ParentId < 0; - } - public static Boolean WasMoved(this Installation installation) { - if (installation.IsRelativeRoot()) - return false; - var existingInstallation = Db.GetInstallationById(installation.Id); return existingInstallation is not null diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index a1ecf900c..5d5d2b4e7 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -1,5 +1,6 @@ using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Relations; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.Backend.DataTypes.Methods; @@ -13,20 +14,23 @@ public static class SessionMethods && folder is not null && user.HasWriteAccess && user.HasAccessTo(folder.Parent()) - && Db.Create(folder) + && Db.Create(folder) // TODO: these two in a transaction && Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id }); } public static Boolean Update(this Session? session, Folder? folder) { - var user = session?.User; + var user = session?.User; + var original = Db.GetFolderById(folder?.Id); return user is not null && folder is not null + && original is not null && user.HasWriteAccess && user.HasAccessTo(folder) - && (folder.IsRelativeRoot() || user.HasAccessTo(folder.Parent())) - && Db.Update(folder); + && folder + .WithParentOf(original) // prevent moving + .Apply(Db.Update); } public static Boolean Delete(this Session? session, Folder? folder) @@ -49,24 +53,27 @@ public static class SessionMethods && installation is not null && user.HasWriteAccess && user.HasAccessTo(installation.Parent()) - && Db.Create(installation) + && Db.Create(installation) // TODO: these two in a transaction && Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id }) && await installation.CreateBucket() && await installation.RenewS3BucketUrl(); // generation of access _after_ generation of - // bucket to prevent "zombie" access-rights. + // bucket to prevent "zombie" access-rights. } public static Boolean Update(this Session? session, Installation? installation) { var user = session?.User; + + var original = Db.GetInstallationById(installation?.Id); return user is not null && installation is not null + && original is not null && user.HasWriteAccess - && installation.Exists() && user.HasAccessTo(installation) - && (installation.IsRelativeRoot() || user.HasAccessTo(installation.Parent())) // TODO: triple check this - && Db.Update(installation); + && installation + .WithParentOf(original) // prevent moving + .Apply(Db.Update); } public static Boolean Delete(this Session? session, Installation? installation) @@ -77,37 +84,37 @@ public static class SessionMethods && installation is not null && user.HasWriteAccess && user.HasAccessTo(installation) - // && installation.DeleteBucket().Result // TODO: await? + // && installation.DeleteBucket().Result // TODO && Db.Delete(installation); } public static Boolean Create(this Session? session, User? newUser) { var sessionUser = session?.User; - - if (sessionUser is null || newUser is null || !sessionUser.HasWriteAccess) - return false; - newUser.ParentId = sessionUser.Id; // Important! - - return Db.Create(newUser); + return sessionUser is not null + && newUser is not null + && sessionUser.HasWriteAccess + && newUser + .WithParent(sessionUser) + .Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password)) + .Apply(Db.Create); } public static Boolean Update(this Session? session, User? editedUser) { var sessionUser = session?.User; - if (editedUser == null || sessionUser == null) return false; + var originalUser = Db.GetUserById(editedUser?.Id); - // TODO: make specific method for changing user account settings like pwd - // Password change is only allowed for oneself - editedUser.Password = editedUser.Id != sessionUser.Id - ? sessionUser.Password - : sessionUser.SaltAndHashPassword(editedUser.Password); - - return sessionUser.HasWriteAccess - && sessionUser.HasAccessTo(editedUser) - && (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent()) || editedUser.Id == sessionUser.Id) // TODO: triple check this - && Db.Update(editedUser); + return editedUser is not null + && sessionUser is not null + && originalUser is not null + && sessionUser.HasWriteAccess + && sessionUser.HasAccessTo(editedUser) + && editedUser + .WithParentOf(originalUser) // prevent moving + .WithPasswordOf(originalUser) + .Apply(Db.Update); } public static Boolean Delete(this Session? session, User? userToDelete) @@ -117,7 +124,7 @@ public static class SessionMethods return sessionUser is not null && userToDelete is not null && sessionUser.HasWriteAccess - && sessionUser.HasAccessTo(userToDelete) // TODO: && user.HasAccessTo(installation.Parent()) ??? + && sessionUser.HasAccessTo(userToDelete) && Db.Delete(userToDelete); } @@ -127,8 +134,8 @@ public static class SessionMethods var sessionUser = session?.User; return sessionUser is not null - && user is not null && installation is not null + && user is not null && user.IsDescendantOf(sessionUser) && sessionUser.HasAccessTo(installation) && !user.HasAccessTo(installation) @@ -140,8 +147,8 @@ public static class SessionMethods var sessionUser = session?.User; return sessionUser is not null - && user is not null && folder is not null + && user is not null && user.IsDescendantOf(sessionUser) && sessionUser.HasAccessTo(folder) && !user.HasAccessTo(folder) @@ -153,8 +160,8 @@ public static class SessionMethods var sessionUser = session?.User; return sessionUser is not null - && user is not null && installation is not null + && user is not null && user.IsDescendantOf(sessionUser) && sessionUser.HasAccessTo(installation) && user.HasAccessTo(installation) @@ -166,8 +173,8 @@ public static class SessionMethods var sessionUser = session?.User; return sessionUser is not null - && user is not null && folder is not null + && user is not null && user.IsDescendantOf(sessionUser) && sessionUser.HasAccessTo(folder) && user.HasAccessTo(folder) diff --git a/csharp/App/Backend/DataTypes/Methods/TreeNode.cs b/csharp/App/Backend/DataTypes/Methods/TreeNode.cs new file mode 100644 index 000000000..3c8a530bc --- /dev/null +++ b/csharp/App/Backend/DataTypes/Methods/TreeNode.cs @@ -0,0 +1,24 @@ +namespace InnovEnergy.App.Backend.DataTypes.Methods; + +public static class TreeNodeMethods +{ + + public static T WithParentOf(this T treeNode, T other) where T: TreeNode + { + treeNode.ParentId = other.ParentId; + return treeNode; + } + + public static T WithParent(this T treeNode, T other) where T: TreeNode + { + treeNode.ParentId = other.Id; + return treeNode; + } + + public static T HideParent(this T treeNode) where T: TreeNode + { + treeNode.ParentId = 0; + return treeNode; + } + +} \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index 410b4680b..5907b7c1e 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -34,7 +34,7 @@ public static class UserMethods // to a child folder of a folder he has already access to // TODO shouldn't we prevent doubling permissions? -K" // TODO yes we should -ig (still TODO) - // however we should leave the distinct, defensive programming... + // however we should still leave the distinct, defensive programming... } public static IEnumerable AccessibleFoldersAndInstallations(this User user) @@ -51,9 +51,9 @@ public static class UserMethods .InstallationAccess .Where(r => r.UserId == user.Id) .Select(r => r.InstallationId) - .Select(Db.GetInstallationById) + .Select(i => Db.GetInstallationById(i)) .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user + .Do(i => i.HideParent()); // hide inaccessible parents from calling user } public static IEnumerable DirectlyAccessibleFolders(this User user) @@ -62,9 +62,9 @@ public static class UserMethods .FolderAccess .Where(r => r.UserId == user.Id) .Select(r => r.FolderId) - .Select(Db.GetFolderById) + .Select(i => Db.GetFolderById(i)) .NotNull() - .Do(i => i.ParentId = 0); // hide inaccessible parents from calling user; + .Do(f => f.HideParent()); // hide inaccessible parents from calling user; } public static IEnumerable ChildUsers(this User parent) @@ -112,21 +112,17 @@ public static class UserMethods public static User? Parent(this User u) { - return u.IsAbsoluteRoot() + return u.IsRoot() ? null : Db.GetUserById(u.ParentId); } - public static Boolean IsAbsoluteRoot(this User u) - { - return u.ParentId == 0; + public static Boolean IsRoot(this User user) + { + return user.ParentId <= 0 + && Db.GetUserById(user.Id)?.Id == 0; // might have been 0 because it is a relative root } - public static Boolean IsRelativeRoot(this User u) - { - return u.ParentId < 0; - } - public static Boolean HasDirectAccessTo(this User user, Folder folder) { return Db @@ -174,12 +170,6 @@ public static class UserMethods .Contains(user); } - public static Boolean IsRelativeRoot(this User user, Installation i) - { - // TODO: determine not by id but by accessibility - return i.ParentId < 0; - } - public static String Salt(this User user) { // + id => salt unique per user @@ -188,6 +178,14 @@ public static class UserMethods return $"{user.Id}InnovEnergy"; } + public static User WithPasswordOf(this User user, User other) + { + user.Password = other.Password; + return user; + } + + + // TODO? private static Boolean IsValidEmail(String email) diff --git a/csharp/App/Backend/DataTypes/TreeNode.cs b/csharp/App/Backend/DataTypes/TreeNode.cs index de24da0b8..796ab9cc2 100644 --- a/csharp/App/Backend/DataTypes/TreeNode.cs +++ b/csharp/App/Backend/DataTypes/TreeNode.cs @@ -5,9 +5,9 @@ namespace InnovEnergy.App.Backend.DataTypes; public abstract partial class TreeNode { [PrimaryKey, AutoIncrement] - public Int64 Id { get; set; } - public String Name { get; set; } = ""; - public String Information { get; set; } = ""; // unstructured random info + public Int64 Id { get; set; } + public virtual String Name { get; set; } = ""; // overridden by User (unique) + public String Information { get; set; } = ""; // unstructured random info [Indexed] // parent/child relation public Int64 ParentId { get; set; } diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index 70ea9e034..449285fe3 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -4,11 +4,13 @@ namespace InnovEnergy.App.Backend.DataTypes; public class User : TreeNode { - [Indexed] public String Email { get; set; } = null!; public Boolean HasWriteAccess { get; set; } = false; public String Language { get; set; } = null!; public String Password { get; set; } = null!; - + + [Unique] + public override String Name { get; set; } = null!; + // TODO: must reset pwd } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs index d1b6d8318..40b40cf2c 100644 --- a/csharp/App/Backend/Database/Create.cs +++ b/csharp/App/Backend/Database/Create.cs @@ -1,5 +1,4 @@ using InnovEnergy.App.Backend.DataTypes; -using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.Relations; @@ -21,11 +20,6 @@ public static partial class Db public static Boolean Create(User user) { - if (GetUserByEmail(user.Email) is not null) - return false; - - user.Password = user.SaltAndHashPassword(user.Password); - return Connection.Insert(user) > 0; } diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 80cd15454..ebdab555c 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -1,3 +1,4 @@ +using System.Reactive.Concurrency; using System.Reactive.Linq; using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes.Methods; @@ -15,10 +16,10 @@ public static partial class Db private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath); - public static TableQuery Sessions => Connection.Table(); - public static TableQuery Folders => Connection.Table(); - public static TableQuery Installations => Connection.Table(); - public static TableQuery Users => Connection.Table(); + public static TableQuery Sessions => Connection.Table(); + public static TableQuery Folders => Connection.Table(); + public static TableQuery Installations => Connection.Table(); + public static TableQuery Users => Connection.Table(); public static TableQuery FolderAccess => Connection.Table(); public static TableQuery InstallationAccess => Connection.Table(); @@ -38,9 +39,10 @@ public static partial class Db }); Observable.Interval(TimeSpan.FromDays(0.5)) - .StartWith(0) // Do it right away (on startup) + .StartWith(0) // Do it right away (on startup) + .ObserveOn(TaskPoolScheduler.Default) .SelectMany(Cleanup) - .Subscribe(); // and then daily + .Subscribe(); } diff --git a/csharp/App/Backend/Database/Read.cs b/csharp/App/Backend/Database/Read.cs index 53c0586de..5a4eae7bb 100644 --- a/csharp/App/Backend/Database/Read.cs +++ b/csharp/App/Backend/Database/Read.cs @@ -7,13 +7,13 @@ namespace InnovEnergy.App.Backend.Database; public static partial class Db { - public static Folder? GetFolderById(Int64 id) + public static Folder? GetFolderById(Int64? id) { return Folders .FirstOrDefault(f => f.Id == id); } - public static Installation? GetInstallationById(Int64 id) + public static Installation? GetInstallationById(Int64? id) { return Installations .FirstOrDefault(i => i.Id == id); @@ -25,22 +25,10 @@ public static partial class Db .FirstOrDefault(u => u.Id == id); } - // private!! - private static Session? GetSessionById(Int64 id) - { - #pragma warning disable CS0618 - - return Sessions - .FirstOrDefault(u => u.Id == id); - - #pragma warning restore CS0618 - } - - - public static User? GetUserByEmail(String email) + public static User? GetUserByName(String userName) { return Users - .FirstOrDefault(u => u.Email == email); + .FirstOrDefault(u => u.Name == userName); } public static Session? GetSession(String token) @@ -62,25 +50,4 @@ public static partial class Db return session; } - - public static User? GetUserBySessionToken(String token) - { - var session = Sessions - .FirstOrDefault(s => s.Token == token); - - // cannot user session.Expired in the DB query above. - // It does not exist in the db (IgnoreAttribute) - - if (session is null) - return null; - - if (!session.Valid) - { - Delete(session); - return null; - } - - return GetUserById(session.UserId); - } - } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Update.cs b/csharp/App/Backend/Database/Update.cs index 72aef3015..923d81345 100644 --- a/csharp/App/Backend/Database/Update.cs +++ b/csharp/App/Backend/Database/Update.cs @@ -1,7 +1,4 @@ using InnovEnergy.App.Backend.DataTypes; -using InnovEnergy.App.Backend.DataTypes.Methods; -using InnovEnergy.App.Backend.Relations; - namespace InnovEnergy.App.Backend.Database; @@ -10,54 +7,24 @@ public static partial class Db { public static Boolean Update(Folder folder) { - if (folder.IsRelativeRoot()) // TODO: triple check - { - var original = GetFolderById(folder.Id); - if (original is null) - return false; - - folder.ParentId = original.ParentId; - } - - return Connection.InsertOrReplace(folder) > 0; + return Connection.Update(folder) > 0; } public static Boolean Update(Installation installation) { - if (installation.IsRelativeRoot()) // TODO: triple check - { - var original = GetInstallationById(installation.Id); - if (original is null) - return false; - - installation.ParentId = original.ParentId; - } - - return Connection.InsertOrReplace(installation) > 0; + return Connection.Update(installation) > 0; } - public static Boolean Update(User user) { var originalUser = GetUserById(user.Id); return originalUser is not null - && user.Id == originalUser.Id // these columns must not be modified! - && user.ParentId == originalUser.ParentId - && user.Email == originalUser.Email - && Connection.InsertOrReplace(user) > 0; + && user.ParentId == originalUser.ParentId // these columns must not be modified! + && user.Name == originalUser.Name + && Connection.Update(user) > 0; } - public static Boolean Update(this Session session) - { - #pragma warning disable CS0618 - var originalSession = GetSessionById(session.Id); - #pragma warning restore CS0618 - - return originalSession is not null - && session.Token == originalSession.Token // these columns must not be modified! - && session.UserId == originalSession.UserId - && Connection.InsertOrReplace(session) > 0; - } + } \ No newline at end of file diff --git a/csharp/App/Backend/HeaderFilter.cs b/csharp/App/Backend/HeaderFilter.cs deleted file mode 100644 index f3520b75e..000000000 --- a/csharp/App/Backend/HeaderFilter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace InnovEnergy.App.Backend; - -/// -/// This is for convenient testing! Todo throw me out? -/// Operation filter to add the requirement of the custom header -/// -public class HeaderFilter : IOperationFilter -{ - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - operation.Parameters ??= new List(); - - operation.Parameters.Add(new OpenApiParameter - { - Name = "auth", - In = ParameterLocation.Header, - Content = new Dictionary(), - Required = false - }); - } -} \ No newline at end of file diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index 385c6250b..4a24d1aa7 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -4,16 +4,15 @@ namespace InnovEnergy.App.Backend; public static class Program { + + // TODO: Trash + public static void Main(String[] args) { //Db.CreateFakeRelations(); var builder = WebApplication.CreateBuilder(args); - //builder.Services.AddHttpContextAccessor(); - //builder.Services.AddEndpointsApiExplorer(); - //builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod())); - builder.Services.AddControllers(); builder.Services.AddSwaggerGen(c => { @@ -30,9 +29,8 @@ public static class Program app.UseSwaggerUI(); } - //app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ; + app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ; app.UseHttpsRedirection(); - //app.UseAuthorization(); app.MapControllers(); app.Run(); diff --git a/csharp/App/Backend/Properties/launchSettings.json b/csharp/App/Backend/Properties/launchSettings.json index 062a71f67..1729b03cd 100644 --- a/csharp/App/Backend/Properties/launchSettings.json +++ b/csharp/App/Backend/Properties/launchSettings.json @@ -1,31 +1,15 @@ { "$schema": "https://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:63205", - "sslPort": 44319 - } - }, "profiles": { "Backend": { "commandName": "Project", "dotnetRunMessages": true, - "launchBrowser": true, + "launchUrl": "swagger", "applicationUrl": "https://localhost:7087;http://localhost:5031", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } } diff --git a/csharp/App/Backend/Relations/Session.cs b/csharp/App/Backend/Relations/Session.cs index fa9a993d4..03af863ee 100644 --- a/csharp/App/Backend/Relations/Session.cs +++ b/csharp/App/Backend/Relations/Session.cs @@ -1,6 +1,5 @@ using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.DataTypes; -using InnovEnergy.Lib.Utils; using SQLite; namespace InnovEnergy.App.Backend.Relations; @@ -13,8 +12,8 @@ public class Session : Relation [Indexed] public Int64 UserId { get => Right; init => Right = value;} [Indexed] public DateTime LastSeen { get; set; } - [Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge - && !User.Email.IsNullOrEmpty(); + [Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge + && (User) is not null; [Ignore] public User User => _User ??= Db.GetUserById(UserId)!; diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index 8cddf6109..5ed0b4ad0 100644 Binary files a/csharp/App/Backend/db.sqlite and b/csharp/App/Backend/db.sqlite differ diff --git a/csharp/Lib/SysTools/Edges/RemoteCommandToProcess.cs b/csharp/Lib/SysTools/Edges/RemoteCommandToProcess.cs deleted file mode 100644 index 984594e11..000000000 --- a/csharp/Lib/SysTools/Edges/RemoteCommandToProcess.cs +++ /dev/null @@ -1,50 +0,0 @@ -using InnovEnergy.Lib.SysTools.Process; -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class RemoteCommandToProcess -{ - public static ProcessResult ExecuteBlocking(this RemoteCommand command, Dictionary variables = null, Boolean logToConsole = false) - { - return command - .ToLocalCommand() - .ExecuteBlocking(variables, logToConsole); - } - - public static AsyncProcess ToAsyncProcess(this RemoteCommand command, Dictionary variables = null) - { - return command - .ToLocalCommand() - .ToAsyncProcess(variables); - } - - public static SyncProcess ToSyncProcess(this RemoteCommand command, Dictionary variables = null) - { - return command - .ToLocalCommand() - .ToSyncProcess(variables); - } - - private static SysCommand ToLocalCommand(this RemoteCommand command) - { - var ssh = "ssh" - .Opt("o", "ConnectTimeout=5") // TODO - .Opt("o", "PasswordAuthentication=no") - .Opt("o", "StrictHostKeyChecking=no") - .Opt("o", "UserKnownHostsFile=/dev/null") - .Opt("o", "LogLevel=ERROR"); - - var host = command.Host; - - if (host.KeyFile.HasValue) - ssh = ssh.Opt("i", host.KeyFile.Value); - - if (host.Port != 22) - ssh = ssh.Opt("p", host.Port); - - return ssh - .Arg($"{host.User}@{host.Address}") - .Arg(command.Path + command.Args); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/RemotePathToRemoteCommand.cs b/csharp/Lib/SysTools/Edges/RemotePathToRemoteCommand.cs deleted file mode 100644 index 5e882d473..000000000 --- a/csharp/Lib/SysTools/Edges/RemotePathToRemoteCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class RemotePathToRemoteCommand -{ - public static RemoteCommand ToCommand(this RemotePath remotePath) => new RemoteCommand(remotePath); - - public static RemoteCommand Opt1(this RemotePath remotePath, String option) - { - return remotePath - .ToCommand() - .Opt1(option); - } - - public static RemoteCommand Opt1(this RemotePath remotePath, String option, Object value, String separator = " ") - { - return remotePath - .ToCommand() - .Opt1(option, value, separator); - } - - public static RemoteCommand Opt2(this RemotePath remotePath, String option) - { - return remotePath - .ToCommand() - .Opt2(option); - } - - public static RemoteCommand Opt2(this RemotePath remotePath, String option, Object value, String separator = "=") - { - return remotePath - .ToCommand() - .Opt2(option, value, separator); - } - - public static RemoteCommand Opt(this RemotePath remotePath, String option) - { - return remotePath - .ToCommand() - .Opt(option); - } - - public static RemoteCommand Opt(this RemotePath remotePath, String option, Object value) - { - return remotePath - .ToCommand() - .Opt(option, value); - } - - public static RemoteCommand Arg(this RemotePath remotePath, Object argument) - { - return remotePath - .ToCommand() - .Arg(argument); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SshHostToRemoteCommand.cs b/csharp/Lib/SysTools/Edges/SshHostToRemoteCommand.cs deleted file mode 100644 index c9cfba220..000000000 --- a/csharp/Lib/SysTools/Edges/SshHostToRemoteCommand.cs +++ /dev/null @@ -1,11 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SshHostToRemoteCommand -{ - public static RemoteCommand Command(this SshHost host, SysPath path) - { - return new RemoteCommand(host, path); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SshHostToRemotePath.cs b/csharp/Lib/SysTools/Edges/SshHostToRemotePath.cs deleted file mode 100644 index 4833861b6..000000000 --- a/csharp/Lib/SysTools/Edges/SshHostToRemotePath.cs +++ /dev/null @@ -1,11 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SshHostToRemotePath -{ - public static RemotePath WithPath(this SshHost host, SysPath path) - { - return new RemotePath(host, path); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/StringToCommand.cs b/csharp/Lib/SysTools/Edges/StringToCommand.cs deleted file mode 100644 index 62c595f75..000000000 --- a/csharp/Lib/SysTools/Edges/StringToCommand.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class StringToCommand -{ - public static SysCommand ToCommand(this String cmd) => new SysCommand(cmd); - - public static SysCommand Opt1(this String cmd, String option) - { - return cmd - .ToCommand() - .Opt1(option); - } - - public static SysCommand Opt1(this String cmd, String option, Object value, String separator = " ") - { - return cmd - .ToCommand() - .Opt1(option, value, separator); - } - - public static SysCommand Opt2(this String cmd, String option) - { - return cmd - .ToCommand() - .Opt2(option); - } - - public static SysCommand Opt2(this String cmd, String option, Object value, String separator = "=") - { - return cmd - .ToCommand() - .Opt2(option, value, separator); - } - - public static SysCommand Opt(this String cmd, String option) - { - return cmd - .ToCommand() - .Opt(option); - } - - public static SysCommand Opt(this String cmd, String option, Object value) - { - return cmd - .ToCommand() - .Opt(option, value); - } - - public static SysCommand Arg(this String cmd, Object argument) - { - return cmd - .ToCommand() - .Arg(argument); - } - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/StringToProcess.cs b/csharp/Lib/SysTools/Edges/StringToProcess.cs deleted file mode 100644 index e8d3c4d63..000000000 --- a/csharp/Lib/SysTools/Edges/StringToProcess.cs +++ /dev/null @@ -1,26 +0,0 @@ -using InnovEnergy.Lib.SysTools.Process; - -namespace InnovEnergy.Lib.SysTools.Edges; - -using Env = Dictionary; - - -public static class StringToProcess -{ - - public static ProcessResult ExecuteBlocking(this String command, Env variables = null, Boolean logToConsole = false) - { - return SysCommandToProcess.ExecuteBlocking(command, variables, logToConsole); - } - - public static AsyncProcess ToAsyncProcess(this String command, Env variables = null, Boolean logToConsole = false) - { - return new AsyncProcess(command, variables, logToConsole); - } - - public static SyncProcess ToSyncProcess(this String command, Env variables = null, Boolean logToConsole = false) - { - return new SyncProcess(command, variables, logToConsole); - } - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/StringToRemotePath.cs b/csharp/Lib/SysTools/Edges/StringToRemotePath.cs deleted file mode 100644 index 9a32e16b1..000000000 --- a/csharp/Lib/SysTools/Edges/StringToRemotePath.cs +++ /dev/null @@ -1,8 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class StringToRemotePath -{ - public static RemotePath At(this String path, SshHost host) => new RemotePath(host, path); -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/StringToSysPath.cs b/csharp/Lib/SysTools/Edges/StringToSysPath.cs deleted file mode 100644 index bf7a93615..000000000 --- a/csharp/Lib/SysTools/Edges/StringToSysPath.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class StringToSysPath -{ - public static SysPath ToPath(this String path) => new SysPath(path); -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SysCommandToProcess.cs b/csharp/Lib/SysTools/Edges/SysCommandToProcess.cs deleted file mode 100644 index b8b142b6f..000000000 --- a/csharp/Lib/SysTools/Edges/SysCommandToProcess.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Diagnostics; -using System.Reactive.Linq; -using System.Text; -using InnovEnergy.Lib.SysTools.Process; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SysCommandToProcess -{ - public static ProcessResult ExecuteBlocking(this SysCommand command, Dictionary variables = null, Boolean logToConsole =false) - { - var process = command.ToAsyncProcess(variables, logToConsole); - - var stdOut = new List(); - var stdErr = new List(); - var output = new StringBuilder(); - - process.StandardOut.Subscribe(o => - { - stdOut.Add(o); - output.AppendLine(o); - }); - - process.StandardErr.Subscribe(e => - { - stdErr.Add(e); - output.AppendLine(e); - }); - - process.Run(); - - - var exitCode = process.ExitCode.Wait(); - - process.Terminate(); - return new ProcessResult(exitCode, stdOut, stdErr, output.ToString()); - } - - public static AsyncProcess ToAsyncProcess(this SysCommand command, Dictionary variables = null, Boolean logToConsole = false) - { - return new AsyncProcess(command, variables, logToConsole); - } - - public static SyncProcess ToSyncProcess(this SysCommand command, Dictionary variables = null, Boolean logToConsole = false) - { - return new SyncProcess(command, variables, logToConsole); - } - - public static System.Diagnostics.Process ToConsoleProcess(this SysCommand command, Dictionary variables = null) - { - var startInfo = new ProcessStartInfo - { - RedirectStandardOutput = false, - RedirectStandardError = false, - RedirectStandardInput = false, - UseShellExecute = false, - CreateNoWindow = true, - Arguments = command.Args, - FileName = command.Path, - }; - - if (variables != null) - foreach (var kv in variables) - startInfo.EnvironmentVariables[kv.Key] = kv.Value; - - return new System.Diagnostics.Process - { - StartInfo = startInfo, - EnableRaisingEvents = true - }; - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SysCommandToRemoteCommand.cs b/csharp/Lib/SysTools/Edges/SysCommandToRemoteCommand.cs deleted file mode 100644 index faa77d432..000000000 --- a/csharp/Lib/SysTools/Edges/SysCommandToRemoteCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SysCommandToRemoteCommand -{ - public static RemoteCommand At(this SysCommand command, SshHost host) - { - var (path, args) = command; - return new RemoteCommand(host, path, args); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SysPathToProcess.cs b/csharp/Lib/SysTools/Edges/SysPathToProcess.cs deleted file mode 100644 index 60a030fe8..000000000 --- a/csharp/Lib/SysTools/Edges/SysPathToProcess.cs +++ /dev/null @@ -1,21 +0,0 @@ -using InnovEnergy.Lib.SysTools.Process; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SysPathToProcess -{ - public static ProcessResult ExecuteBlocking(this SysPath command, Dictionary variables = null, Boolean logToConsole = false) - { - return SysCommandToProcess.ExecuteBlocking(command, variables, logToConsole); - } - - public static AsyncProcess ToAsyncProcess(this SysPath command, Dictionary variables = null, Boolean logToConsole = false) - { - return new AsyncProcess(command, variables, logToConsole); - } - - public static SyncProcess ToSyncProcess(this SysPath command, Dictionary variables = null, Boolean logToConsole = false) - { - return new SyncProcess(command, variables, logToConsole); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SysPathToRemotePath.cs b/csharp/Lib/SysTools/Edges/SysPathToRemotePath.cs deleted file mode 100644 index 410f6bb7e..000000000 --- a/csharp/Lib/SysTools/Edges/SysPathToRemotePath.cs +++ /dev/null @@ -1,11 +0,0 @@ -using InnovEnergy.Lib.SysTools.Remote; - -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SysPathToRemotePath -{ - public static RemotePath At(this SysPath path, SshHost host) - { - return new RemotePath(host, path); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Edges/SysPathToSysCommand.cs b/csharp/Lib/SysTools/Edges/SysPathToSysCommand.cs deleted file mode 100644 index 9f27e68ff..000000000 --- a/csharp/Lib/SysTools/Edges/SysPathToSysCommand.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Edges; - -public static class SysPathToSysCommand -{ - public static SysCommand ToCommand(this SysPath path) => new SysCommand(path); - - public static SysCommand Opt1(this SysPath path, String option) - { - return path - .ToCommand() - .Opt1(option); - } - - public static SysCommand Opt1(this SysPath path, String option, Object value, String separator = " ") - { - return path - .ToCommand() - .Opt1(option, value, separator); - } - - public static SysCommand Opt2(this SysPath path, String option) - { - return path - .ToCommand() - .Opt2(option); - } - - public static SysCommand Opt2(this SysPath path, String option, Object value, String separator = "=") - { - return path - .ToCommand() - .Opt2(option, value, separator); - } - - public static SysCommand Opt(this SysPath path, String option) - { - return path - .ToCommand() - .Opt(option); - } - - public static SysCommand Opt(this SysPath path, String option, Object value) - { - return path - .ToCommand() - .Opt(option, value); - } - - public static SysCommand Arg(this SysPath path, Object argument) - { - return path - .ToCommand() - .Arg(argument); - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/FileIo.cs b/csharp/Lib/SysTools/FileIo.cs index bec888e56..553ef1418 100644 --- a/csharp/Lib/SysTools/FileIo.cs +++ b/csharp/Lib/SysTools/FileIo.cs @@ -3,7 +3,6 @@ using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.SysTools; -[Obsolete("Needs rework before use")] public static class FileIo { public static Boolean Exists (this SysPath path) => path.FileExists() || path.DirectoryExists(); @@ -92,7 +91,7 @@ public static class FileIo SysPath Target(SysPath path) => targetDir.Append(path.RelativeTo(sourceDir)); - sourceDir.Traverse(Directories) + sourceDir.TraverseDepthFirstPreOrder(Directories) .Do(d => Target(d).CreateDirectory()) .SelectMany(Files) .ForEach(f => f.CopyFileTo(Target(f))); @@ -116,7 +115,7 @@ public static class FileIo public static IEnumerable DescendantDirectories(this SysPath path) { - return path.Traverse(Directories); + return path.TraverseDepthFirstPreOrder(Directories); } public static IEnumerable DescendantFiles(this SysPath path) diff --git a/csharp/Lib/SysTools/Process/AsyncProcess.cs b/csharp/Lib/SysTools/Process/AsyncProcess.cs deleted file mode 100644 index ee0137482..000000000 --- a/csharp/Lib/SysTools/Process/AsyncProcess.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System.Diagnostics; -using System.Reactive.Concurrency; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using InnovEnergy.Lib.SysTools.Utils; -using static System.ConsoleColor; - -namespace InnovEnergy.Lib.SysTools.Process; - -using Env = Dictionary; - -[Obsolete("Use CliWrap instead")] -public class AsyncProcess -{ - private readonly Subject _StandardIn; - private readonly Subject _StandardOut; - private readonly Subject _StandardErr; - private readonly ReplaySubject _ExitCode; - - public SysCommand Command { get; } - public Env Environment { get; } - - public IObserver StandardIn => _StandardIn; - public IObservable StandardOut => _StandardOut; - public IObservable StandardErr => _StandardErr; - public IObservable ExitCode => _ExitCode; - - public Boolean HasStarted => Process != null; - public Boolean HasFinished => HasStarted && Process.HasExited; - - private System.Diagnostics.Process Process { get; set; } - - public AsyncProcess(SysCommand command, Env environment = null, Boolean logToConsole = false) - { - Command = command; - Environment = environment; - - _StandardIn = new Subject(); - _StandardOut = new Subject(); - _StandardErr = new Subject(); - _ExitCode = new ReplaySubject(); - - if (logToConsole) - LogToConsole(); - } - - private void LogToConsole() - { - _StandardOut.Subscribe(d => d.WriteLine()); - _StandardErr.Subscribe(d => d.WriteLine(Red)); - _ExitCode.Subscribe(ec => - { - "ExitCode: ".Write(Cyan); - ec.WriteLine(ec == 0 ? Green : Red); - }); - } - - - public Int32 WaitForExit() - { - EnsureStarted(); - - return ExitCode.Wait(); - - //Process.WaitForExit(); // TODO - //return Process.ExitCode; - } - - private void EnsureNotStarted() - { - if (HasStarted) - throw new Exception("Process has already started"); - } - - private void EnsureStarted() - { - if (!HasStarted) - throw new Exception("Process has not yet started"); - } - - - public AsyncProcess Run() - { - EnsureNotStarted(); - Process = InitProcess(); - - return this; - } - - private System.Diagnostics.Process InitProcess() - { - var startInfo = new ProcessStartInfo - { - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - UseShellExecute = false, - CreateNoWindow = true, - Arguments = Command.Args, - FileName = Command.Path, - }; - - foreach (var ev in Environment.EmptyIfNull()) - startInfo.EnvironmentVariables[ev.Key] = ev.Value; - - var proc = new System.Diagnostics.Process - { - StartInfo = startInfo, - EnableRaisingEvents = true - }; - - proc.Start(); // must start BEFORE we access Streams - - Observable.Repeat(proc.StandardOutput, TaskPoolScheduler.Default) - .Select(s => s.ReadLine()) - .TakeWhile(l => l != null) - .Subscribe(_StandardOut); - - Observable.Repeat(proc.StandardError, TaskPoolScheduler.Default) - .Select(s => s.ReadLine()) - .TakeWhile(l => l != null) - .Subscribe(_StandardErr); - - _StandardIn.Subscribe(onNext: proc.StandardInput.WriteLine, - onError: _ => proc.StandardInput.Close(), - onCompleted: proc.StandardInput.Close); - - Observable.FromEventPattern(e => proc.Exited += e, e => proc.Exited -= e) - .Take(1) - .Do(_=>Console.WriteLine("Exited")) - .OfType() - .Concat(StandardOut.IgnoreElements()) // make sure std streams finish first - .Concat(StandardErr.IgnoreElements()) - .Select(_ => Process.ExitCode) - .Subscribe(_ExitCode); - - _ExitCode.Subscribe(_ => - { - proc.StandardInput.Close(); - proc.StandardOutput.Close(); - proc.StandardError.Close(); - }); - - return proc; - } - - public AsyncProcess Terminate() - { - - try - { - Process.Kill(); - } - catch - { - // ignored - } - - - return this; - } - - public AsyncProcess Kill() - { - EnsureStarted(); - - try - { - Process.Kill(); - } - catch - { - // ignored - } - - return this; - } - - - public IDisposable ThrowOnStdErr() - { - return StandardErr.Subscribe(e => throw new Exception(e)); - } - - public override String ToString() => Command.ToString(); // TODO: env -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Process/ProcessResult.cs b/csharp/Lib/SysTools/Process/ProcessResult.cs deleted file mode 100644 index e8af589b3..000000000 --- a/csharp/Lib/SysTools/Process/ProcessResult.cs +++ /dev/null @@ -1,63 +0,0 @@ -using InnovEnergy.Lib.SysTools.Utils; - -namespace InnovEnergy.Lib.SysTools.Process; - -[Obsolete("Use CliWrap instead")] -public readonly struct ProcessResult -{ - public ProcessResult(Int32 exitCode, - IReadOnlyList stdOut, - IReadOnlyList stdErr, - String output) - { - ExitCode = exitCode; - StdOut = stdOut; - StdErr = stdErr; - Output = output; - } - - public Int32 ExitCode { get; } - public String Output { get; } - - public IReadOnlyList StdOut { get; } - public IReadOnlyList StdErr { get; } - - public ProcessResult ThrowOnError() - { - if (ExitCode != 0) - { - if (StdErr.Count ==0) - throw new Exception(nameof(AsyncProcess) + " exited with exit code " + ExitCode); - - throw new Exception(StdErr.Aggregate((a, b) => a.NewLine() + b)); - } - - return this; - } - - public ProcessResult OnError(Action action) - { - if (Failed) action(this); - return this; - } - - public ProcessResult OnSuccess(Action action) - { - if (Succeeded) action(this); - return this; - } - - public ProcessResult Dump() - { - Output.Write(); - return this; - } - - public Boolean Succeeded => ExitCode == 0; - public Boolean Failed => ExitCode != 0; - - public override String ToString() - { - return Output.NewLine() + "ExitCode: " + ExitCode; - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Process/SyncProcess.cs b/csharp/Lib/SysTools/Process/SyncProcess.cs deleted file mode 100644 index 7d47f447f..000000000 --- a/csharp/Lib/SysTools/Process/SyncProcess.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System.Diagnostics; -using System.Text.RegularExpressions; -using InnovEnergy.Lib.SysTools.Utils; -using static System.ConsoleColor; - -namespace InnovEnergy.Lib.SysTools.Process; - -using Env = Dictionary; - -[Obsolete("Use CliWrap instead")] -public class SyncProcess -{ - public SysCommand Command { get; } - public Env Environment { get; } - public Boolean LogToConsole { get; } - - public Boolean HasFinished => Process.HasExited; - - private Task<(DateTime timeStamp, StdOutput output)> _ReadStdOut; - private Task<(DateTime timeStamp, StdError error )> _ReadStdErr; - - private System.Diagnostics.Process Process { get; } - - public SyncProcess(SysCommand command, Env environment = null, Boolean logToConsole = false) - { - Command = command; - Environment = environment; - LogToConsole = logToConsole; - Process = InitProcess(); - - _ReadStdOut = ReadNextStdOut(); - _ReadStdErr = ReadNextStdErr(); - } - - private Task<(DateTime timeStamp, StdOutput output)> ReadNextStdOut() - { - Debug.Assert(_ReadStdOut == null || _ReadStdOut.IsCompleted); - - return Process - .StandardOutput - .ReadLineAsync() - .ContinueWith(t => (DateTime.Now, SyncProcessOutput.StdOutput(t.Result))); - - } - - private Task<(DateTime timeStamp, StdError error)> ReadNextStdErr() - { - Debug.Assert(_ReadStdErr == null || _ReadStdErr.IsCompleted); - - return Process - .StandardError - .ReadLineAsync() - .ContinueWith(t => (DateTime.Now, SyncProcessOutput.StdError(t.Result))); - } - - - private System.Diagnostics.Process InitProcess() - { - var startInfo = new ProcessStartInfo - { - RedirectStandardOutput = true, - RedirectStandardError = true, - RedirectStandardInput = true, - UseShellExecute = false, - CreateNoWindow = true, - Arguments = Command.Args.Trim(), - FileName = Command.Path, - }; - - foreach (var ev in Environment.EmptyIfNull()) - startInfo.EnvironmentVariables[ev.Key] = ev.Value; - - var proc = new System.Diagnostics.Process - { - StartInfo = startInfo, - EnableRaisingEvents = true - }; - - proc.Start(); // must start BEFORE we access Streams - - return proc; - } - - public Match ReadStdOutUntil(Regex rx) - { - foreach (var l in ReadStdOut()) - { - - var match = rx.Match(l); - if (match.Success) - return match; - } - - throw new Exception("no matching output"); - } - - public String ReadStdOutUntil(String line) - { - return ReadStdOutUntil(l => l == line); - } - - - public String ReadStdOutUntil(Func predicate) - { - foreach (var l in ReadStdOut()) - { - if (predicate(l)) - return l; - } - - throw new Exception("no matching output"); - } - - public IEnumerable ReadStdOut(TimeSpan timeOut = default) => ReadMany(timeOut).ThrownOnStdErr(); - - public IEnumerable ReadMany(TimeSpan timeOut = default) - { - SyncProcessOutput output; - - do - { - output = ReadNext(timeOut); - yield return output; - } - while (!(output is ExitCode) && !(output is TimeoutError)); - } - - public SyncProcessOutput ReadNext(TimeSpan timeOut = default) - { - var wait = timeOut == default - ? Task.WaitAny(_ReadStdOut, _ReadStdErr) - : Task.WaitAny(_ReadStdOut, _ReadStdErr, Task.Delay(timeOut)); - - if (wait == 2) - { - if (LogToConsole) - "Timeout".WriteLine(Red); - - return SyncProcessOutput.TimeoutError(timeOut); - } - - if (_ReadStdOut.IsCompleted && _ReadStdErr.IsCompleted) - { - var (outTimeStamp, output) = _ReadStdOut.Result; - var (errTimeStamp, error) = _ReadStdErr.Result; - - if (outTimeStamp < errTimeStamp && output != null) - { - _ReadStdOut = ReadNextStdOut(); - - if (LogToConsole) - output.Data.WriteLine(); - - return output; - } - - if (error != null) - { - _ReadStdErr = ReadNextStdErr(); - - if (LogToConsole) - error.Data.WriteLine(Red); - - return error; - } - - Process.WaitForExit(); - - var exitCode = Process.ExitCode; - - if (LogToConsole) - { - "ExitCode: ".Write(Cyan); - exitCode.WriteLine(exitCode == 0 ? Green : Red); - } - - return SyncProcessOutput.ExitCode(exitCode); - } - - if (_ReadStdOut.IsCompleted) - { - var (_, output) = _ReadStdOut.Result; - - if (output != null) - { - _ReadStdOut = ReadNextStdOut(); - return output; - } - - } - - if (_ReadStdErr.IsCompleted) - { - var (_, error) = _ReadStdErr.Result; - - if (error != null) - { - _ReadStdErr = ReadNextStdErr(); - return error; - } - - } - - throw new Exception("This should not happen"); - } - - public SyncProcess Terminate() - { - Process.Kill(); - return this; - } - - public SyncProcess Kill() - { - Process.Kill(); - return this; - } - - public void Write(String line) - { - Process.StandardInput.WriteLine(line); - } - - public override String ToString() => Command.ToString(); // TODO: env -} - - -public abstract class SyncProcessOutput -{ - public static StdOutput StdOutput (String data ) => new StdOutput(data); - public static StdError StdError (String data ) => new StdError(data); - public static ExitCode ExitCode (Int32 data ) => new ExitCode(data); - public static TimeoutError TimeoutError (TimeSpan timeout) => new TimeoutError(timeout); -} - -public class ExitCode : SyncProcessOutput -{ - public ExitCode(Int32 data) { Data = data;} - public Int32 Data { get; } - - public override String ToString() => Data.ToString(); -} - -public class StdError : SyncProcessOutput -{ - public StdError(String data) { Data = data;} - public String Data { get; } - - public override String ToString() => Data; -} - -public class StdOutput : SyncProcessOutput -{ - public StdOutput(String data) { Data = data; } - public String Data { get; } - - public override String ToString() => Data; -} - -public class TimeoutError : SyncProcessOutput -{ - public TimeoutError(TimeSpan timeout) { Timeout = timeout; } - public TimeSpan Timeout { get; } - - public override String ToString() => Timeout.ToString(); -} - - -public static class ProcessOutputExtensions -{ - public static IEnumerable ThrownOnStdErr(this IEnumerable po) - { - foreach (var p in po) - { - if (p is StdOutput o) - yield return o.Data; - - if (p is StdError e) - throw new Exception(e.Data); - - // Ignore exit code - } - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Remote/RemoteCommand.cs b/csharp/Lib/SysTools/Remote/RemoteCommand.cs deleted file mode 100644 index fa80d2671..000000000 --- a/csharp/Lib/SysTools/Remote/RemoteCommand.cs +++ /dev/null @@ -1,117 +0,0 @@ -using InnovEnergy.Lib.SysTools.Utils; - -namespace InnovEnergy.Lib.SysTools.Remote; - -[Obsolete("Use CliWrap instead")] -public readonly struct RemoteCommand -{ - public SshHost Host { get; } - public SysPath Path { get; } - public String Args { get; } - - public RemoteCommand(RemotePath remotePath, String args = "") - { - var (host, path) = remotePath; - - Args = args; - Host = host; - Path = path; - } - - public RemoteCommand(SshHost host, SysPath path, String args = "") - { - Args = args; - Host = host; - Path = path; - } - - public static RemoteCommand FromRemotePath(RemotePath remotePath) => new RemoteCommand(remotePath); - - - // TODO - public RemoteCommand Opt1(String option) - { - return new RemoteCommand(Host, Path, $"{Args} -{option}"); - } - - public RemoteCommand Opt1(String option, Object value, String separator = " ") - { - return new RemoteCommand(Host, Path, $"{Args} -{option}{separator}{Quote(value)}"); - } - - public RemoteCommand Opt2(String option) - { - return new RemoteCommand(Host, Path, $"{Args} --{option}"); - } - - public RemoteCommand Opt2(String option, Object value, String separator = "=") - { - return new RemoteCommand(Host, Path, $"{Args} --{option}{separator}{Quote(value)}"); - } - - public RemoteCommand Opt(String option) - { - return option.Length == 1 - ? Opt1(option) - : Opt2(option); - } - - public RemoteCommand Opt(String option, Object value) - { - return option.Length == 1 - ? Opt1(option, value) - : Opt2(option, value); - } - - public RemoteCommand Arg(Object argument) - { - return new RemoteCommand(Host, Path, Args + " " + Quote(argument)); - } - - private static String Quote(Object argument) - { - var arg = argument.ToString(); - if (arg.Contains(" ")) - arg = arg.Quote(); // TODO: single quote? - - return arg; - } - - - #region equality - - public Boolean Equals(RemoteCommand other) - { - return Host.Equals(other.Host) && Path.Equals(other.Path) && Args == other.Args; - } - - public override Boolean Equals(Object obj) - { - return obj is RemoteCommand other && Equals(other); - } - - public override Int32 GetHashCode() - { - unchecked - { - var hashCode = Host.GetHashCode(); - hashCode = (hashCode * 397) ^ Path.GetHashCode(); - hashCode = (hashCode * 397) ^ (Args != null ? Args.GetHashCode() : 0); - return hashCode; - } - } - - #endregion equality - - - public static implicit operator RemoteCommand(RemotePath remotePath) => new RemoteCommand(remotePath); - - public void Deconstruct(out SshHost host, out SysPath path, out String args) - { - host = Host; - path = Path; - args = Args; - } - - public override String ToString() => $"{Host}:{Path}{Args}"; -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Remote/RemoteFileIo.cs b/csharp/Lib/SysTools/Remote/RemoteFileIo.cs deleted file mode 100644 index e317d7227..000000000 --- a/csharp/Lib/SysTools/Remote/RemoteFileIo.cs +++ /dev/null @@ -1,299 +0,0 @@ -using InnovEnergy.Lib.SysTools.Edges; -using InnovEnergy.Lib.SysTools.Utils; - -namespace InnovEnergy.Lib.SysTools.Remote; - -[Obsolete("Needs rework before use")] -public static class RemoteFileIo -{ - - public static Boolean Exists(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "test" - .Opt("e") - .Arg(path) - .At(host) - .ExecuteBlocking() - .ExitCode == 0; - } - - // TODO: how to find out if ssh has failed or remote command? - - public static Boolean DirectoryExists(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "test" - .Opt("d") - .Arg(path) - .At(host) - .ExecuteBlocking() - .ExitCode == 0; - } - - public static Boolean FileExists(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "test" - .Opt("f") - .Arg(path) - .At(host) - .ExecuteBlocking() - .ExitCode == 0; - } - - public static Boolean DirectoryExists(this RemotePath remotePath, String directoryName) - { - return DirectoryExists(remotePath / directoryName); - } - - public static Boolean FileExists(this RemotePath remotePath, String fileName) - { - return DirectoryExists(remotePath / fileName); - } - - public static IEnumerable Directories(this RemotePath rp) - { - var (host, path) = rp; - - return "find" - .Arg(path) - .Opt1("mindepth", 1) - .Opt1("maxdepth", 1) - .Opt1("type", "d") - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut - .Select(p => new RemotePath(host, p)); - } - - public static IEnumerable Files(this RemotePath rp) - { - var (host, path) = rp; - - return "find" - .Arg(path) - .Opt1("mindepth", 1) - .Opt1("maxdepth", 1) - .Opt1("type", "f") - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut - .Select(p => new RemotePath(host, p)); - } - - public static RemotePath CreateDirectory(this RemotePath remotePath) - { - var (host, path) = remotePath; - - "mkdir".Arg(path) - .At(host) - .ExecuteBlocking() - .ThrowOnError(); - - return remotePath; - } - - public static RemotePath MoveFileTo(this RemotePath sourcePath, RemotePath targetPath) - { - throw new NotImplementedException(); - } - - public static RemotePath MoveDirectory(this RemotePath sourcePath, RemotePath targetPath) - { - throw new NotImplementedException(); - } - - public static RemotePath CreateDirectory(this RemotePath path, String dirName) - { - return CreateDirectory(path / dirName); - } - - public static RemotePath DeleteFile(this RemotePath remotePath) - { - var (host, path) = remotePath; // TODO: test file - - "rm".Arg(path) - .At(host) - .ExecuteBlocking() - .ThrowOnError(); - - return remotePath; - } - - - public static RemotePath DeleteFile(this RemotePath path, String fileName) - { - return DeleteFile(path / fileName); - } - - public static RemotePath DeleteDirectory(this RemotePath remotePath) - { - var (host, path) = remotePath; // TODO: test dir - - "rm".Opt("r") - .Arg(path) - .At(host) - .ExecuteBlocking() - .ThrowOnError(); - - return remotePath; - } - - public static RemotePath DeleteDirectory(this RemotePath path, String directoryName) - { - return DeleteDirectory(path / directoryName); - } - - public static RemotePath CopyDirectoryTo(this RemotePath sourceDir, RemotePath targetDir) - { - throw new NotImplementedException(); - } - - - public static RemotePath CopyFileTo(this RemotePath srcFile, RemotePath targetFile) - { - throw new NotImplementedException(); - } - - public static RemotePath CopyFileToDirectory(this RemotePath srcFile, RemotePath targetDir) - { - throw new NotImplementedException(); - } - - public static IEnumerable DescendantDirectories(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "find" - .Arg(path) - .Opt1("type", "d") - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut - .Select(d => new RemotePath(host, d)); - } - - public static IEnumerable DescendantFiles(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "find" - .Arg(path) - .Opt1("type", "f") - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut - .Select(d => new RemotePath(host, d)); - } - - public static IEnumerable Descendants(this RemotePath remotePath) - { - var (host, path) = remotePath; - return "find" - .Arg(path) - .Opt1("mindepth", 1) - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut - .Select(l => new RemotePath(host, l)); - } - - - public static IEnumerable ReadBytes(this RemotePath path) - { - throw new NotImplementedException(); - } - - - public static IReadOnlyList ReadLines(this RemotePath remotePath) - { - var (host, path) = remotePath; - - return "cat" - .Arg(path) - .At(host) - .ExecuteBlocking() - .ThrowOnError() - .StdOut; - } - - // public static IEnumerable ReadLines(this RemotePath path, Encoding encoding) - // { - // throw new NotImplementedException(); - // } - - //public static String ReadText(this RemotePath path) => path.ReadText(Encoding.UTF8); - - public static String ReadText(this RemotePath rp) - { - var lines = ReadLines(rp); - - if (lines.Count == 0) return ""; - if (lines.Count == 1) return lines[0]; - - return lines.Aggregate((a, b) => a.NewLine() + b); - } - - public static RemotePath WriteText(this RemotePath rp, String text) - { - throw new NotImplementedException(); - } - - - public static RemotePath AppendLines(this RemotePath filePath, params String[] lines) - { - return AppendLines(filePath, (IEnumerable) lines); - } - - public static RemotePath AppendLines(this RemotePath rp, IEnumerable lines) - { - return WriteLines(rp, lines, append: true); - } - - public static RemotePath WriteLines(this RemotePath filePath, params String[] lines) - { - return WriteLines(filePath, (IEnumerable) lines); - } - - public static RemotePath WriteLines(this RemotePath rp, IEnumerable lines) - { - return WriteLines(rp, lines, append: false); - } - - private static RemotePath WriteLines(this RemotePath rp, IEnumerable lines, Boolean append) - { - var (host, path) = rp; - - var proc = "cat" - .Arg(append ? ">>" : ">") - .Arg(path) - .At(host).ToAsyncProcess(); - - foreach (var line in lines) - proc.StandardIn.OnNext(line); - - proc.StandardIn.OnCompleted(); // TODO: OnCompleted or not? - - // TODO: error handling - - var exitCode = proc.WaitForExit(); - - return rp; - } - - public static RemotePath AppendText(this RemotePath filePath, String text) - { - throw new NotImplementedException(); - } - - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Remote/RemotePath.cs b/csharp/Lib/SysTools/Remote/RemotePath.cs deleted file mode 100644 index dcb75adb3..000000000 --- a/csharp/Lib/SysTools/Remote/RemotePath.cs +++ /dev/null @@ -1,99 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Remote; - -[Obsolete] -public readonly struct RemotePath -{ - public SysPath Path { get; } - public SshHost Host { get; } - - public RemotePath Parent => Path.Parent.At(Host); - public String Tail => Path.Tail; - public String Head => Path.Head; - public String Extension => Path.Extension; - - public RemotePath(SshHost host, SysPath path) - { - Host = host; - Path = path; - } - - public RemotePath At(SshHost host) - { - return new RemotePath(host, Path); - } - - public RemotePath ToRelative() => Path.ToRelative().At(Host); - public RemotePath ToAbsolute() => Path.ToAbsolute().At(Host); - public RemotePath ToEnvRef() => Path.ToEnvRef().At(Host); - - public RemotePath RelativeTo(RemotePath referencePath) - { - if (!Host.Equals(referencePath.Host)) - throw new ArgumentException(nameof(referencePath)); - - return Path.RelativeTo(referencePath.Path).At(Host); - } - - public RemotePath RelativeTo(SysPath referencePath) - { - return Path.RelativeTo(referencePath).At(Host); - } - - public Boolean IsSubPathOf(RemotePath referencePath) - { - var (host, path) = referencePath; - return Host.Equals(host) && Path.IsSubPathOf(path); - } - - public RemotePath Append(SysPath right) - { - return Path.Append(right).At(Host); - } - - #region equality - - public Boolean Equals(RemotePath other) - { - var (host, path) = other; - return Path.Equals(path) && Host.Equals(host); - } - - public override Boolean Equals(Object obj) - { - return obj is RemotePath other && Equals(other); - } - - public override Int32 GetHashCode() - { - unchecked - { - return (Path.GetHashCode() * 397) ^ Host.GetHashCode(); - } - } - - #endregion equality - - - - - - #region operators - - public static RemotePath operator /(RemotePath left, SysPath right) => left.Append(right); - public static RemotePath operator /(RemotePath left, String right) => left.Append(right); - - public static Boolean operator ==(RemotePath left, RemotePath right) => left.Equals(right); - public static Boolean operator !=(RemotePath left, RemotePath right) => left.Equals(right); - - public static implicit operator String(RemotePath remotePath) => remotePath.ToString(); - - #endregion - - public void Deconstruct(out SshHost host, out SysPath path) - { - path = Path; - host = Host; - } - - public override String ToString() => $"{Host}:{Path}"; -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Remote/SshHost.cs b/csharp/Lib/SysTools/Remote/SshHost.cs deleted file mode 100644 index a957f2887..000000000 --- a/csharp/Lib/SysTools/Remote/SshHost.cs +++ /dev/null @@ -1,103 +0,0 @@ -using InnovEnergy.Lib.SysTools.Edges; -using InnovEnergy.Lib.SysTools.Utils; - -namespace InnovEnergy.Lib.SysTools.Remote; - -[Obsolete("Needs rework before use")] -public readonly struct SshHost -{ - public const Int32 DefaultPort = 22; - - public String User { get; } - public String Address { get; } - public Int32 Port { get; } - public SysPath? KeyFile { get; } - - public SshHost(String user, String address, Int32 port = DefaultPort) - { - User = user; - Address = address; - KeyFile = null; - Port = port; - } - - public SshHost(String user, String address, SysPath? keyFile, Int32 port = DefaultPort) - { - User = user; - Address = address; - KeyFile = keyFile; - Port = port; - } - - public static SshHost Parse(String userAtAddressColonPort) - { - var addressPort = userAtAddressColonPort.AfterFirst('@').Split(':'); - - var user = userAtAddressColonPort.UntilFirst('@'); - var address = addressPort[0]; - var port = addressPort.Length < 2 ? DefaultPort : Int32.Parse(addressPort[1]); - - return new SshHost(user, address, null, port); - } - - public static SshHost Parse(String userAtAddressColonPort, SysPath keyFile) - { - var addressPort = userAtAddressColonPort.AfterFirst('@').Split(':'); - - var user = userAtAddressColonPort.UntilFirst('@'); - var address = addressPort[0]; - var port = addressPort.Length < 2 ? DefaultPort : Int32.Parse(addressPort[1]); - - return new SshHost(user, address, keyFile, port); - } - - - - public static SshHost Localhost { get; } = new SshHost(Environment.UserName, "127.0.0.1"); - - public Boolean IsLocalhost => Equals(Localhost); - - public override String ToString() => $"{User}@{Address}"; - - #region equality - - public Boolean Equals(SshHost other) - { - return User == other.User && - Address == other.Address && - Port == other.Port && - Nullable.Equals(KeyFile, other.KeyFile); - } - - public override Boolean Equals(Object obj) - { - return obj is SshHost other && Equals(other); - } - - public override Int32 GetHashCode() - { - unchecked - { - var hashCode = (User != null ? User.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (Address != null ? Address.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ Port; - hashCode = (hashCode * 397) ^ KeyFile.GetHashCode(); - return hashCode; - } - } - - #endregion - - public static RemotePath operator / (SshHost host, SysPath path) => host.WithPath(path); - public static RemotePath operator / (SshHost host, String path) - { - return host.WithPath(path.ToPath().ToAbsolute()); - } - - public void Deconstruct(out String user, out String address, out SysPath? keyFile) - { - user = User; - address = Address; - keyFile = KeyFile; - } -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/SysCommand.cs b/csharp/Lib/SysTools/SysCommand.cs deleted file mode 100644 index 50237a5d8..000000000 --- a/csharp/Lib/SysTools/SysCommand.cs +++ /dev/null @@ -1,104 +0,0 @@ -using InnovEnergy.Lib.SysTools.Utils; - -namespace InnovEnergy.Lib.SysTools; - -[Obsolete("Use CliWrap instead")] -public readonly struct SysCommand -{ - public SysPath Path { get; } - public String Args { get; } - - public SysCommand(SysPath path, String args = "") - { - Path = path; - Args = args; - } - - public static SysCommand FromPath(SysPath path) => new SysCommand(path); - public static SysCommand FromString(String cmd) => new SysCommand(cmd); - - public SysCommand Opt1(String option) - { - return new SysCommand(Path, $"{Args} -{option}"); - } - - public SysCommand Opt1(String option, Object value, String separator = " ") - { - return new SysCommand(Path, $"{Args} -{option}{separator}{QuoteArg(value)}"); - } - - public SysCommand Opt2(String option) - { - return new SysCommand(Path, $"{Args} --{option}"); - } - - public SysCommand Opt2(String option, Object value, String separator = "=") - { - return new SysCommand(Path, $"{Args} --{option}{separator}{QuoteArg(value)}"); - } - - public SysCommand Opt(String option) - { - return option.Length == 1 - ? Opt1(option) - : Opt2(option); - } - - public SysCommand Opt(String option, Object value) - { - return option.Length == 1 - ? Opt1(option, value) - : Opt2(option, value); - } - - public SysCommand Arg(Object argument) - { - return new SysCommand(Path, $"{Args} {QuoteArg(argument)}"); - } - - private static String QuoteArg(Object argument) - { - var arg = argument.ToString(); - if (arg.Contains(" ")) - arg = arg.Quote(); // TODO: single quote? - - return arg; - } - - public static implicit operator SysCommand(String cmd) => FromString(cmd); - public static implicit operator SysCommand(SysPath path) => FromPath(path); - - public static SysCommand operator %(SysCommand cmd, String argument) => cmd.Arg(argument); - public static SysCommand operator -(SysCommand cmd, String option ) => cmd.Opt(option); - public static SysCommand operator -(SysCommand cmd, (String opt, Object value) option ) => cmd.Opt(option.opt, option.value); - - #region equality - - public Boolean Equals(SysCommand other) - { - return Path.Equals(other.Path) && Args == other.Args; - } - - public override Boolean Equals(Object obj) - { - return obj is SysCommand other && Equals(other); - } - - public override Int32 GetHashCode() - { - unchecked - { - return (Path.GetHashCode() * 397) ^ (Args != null ? Args.GetHashCode() : 0); - } - } - - #endregion equality - - public void Deconstruct(out SysPath path, out String args) - { - path = Path; - args = Args; - } - - public override String ToString() => $"{Path}{Args}"; -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/SysDirs.cs b/csharp/Lib/SysTools/SysDirs.cs index c65d4e7b7..fd30895fa 100644 --- a/csharp/Lib/SysTools/SysDirs.cs +++ b/csharp/Lib/SysTools/SysDirs.cs @@ -6,22 +6,9 @@ namespace InnovEnergy.Lib.SysTools; [Obsolete] public static class SysDirs { - - // TODO conditional compilation - - /* - public static SysPath ProgramDirectory => ExecutingAssemblyDirectory.Parent.Head == "bin" && (ExecutingAssemblyDirectory.Head == "Debug" || ExecutingAssemblyDirectory.Head == "Release") - ? ExecutingAssemblyDirectory.Parent.Parent - : ExecutingAssemblyDirectory; - - public static SysPath ExecutingAssemblyDirectory => ExecutingAssembly.Parent; - public static SysPath ExecutingAssembly => Assembly.GetExecutingAssembly().Location.Apply(SysPath.FromUri); - */ - public static SysPath CurrentDirectory => Environment.CurrentDirectory; - public static SysPath TempDirectory => System.IO.Path.GetTempPath(); + public static SysPath CurrentDirectory => Environment.CurrentDirectory; + public static SysPath TempDirectory => Path.GetTempPath(); public static SysPath UserHomeDirectory { get; } = GetFolderPath(UserProfile); public static SysPath UserDesktopDirectory { get; } = GetFolderPath(DesktopDirectory); - - } \ No newline at end of file diff --git a/csharp/Lib/SysTools/SysPath.cs b/csharp/Lib/SysTools/SysPath.cs index b5ff40c36..1527ff378 100644 --- a/csharp/Lib/SysTools/SysPath.cs +++ b/csharp/Lib/SysTools/SysPath.cs @@ -1,11 +1,10 @@ using System.Text; -using InnovEnergy.Lib.SysTools.Remote; -using InnovEnergy.Lib.SysTools.Utils; +using InnovEnergy.Lib.Utils; using static System.IO.Path; namespace InnovEnergy.Lib.SysTools; -[Obsolete("Needs rework before use")] + public readonly struct SysPath { private readonly String _Path; @@ -28,9 +27,9 @@ public readonly struct SysPath throw new ArgumentException(nameof(path)); path = path - .Replace(@"\", "/") - .Trim() - .TrimEnd('/'); + .Replace(@"\", "/") + .Trim() + .TrimEnd('/'); if (path.Length == 0) _Path = "/"; // root else if (!path.Contains('/')) _Path = path; // env @@ -160,15 +159,15 @@ public readonly struct SysPath var path = _Path.Split(); var prefixSize = Enumerable - .Zip(refPath, path, (l, r) => l == r) - .TakeWhile(e => e) - .Count(); + .Zip(refPath, path, (l, r) => l == r) + .TakeWhile(e => e) + .Count(); var nUps = refPath.Length - prefixSize; var dirs = Enumerable - .Repeat("..", nUps) - .Concat(path.Skip(prefixSize)); + .Repeat("..", nUps) + .Concat(path.Skip(prefixSize)); var relative = String.Join(DirectorySeparatorChar.ToString(), dirs); @@ -197,13 +196,12 @@ public readonly struct SysPath return $"{_Path}/{right}"; } - public RemotePath At(SshHost host) => new RemotePath(host, this); #region overrides - public override Boolean Equals(Object obj) => _Path.Equals(obj?.ToString()); - public override Int32 GetHashCode() => _Path.GetHashCode(); - public override String ToString() => _Path; + public override Boolean Equals(Object? obj) => _Path.Equals(obj?.ToString()); + public override Int32 GetHashCode() => _Path.GetHashCode(); + public override String ToString() => _Path; #endregion diff --git a/csharp/Lib/SysTools/Utils/ConsoleUtils.cs b/csharp/Lib/SysTools/Utils/ConsoleUtils.cs deleted file mode 100644 index 5c58ef3b4..000000000 --- a/csharp/Lib/SysTools/Utils/ConsoleUtils.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Utils; - -internal static class ConsoleUtils -{ - public static T WriteLine(this T t, ConsoleColor color) - { - var c = Console.ForegroundColor; - - Console.ForegroundColor = color; - Console.WriteLine(t); - Console.ForegroundColor = c; - - return t; - } - - public static T WriteLine(this T t) - { - Console.WriteLine(t); - return t; - } - - public static T WriteLine(this T t, ConsoleColor color, params String[] more) - { - var c = Console.ForegroundColor; - - Console.ForegroundColor = color; - Console.WriteLine(t + " " + more.JoinWith(" ")); - Console.ForegroundColor = c; - - return t; - } - - public static T WriteLine(this T t, params String[] more) - { - Console.WriteLine(t + " " + more.JoinWith(" ")); - return t; - } - - - public static T Write(this T t, ConsoleColor color) - { - var c = Console.ForegroundColor; - - Console.ForegroundColor = color; - Console.Write(t); - Console.ForegroundColor = c; - - return t; - } - - public static T Write(this T t) - { - Console.Write(t); - return t; - } - - public static T Write(this T t, ConsoleColor color, params String[] more) - { - var c = Console.ForegroundColor; - - Console.ForegroundColor = color; - Console.Write(t + " " + more.JoinWith(" ")); - Console.ForegroundColor = c; - - return t; - } - - public static T Write(this T t, params String[] more) - { - Console.Write(t + " " + more.JoinWith(" ")); - return t; - } - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Utils/EnumerableUtils.cs b/csharp/Lib/SysTools/Utils/EnumerableUtils.cs deleted file mode 100644 index 9f02caacf..000000000 --- a/csharp/Lib/SysTools/Utils/EnumerableUtils.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Utils; - -internal static class EnumerableUtils -{ - - public static IEnumerable Pad(this IEnumerable src, Int32 length, T padding) - { - using var enumerator = src.GetEnumerator(); - while (enumerator.MoveNext() && length-- > 0) - yield return enumerator.Current; - - while (length-- > 0) - yield return padding; - } - - public static Dictionary> IndexColumn(this IEnumerable> src, UInt16 index) - { - var d = new Dictionary>(); - - foreach (var outer in src) - { - var inner = outer.ToList(); - var key = inner[index]; - d.Add(key, inner); - } - - return d; - } - - - public static IEnumerable<(TLeft left, TRight right)> Zip(IEnumerable left, - IEnumerable right) - { - using var l = left.GetEnumerator(); - using var r = right.GetEnumerator(); - while (l.MoveNext() && r.MoveNext()) - yield return (l.Current, r.Current); - } - - public static IEnumerator Enumerator(this T t) - { - yield return t; - } - - public static IEnumerable Enumerable(this T t) - { - yield return t; - } - - public static IEnumerable Flatten(this IEnumerable> src) => src.SelectMany(s => s); - - public static void ForEach(this IEnumerable enumerable, Action action) - { - foreach (var e in enumerable) - action(e); - } - - - - public static IEnumerable WhereNot(this IEnumerable enumerable, Func predicate) - { - return enumerable.Where(e => !predicate(e)); - } - - - public static IEnumerable EmptyIfNull(this IEnumerable enumerable) - { - return enumerable ?? new T[0]; - } - - // // https://stackoverflow.com/a/34006336/141397 - // public static Int32 CombineHashes(this ITuple tupled, Int32 seed = 1009, Int32 factor = 9176) - // { - // var hash = seed; - // - // for (var i = 0; i < tupled.Length; i++) - // { - // unchecked - // { - // hash = hash * factor + tupled[i].GetHashCode(); - // } - // } - // - // return hash; - // } - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Utils/StringUtils.cs b/csharp/Lib/SysTools/Utils/StringUtils.cs deleted file mode 100644 index c6a6ef12c..000000000 --- a/csharp/Lib/SysTools/Utils/StringUtils.cs +++ /dev/null @@ -1,208 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Utils; - -internal static class StringUtils -{ - - public static String NewLineBefore(this String s) - { - return Environment.NewLine + s; - } - - public static String AddTitle(this String s, String title) - { - return title.Underline() + s; - } - - public static String Underline(this String s) - { - return s.NewLine() + "".PadRight(s.Length, '-').NewLine(2); - } - - public static Boolean IsNullOrEmpty(this String s) - { - return String.IsNullOrEmpty(s); - } - - public static Boolean IsNullOrWhiteSpace(this String s) - { - return String.IsNullOrWhiteSpace(s); - } - - public static String Format(this Object value, String fmt) - { - return String.Format(fmt, value); - } - - public static String NewLine(this String s) - { - return s + Environment.NewLine; - } - - public static String NewLine(this String s, Int32 number) - { - return Enumerable - .Repeat(Environment.NewLine, number) - .Aggregate(s, (a, b) => a + b); - } - - public static String AppendSpaces(this String s, Int32 count = 4) - { - return s.PadRight(s.Length + count); - } - - public static String Append(this String s, String other) - { - return s + other; - } - - public static String Concat(this IEnumerable ss) - { - return String.Join("", ss); - } - - public static String SurroundWith(this String s, String before, String after = null) - { - return before + s + (after ?? before); - } - - - public static String ShellSingleQuote(this String s) - { - return $"'{s.Replace("'", @"'\''")}'"; - } - - public static String Quote(this String s) - { - return s.Replace(@"\", @"\\").Replace("\"", "\\\"").SurroundWith("\""); - } - - public static String Join(this IEnumerable strings) - { - return String.Join("", strings); - } - - public static String JoinWith(this IEnumerable strings, String separator) - { - return String.Join(separator, strings); - } - - public static String JoinToLines(this IEnumerable lines) - { - return String.Join(Environment.NewLine, lines); - } - - public static String JoinToLines(params String[] lines) - { - return String.Join(Environment.NewLine, lines); - } - - public static String Indent(this String text) - { - return " ".SideBySideWith(text, ""); - } - - public static String Indent(this String text, String indentor) - { - return indentor.SideBySideWith(text); - } - - public static IReadOnlyList SplitLines(this String s) - { - return s.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - } - - public static String SideBySideWith(this String left, String right, String separator = " ") - { - var leftLines = left .Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(); - var rightLines = right.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(); - - var leftWidth = leftLines.Count != 0 - ? leftLines.Max(l => l.Length) - : 0; - - var nLines = Math.Max(leftLines.Count, rightLines.Count); - - var leftPadded = leftLines .Pad(nLines, "").Select(l => l.PadRight(leftWidth)); - var rightPadded = rightLines.Pad(nLines, ""); - - return Enumerable - .Zip(leftPadded, rightPadded, (l, r) => String.Join(separator, l, r)) - .JoinToLines(); - } - - - public static String JoinWith(this IEnumerable args, String separator) - { - return String.Join(separator, args); - } - - public static IEnumerable GetLinesStartingWith(this IEnumerable lines, String prefix) - { - return lines.Where(l => l.StartsWith(prefix)); - } - - public static String GetLineStartingWith(this IEnumerable lines, String prefix) - { - return lines - .GetLinesStartingWith(prefix) - .Single(); - } - - - - public static ILookup ParseKeyValuesTextFile(this SysPath file) - { - var lines = file - .ReadLines() - .Select(l => l.Trim()) - .Where(l => l.Length > 0 && !l.StartsWith("#") && !l.StartsWith(";")); - - var keyValuePairs = from line in lines - let fields = line.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) - let key = fields[0] - from value in fields.Skip(1) - select (key, value); - - return keyValuePairs.ToLookup(e => e.key, e => e.value); - } - - public static Boolean EndsWith(this String str, Char c) - { - if (str.Length <= 0) - return false; - - return str[str.Length - 1] == c; - } - - public static String CommonPrefix(String str, params String[] more) - { - var prefixLength = str - .TakeWhile((c, i) => more.All(s => i < s.Length && s[i] == c)) - .Count(); - - return str.Substring(0, prefixLength); - } - - - public static String AfterFirst(this String str, Char separator) - { - return str.Substring(str.IndexOf(separator) + 1); - } - - public static String AfterLast(this String str, Char separator) - { - return str.Substring(str.LastIndexOf(separator) + 1); - } - - public static String UntilLast(this String str, Char separator) - { - return str.Substring(0, str.LastIndexOf(separator)); - } - - public static String UntilFirst(this String str, Char separator) - { - return str.Substring(0, str.IndexOf(separator)); - } - - -} \ No newline at end of file diff --git a/csharp/Lib/SysTools/Utils/Utils.cs b/csharp/Lib/SysTools/Utils/Utils.cs deleted file mode 100644 index 026c63e8c..000000000 --- a/csharp/Lib/SysTools/Utils/Utils.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace InnovEnergy.Lib.SysTools.Utils; - -public static class Utils -{ - - private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - public static DateTime FromUnixTime(UInt64 unixTime) - { - return Epoch.AddSeconds(unixTime); - } - - public static R ValueOrDefault(this Dictionary dict, T key) - { - return ValueOrDefault(dict, key, default); - } - - public static R ValueOrDefault(this Dictionary dict, T key, R defaultValue) - { - return dict.TryGetValue(key, out var value) ? value : defaultValue; - } - - public static void CopyFilesRecursively(String source, String target) - { - CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target)); - } - - public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) - { - foreach (var file in source.GetFiles()) - file.CopyTo(Path.Combine(target.FullName, file.Name)); - - foreach (var dir in source.GetDirectories()) - CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name)); - } - -} \ No newline at end of file diff --git a/csharp/Lib/Time/Unix/UnixTime.Converters.cs b/csharp/Lib/Time/Unix/UnixTime.Converters.cs index 0d5d17aeb..7c28860a9 100644 --- a/csharp/Lib/Time/Unix/UnixTime.Converters.cs +++ b/csharp/Lib/Time/Unix/UnixTime.Converters.cs @@ -3,4 +3,7 @@ namespace InnovEnergy.Lib.Time.Unix; public readonly partial struct UnixTime { public DateTime ToUtcDateTime() => DateTime.UnixEpoch + TimeSpan.FromSeconds(Ticks); + + public static implicit operator DateTime(UnixTime unixTimeSpan) => unixTimeSpan.ToUtcDateTime(); + public static implicit operator UnixTime(DateTime dateTime) => FromUtcDateTime(dateTime); } \ No newline at end of file diff --git a/csharp/Lib/Time/Unix/UnixTimeSpan.Converters.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.Converters.cs new file mode 100644 index 000000000..b2bce89d4 --- /dev/null +++ b/csharp/Lib/Time/Unix/UnixTimeSpan.Converters.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Time.Unix; + +public readonly partial struct UnixTimeSpan +{ + public TimeSpan ToTimeSpan() => TimeSpan.FromSeconds(Ticks); + + public static implicit operator TimeSpan(UnixTimeSpan unixTimeSpan) => unixTimeSpan.ToTimeSpan(); + public static implicit operator UnixTimeSpan(TimeSpan timeSpan) => FromTimeSpan(timeSpan); +} \ No newline at end of file diff --git a/csharp/Lib/Time/Unix/UnixTimeSpan.cs b/csharp/Lib/Time/Unix/UnixTimeSpan.cs index d525deaba..4dd19d675 100644 --- a/csharp/Lib/Time/Unix/UnixTimeSpan.cs +++ b/csharp/Lib/Time/Unix/UnixTimeSpan.cs @@ -4,8 +4,5 @@ public readonly partial struct UnixTimeSpan { public UInt32 Ticks { get; } - public TimeSpan ToTimeSpan() - { - return TimeSpan.FromSeconds(Ticks); - } + } \ No newline at end of file diff --git a/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs b/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs index fd98e3487..625253a88 100644 --- a/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs +++ b/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs @@ -1,6 +1,6 @@ namespace InnovEnergy.Lib.Time.Unix; -public static class UnixTimeDeltaExtensions +public static class UnixTimeSpanExtensions { public static UnixTimeSpan Seconds(this Int32 s) => UnixTimeSpan.FromSeconds(s); public static UnixTimeSpan Minutes(this Int32 m) => UnixTimeSpan.FromMinutes(m); diff --git a/csharp/Lib/Units/Angle.cs b/csharp/Lib/Units/Angle.cs index b8972c826..87a3ed2b4 100644 --- a/csharp/Lib/Units/Angle.cs +++ b/csharp/Lib/Units/Angle.cs @@ -4,7 +4,7 @@ using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Units; -[Sum] +[Generate] public readonly partial struct Angle { public static String Unit => "rad"; diff --git a/csharp/Lib/Units/Angle.generated.cs b/csharp/Lib/Units/Angle.generated.cs index 837f0b2a2..ccc8722f8 100644 --- a/csharp/Lib/Units/Angle.generated.cs +++ b/csharp/Lib/Units/Angle.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Angle; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(AngleConverter))] public readonly partial struct Angle { @@ -22,40 +24,12 @@ public readonly partial struct Angle public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value); public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar); - // parallel + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/ApparentPower.cs b/csharp/Lib/Units/ApparentPower.cs index 8c92385de..b2a5bba6c 100644 --- a/csharp/Lib/Units/ApparentPower.cs +++ b/csharp/Lib/Units/ApparentPower.cs @@ -2,7 +2,7 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -[Sum] +[Generate] public readonly partial struct ApparentPower { public static String Unit => "VA"; diff --git a/csharp/Lib/Units/ApparentPower.generated.cs b/csharp/Lib/Units/ApparentPower.generated.cs index a72053726..68d418277 100644 --- a/csharp/Lib/Units/ApparentPower.generated.cs +++ b/csharp/Lib/Units/ApparentPower.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = ApparentPower; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(ApparentPowerConverter))] public readonly partial struct ApparentPower { @@ -22,40 +24,12 @@ public readonly partial struct ApparentPower public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value); public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar); - // parallel + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Composite/Ac1Bus.cs b/csharp/Lib/Units/Composite/Ac1Bus.cs index f2c769f24..b968d72e9 100644 --- a/csharp/Lib/Units/Composite/Ac1Bus.cs +++ b/csharp/Lib/Units/Composite/Ac1Bus.cs @@ -6,20 +6,20 @@ 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 - }; - } + // [SuppressMessage("ReSharper", "RedundantCast")] + // public static Ac1Bus operator |(Ac1Bus left, Ac1Bus right) + // { + // var f = left.Frequency | right.Frequency; + // var p = (AcPhase)left | (AcPhase)right; + // + // return new Ac1Bus + // { + // Frequency = f, + // Current = p.Current, + // Voltage = p.Voltage, + // Phi = p.Phi + // }; + // } } diff --git a/csharp/Lib/Units/Composite/AcPhase.cs b/csharp/Lib/Units/Composite/AcPhase.cs index d590fbdee..8186e01f9 100644 --- a/csharp/Lib/Units/Composite/AcPhase.cs +++ b/csharp/Lib/Units/Composite/AcPhase.cs @@ -22,45 +22,45 @@ public record AcPhase : IBus public Angle Phi { get; init; } public ApparentPower ApparentPower => Voltage.Value * Current.Value ; - public Power ActivePower => ApparentPower.Value * PowerFactor.Value; + public Power ActivePower => ApparentPower.Value * PowerFactor; public ReactivePower ReactivePower => ApparentPower.Value * Sin(Phi); - public Number PowerFactor => Cos(Phi); + public Decimal PowerFactor => Cos(Phi); - public static AcPhase operator |(AcPhase left, AcPhase right) - { - // the Voltages of two phases are expected to be in phase and equal - - var v = left.Voltage | right.Voltage; - - // currents (RMS) can be different and out of phase - // https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/ - - // IF - // left(t) = ILeft sin(ωt) - // right(t) = IRight sin(ωt + φ). - // sum(t) = left(t) + right(t) = ISum sin(ωt + ψ). - - // THEN - // ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ). - // C = IRight * sin(φ) / sin(ψ). - - // in this calculation left(t) has zero phase shift. - // we can shift both waves by -left.Phi, so - // φ := right.phi - left.phi - - - var phi = right.Phi - left.Phi; - var phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi)); - var iSum = right.Current * Sin(phi) / Sin(phiSum); - - return new AcPhase - { - Voltage = v, - Current = iSum, - Phi = phiSum - }; - } + // public static AcPhase operator |(AcPhase left, AcPhase right) + // { + // // the Voltages of two phases are expected to be in phase and equal + // + // var v = left.Voltage | right.Voltage; + // + // // currents (RMS) can be different and out of phase + // // https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/ + // + // // IF + // // left(t) = ILeft sin(ωt) + // // right(t) = IRight sin(ωt + φ). + // // sum(t) = left(t) + right(t) = ISum sin(ωt + ψ). + // + // // THEN + // // ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ). + // // C = IRight * sin(φ) / sin(ψ). + // + // // in this calculation left(t) has zero phase shift. + // // we can shift both waves by -left.Phi, so + // // φ := right.phi - left.phi + // + // + // var phi = right.Phi - left.Phi; + // var phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi)); + // var iSum = right.Current * Sin(phi) / Sin(phiSum); + // + // return new AcPhase + // { + // Voltage = v, + // Current = iSum, + // Phi = phiSum + // }; + // } } \ No newline at end of file diff --git a/csharp/Lib/Units/Current.cs b/csharp/Lib/Units/Current.cs index b3c36ee34..07b473c24 100644 --- a/csharp/Lib/Units/Current.cs +++ b/csharp/Lib/Units/Current.cs @@ -2,8 +2,7 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; - -[Sum] +[Generate] public readonly partial struct Current { public static String Unit => "A"; diff --git a/csharp/Lib/Units/Current.generated.cs b/csharp/Lib/Units/Current.generated.cs index c06cb3ff0..b8b19963c 100644 --- a/csharp/Lib/Units/Current.generated.cs +++ b/csharp/Lib/Units/Current.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Current; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(CurrentConverter))] public readonly partial struct Current { @@ -22,40 +24,12 @@ public readonly partial struct Current public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value); public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar); - // parallel + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Energy.cs b/csharp/Lib/Units/Energy.cs index 006568b06..ab05ba6c4 100644 --- a/csharp/Lib/Units/Energy.cs +++ b/csharp/Lib/Units/Energy.cs @@ -1,10 +1,9 @@ using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units.Generator; - namespace InnovEnergy.Lib.Units; -[Sum] +[Generate] public readonly partial struct Energy { public static String Unit => "kWh"; diff --git a/csharp/Lib/Units/Energy.generated.cs b/csharp/Lib/Units/Energy.generated.cs index 90496238b..dabe6ed16 100644 --- a/csharp/Lib/Units/Energy.generated.cs +++ b/csharp/Lib/Units/Energy.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Energy; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(EnergyConverter))] public readonly partial struct Energy { @@ -22,40 +24,12 @@ public readonly partial struct Energy 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Frequency.cs b/csharp/Lib/Units/Frequency.cs index 3041325c2..f34b83334 100644 --- a/csharp/Lib/Units/Frequency.cs +++ b/csharp/Lib/Units/Frequency.cs @@ -2,7 +2,8 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -[Equal] + +[Generate] public readonly partial struct Frequency { public static String Unit => "Hz"; diff --git a/csharp/Lib/Units/Frequency.generated.cs b/csharp/Lib/Units/Frequency.generated.cs index 576e7b1a4..6bdfb8ded 100644 --- a/csharp/Lib/Units/Frequency.generated.cs +++ b/csharp/Lib/Units/Frequency.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Equal +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Frequency; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(FrequencyConverter))] public readonly partial struct Frequency { @@ -22,40 +24,12 @@ public readonly partial struct Frequency public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value); public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar); - // parallel + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Generator/EqualAttribute.cs b/csharp/Lib/Units/Generator/EqualAttribute.cs deleted file mode 100644 index c0131044a..000000000 --- a/csharp/Lib/Units/Generator/EqualAttribute.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace InnovEnergy.Lib.Units.Generator; - -[AttributeUsage(AttributeTargets.Struct)] -internal class EqualAttribute: Attribute -{} \ No newline at end of file diff --git a/csharp/Lib/Units/Generator/GenerateAttribute.cs b/csharp/Lib/Units/Generator/GenerateAttribute.cs new file mode 100644 index 000000000..12337cf50 --- /dev/null +++ b/csharp/Lib/Units/Generator/GenerateAttribute.cs @@ -0,0 +1,6 @@ +using System.Text.Json.Serialization; + +namespace InnovEnergy.Lib.Units.Generator; + +internal class GenerateAttribute : Attribute +{} \ No newline at end of file diff --git a/csharp/Lib/Units/Generator/MeanAttribute.cs b/csharp/Lib/Units/Generator/MeanAttribute.cs deleted file mode 100644 index 161da8b0e..000000000 --- a/csharp/Lib/Units/Generator/MeanAttribute.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace InnovEnergy.Lib.Units.Generator; - -[AttributeUsage(AttributeTargets.Struct)] -internal class MeanAttribute: Attribute -{} \ No newline at end of file diff --git a/csharp/Lib/Units/Generator/SumAttribute.cs b/csharp/Lib/Units/Generator/SumAttribute.cs deleted file mode 100644 index c4af9a156..000000000 --- a/csharp/Lib/Units/Generator/SumAttribute.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace InnovEnergy.Lib.Units.Generator; - -[AttributeUsage(AttributeTargets.Struct)] -internal class SumAttribute: Attribute -{} \ No newline at end of file diff --git a/csharp/Lib/Units/Generator/Template.txt b/csharp/Lib/Units/Generator/Template.txt index 38e30aa04..c23e0820a 100644 --- a/csharp/Lib/Units/Generator/Template.txt +++ b/csharp/Lib/Units/Generator/Template.txt @@ -5,11 +5,13 @@ using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Template; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(TemplateConverter))] public readonly partial struct Template { @@ -22,40 +24,12 @@ public readonly partial struct Template public static T operator *(T t, Decimal scalar) => new T(scalar * t.Value); public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar); - // parallel + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Generator/generate.sh b/csharp/Lib/Units/Generator/generate.sh index 74cc6a0c4..ec9f7abce 100755 --- a/csharp/Lib/Units/Generator/generate.sh +++ b/csharp/Lib/Units/Generator/generate.sh @@ -4,7 +4,7 @@ scriptDir=$( dirname -- "$0"; ) cd "$scriptDir/.." || exit -for match in $(grep -e '\[Sum\]\|\[Equal\]\|\[Mean\]' -o *.cs | tr -d '[]') +for match in $(grep -e '\[Generate\]' -o *.cs | tr -d '[]') do path="${match%:*}" type="${match#*:}" diff --git a/csharp/Lib/Units/Number.cs b/csharp/Lib/Units/Number.cs deleted file mode 100644 index fdc9b3b42..000000000 --- a/csharp/Lib/Units/Number.cs +++ /dev/null @@ -1,13 +0,0 @@ -using InnovEnergy.Lib.Units.Generator; - - -namespace InnovEnergy.Lib.Units; - -[Sum] -public readonly partial struct Number -{ - public static String Unit => ""; - public static String Symbol => ""; - - public Number(Decimal value) => Value = value; -} \ No newline at end of file diff --git a/csharp/Lib/Units/Number.generated.cs b/csharp/Lib/Units/Number.generated.cs deleted file mode 100644 index 4c6eb5693..000000000 --- a/csharp/Lib/Units/Number.generated.cs +++ /dev/null @@ -1,97 +0,0 @@ -#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 = Number; - -[JsonConverter(typeof(NumberConverter))] -public readonly partial struct Number -{ - 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 NumberConverter : JsonConverter -{ - public override Number Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new Number(reader.GetDecimal()); - } - - public override void Write(Utf8JsonWriter writer, Number value, JsonSerializerOptions options) - { - var rounded = value.Value.RoundToSignificantDigits(Units.JsonSignificantDigits); - - writer.WriteNumberValue(rounded); - } -} \ No newline at end of file diff --git a/csharp/Lib/Units/Percent.cs b/csharp/Lib/Units/Percent.cs index 577dec8a0..e20bc0c22 100644 --- a/csharp/Lib/Units/Percent.cs +++ b/csharp/Lib/Units/Percent.cs @@ -1,43 +1,12 @@ +using InnovEnergy.Lib.Units.Generator; + namespace InnovEnergy.Lib.Units; -using T = Percent; - -public readonly struct Percent +[Generate] +public readonly partial struct Percent { public static String Unit => "%"; public static String Symbol => "%"; // ?? public Percent(Decimal value) => Value = value; - - // not generated - // TODO: generate? - - public Decimal Value { get; } - public override String ToString() => Value + Unit; - - // scalar multiplication - public static Decimal operator *(Decimal scalar, T t) => scalar * t.Value / 100m; - public static Decimal operator *(T t, Decimal scalar) => scalar * t.Value / 100m; - - // parallel - public static Percent operator |(T left, T right) => new T((left.Value + right.Value) / 2m); - - // 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(); } \ No newline at end of file diff --git a/csharp/Lib/Units/Power.cs b/csharp/Lib/Units/Power.cs index de4a7f9ab..5eb088024 100644 --- a/csharp/Lib/Units/Power.cs +++ b/csharp/Lib/Units/Power.cs @@ -1,10 +1,9 @@ - using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -[Sum] +[Generate] public readonly partial struct Power { public static String Unit => "W"; diff --git a/csharp/Lib/Units/Power.generated.cs b/csharp/Lib/Units/Power.generated.cs index 1e68ec3a8..750318c6a 100644 --- a/csharp/Lib/Units/Power.generated.cs +++ b/csharp/Lib/Units/Power.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Power; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(PowerConverter))] public readonly partial struct Power { @@ -22,40 +24,12 @@ public readonly partial struct Power 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/ReactivePower.cs b/csharp/Lib/Units/ReactivePower.cs index 30d7641b9..a7017cb32 100644 --- a/csharp/Lib/Units/ReactivePower.cs +++ b/csharp/Lib/Units/ReactivePower.cs @@ -3,7 +3,7 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -[Sum] +[Generate] public readonly partial struct ReactivePower { public static String Unit => "var"; diff --git a/csharp/Lib/Units/ReactivePower.generated.cs b/csharp/Lib/Units/ReactivePower.generated.cs index f6b694b7a..ec3be808a 100644 --- a/csharp/Lib/Units/ReactivePower.generated.cs +++ b/csharp/Lib/Units/ReactivePower.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = ReactivePower; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(ReactivePowerConverter))] public readonly partial struct ReactivePower { @@ -22,40 +24,12 @@ public readonly partial struct ReactivePower 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Resistance.cs b/csharp/Lib/Units/Resistance.cs index ab0bc7bbe..cd12e11a5 100644 --- a/csharp/Lib/Units/Resistance.cs +++ b/csharp/Lib/Units/Resistance.cs @@ -3,9 +3,8 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -// TODO: op parallel is wrong -[Sum] +[Generate] public readonly partial struct Resistance { public static String Unit => "Ω"; diff --git a/csharp/Lib/Units/Resistance.generated.cs b/csharp/Lib/Units/Resistance.generated.cs index 6918e60dd..611c6a8fa 100644 --- a/csharp/Lib/Units/Resistance.generated.cs +++ b/csharp/Lib/Units/Resistance.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Sum +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Resistance; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(ResistanceConverter))] public readonly partial struct Resistance { @@ -22,40 +24,12 @@ public readonly partial struct Resistance 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/State.cs b/csharp/Lib/Units/State.cs deleted file mode 100644 index 88610517c..000000000 --- a/csharp/Lib/Units/State.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections; - -namespace InnovEnergy.Lib.Units; - -public readonly struct State : IReadOnlyList -{ - public IReadOnlyList Values { get; } - - public State(IReadOnlyList values) - { - if (values.Any(v => v.Contains(";"))) - throw new ArgumentException("State values cannot contain the character ;", nameof(values)); - - Values = values; - } - - public State(params String[] values) : this((IReadOnlyList)values){} - public State(params State[] states) : this((IReadOnlyList)states.SelectMany(s => s.Values).ToList()){} - - public static implicit operator State(String s) => new(s); - public static implicit operator State(Enum e) => new(e.ToString()); - public static implicit operator State(Boolean s) => new(s.ToString()); - public static implicit operator State(List s) => new((IReadOnlyList)s); - public static implicit operator State(String[] s) => new((IReadOnlyList)s); - public static implicit operator State(List es) => new(es.Select(e => e.ToString()).ToList()); - public static implicit operator State(Enum[] es) => new(es.Select(e => e.ToString()).ToList()); - - public static State operator |(State left, State right) => new(left, right); - - public IEnumerator GetEnumerator() => Values.GetEnumerator(); - - public override String ToString() => String.Join("; ", Values); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public Int32 Count => Values.Count; - public String this[Int32 index] => Values[index]; - - public static State From(T t) where T : Enum - { - return new State(t); - } -} - - diff --git a/csharp/Lib/Units/StateOfT.cs b/csharp/Lib/Units/StateOfT.cs deleted file mode 100644 index 4a1242dc2..000000000 --- a/csharp/Lib/Units/StateOfT.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections; - -namespace InnovEnergy.Lib.Units; - -public readonly struct State : IReadOnlyList where T:Enum -{ - public IReadOnlyList Values { get; } - - public State(IReadOnlyList values) => Values = values; - - public State(params T[] values) : this((IReadOnlyList)values){} - public State(params State[] states) : this((IReadOnlyList)states.SelectMany(s => s.Values).ToList()){} - - public static implicit operator State(T s) => new((IReadOnlyList)s); - public static implicit operator State(List s) => new((IReadOnlyList)s); - public static implicit operator State(T[] s) => new((IReadOnlyList)s); - - public static State operator |(State left, State right) => new State(left, right); - - public IEnumerator GetEnumerator() => Values.GetEnumerator(); - - public override String ToString() => String.Join("; ", Values); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public Int32 Count => Values.Count; - public T this[Int32 index] => Values[index]; -} \ No newline at end of file diff --git a/csharp/Lib/Units/Temperature.cs b/csharp/Lib/Units/Temperature.cs index 57e2406a3..24f91a8a8 100644 --- a/csharp/Lib/Units/Temperature.cs +++ b/csharp/Lib/Units/Temperature.cs @@ -2,14 +2,14 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -using T=Temperature; +using T = Temperature; -[Mean] + +[Generate] public readonly partial struct Temperature { public static String Unit => "°C"; public static String Symbol => "T"; public Temperature(Decimal value) => Value = value; - } \ No newline at end of file diff --git a/csharp/Lib/Units/Temperature.generated.cs b/csharp/Lib/Units/Temperature.generated.cs index c182f6737..0e993ec15 100644 --- a/csharp/Lib/Units/Temperature.generated.cs +++ b/csharp/Lib/Units/Temperature.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Mean +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Temperature; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(TemperatureConverter))] public readonly partial struct Temperature { @@ -22,40 +24,12 @@ public readonly partial struct Temperature 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Units/Units.cs b/csharp/Lib/Units/Units.cs index 0ee78882b..3694c0783 100644 --- a/csharp/Lib/Units/Units.cs +++ b/csharp/Lib/Units/Units.cs @@ -21,18 +21,14 @@ public static class Units public static Byte DisplaySignificantDigits { get; set; } = 3; public static Byte JsonSignificantDigits { get; set; } = 3; - - public const Decimal MaxRelativeError = 0.05m; // 5% - - public static Current A (this Decimal value) => new Current(value); - public static Voltage V (this Decimal value) => new Voltage(value); - public static Power W (this Decimal value) => new Power(value); - public static ReactivePower Var (this Decimal value) => new ReactivePower(value); - public static ApparentPower Va (this Decimal value) => new ApparentPower(value); - public static Resistance Ohm (this Decimal value) => new Resistance(value); - public static Frequency Hz (this Decimal value) => new Frequency(value); - public static Angle Rad (this Decimal value) => new Angle(value); - public static Temperature Celsius(this Decimal value) => new Temperature(value); - - + public static Current A (this Decimal value) => value; + public static Voltage V (this Decimal value) => value; + public static Power W (this Decimal value) => value; + public static ReactivePower Var (this Decimal value) => value; + public static ApparentPower Va (this Decimal value) => value; + public static Resistance Ohm (this Decimal value) => value; + public static Frequency Hz (this Decimal value) => value; + public static Angle Rad (this Decimal value) => value; + public static Temperature Celsius(this Decimal value) => value; + public static Energy KWh (this Decimal value) => value; } \ No newline at end of file diff --git a/csharp/Lib/Units/Voltage.cs b/csharp/Lib/Units/Voltage.cs index 9d9df166c..5849a2e56 100644 --- a/csharp/Lib/Units/Voltage.cs +++ b/csharp/Lib/Units/Voltage.cs @@ -2,7 +2,8 @@ using InnovEnergy.Lib.Units.Generator; namespace InnovEnergy.Lib.Units; -[Equal] + +[Generate] public readonly partial struct Voltage { public static String Unit => "V"; diff --git a/csharp/Lib/Units/Voltage.generated.cs b/csharp/Lib/Units/Voltage.generated.cs index 4f5c57f91..42d02b6fc 100644 --- a/csharp/Lib/Units/Voltage.generated.cs +++ b/csharp/Lib/Units/Voltage.generated.cs @@ -1,15 +1,17 @@ #nullable enable // Auto-generated code requires an explicit '#nullable' directive in source. -#define Equal +#define Generate using static System.Math; using System.Text.Json; using System.Text.Json.Serialization; using InnovEnergy.Lib.Utils; +using System.CodeDom.Compiler; namespace InnovEnergy.Lib.Units; using T = Voltage; +[GeneratedCode("generate.sh", "1")] [JsonConverter(typeof(VoltageConverter))] public readonly partial struct Voltage { @@ -22,40 +24,12 @@ public readonly partial struct Voltage 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 + // addition - #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 t) => new T(-t.Value); - public static T operator |(T left, T right) => new T(left.Value + right.Value); - public static T operator -(T t) => new T(-t.Value); - - #elif Mean - - public static T operator |(T left, T right) => new T((left.Value + right.Value)/2m); - - #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; diff --git a/csharp/Lib/Utils/StringUtils.cs b/csharp/Lib/Utils/StringUtils.cs index e0ee21746..46372e381 100644 --- a/csharp/Lib/Utils/StringUtils.cs +++ b/csharp/Lib/Utils/StringUtils.cs @@ -352,6 +352,34 @@ public static class StringUtils } + public static String TrimStart(this String target, String trimString) + { + if (String.IsNullOrEmpty(trimString)) return target; + + var result = target; + while (result.StartsWith(trimString)) + result = result[trimString.Length..]; + + return result; + } + + + public static String TrimEnd(this String target, String trimString) + { + if (String.IsNullOrEmpty(trimString)) + return target; + + var result = target; + while (result.EndsWith(trimString)) + result = result[..^trimString.Length]; + + return result; + } + + public static String EnsureStartsWith(this String target, String prefix) + { + return $"{prefix}{target.TrimStart(prefix)}"; + } } \ No newline at end of file