Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
bc8abef33e
|
@ -22,21 +22,16 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Resources" />
|
<Folder Include="Resources" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="Resources\s3cmd.py">
|
<None Update="Resources/s3cmd.py">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class Controller : ControllerBase
|
||||||
[HttpPost(nameof(Login))]
|
[HttpPost(nameof(Login))]
|
||||||
public ActionResult<Session> Login(String username, String password)
|
public ActionResult<Session> Login(String username, String password)
|
||||||
{
|
{
|
||||||
var user = Db.GetUserByEmail(username);
|
var user = Db.GetUserByName(username);
|
||||||
|
|
||||||
if (user is null || !user.VerifyPassword(password))
|
if (user is null || !user.VerifyPassword(password))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
@ -162,14 +162,19 @@ public class Controller : ControllerBase
|
||||||
|
|
||||||
|
|
||||||
[HttpGet(nameof(GetAllFoldersAndInstallations))]
|
[HttpGet(nameof(GetAllFoldersAndInstallations))]
|
||||||
public ActionResult<IEnumerable<TreeNode>> GetAllFoldersAndInstallations(Token authToken)
|
public ActionResult<IEnumerable<Object>> GetAllFoldersAndInstallations(Token authToken)
|
||||||
{
|
{
|
||||||
var user = Db.GetSession(authToken)?.User;
|
var user = Db.GetSession(authToken)?.User;
|
||||||
|
|
||||||
if (user is null)
|
if (user is null)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
|
var foldersAndInstallations = user
|
||||||
|
.AccessibleFoldersAndInstallations()
|
||||||
|
.OfType<Object>(); // Important! JSON serializer must see Objects otherwise
|
||||||
|
// it will just serialize the members of TreeNode %&@#!!!
|
||||||
|
|
||||||
return new (user.AccessibleFoldersAndInstallations());
|
return new (foldersAndInstallations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,36 +68,17 @@ public static class FolderMethods
|
||||||
|
|
||||||
public static Folder? Parent(this Folder folder)
|
public static Folder? Parent(this Folder folder)
|
||||||
{
|
{
|
||||||
return IsAbsoluteRoot(folder)
|
return IsRoot(folder)
|
||||||
? null
|
? null
|
||||||
: Db.GetFolderById(folder.ParentId);
|
: 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,21 +76,20 @@ public static class InstallationMethods
|
||||||
|
|
||||||
public static Folder? Parent(this Installation installation)
|
public static Folder? Parent(this Installation installation)
|
||||||
{
|
{
|
||||||
return installation.IsRelativeRoot()
|
if (installation.ParentId <= 0) // relative root
|
||||||
? null
|
{
|
||||||
: Db.GetFolderById(installation.ParentId);
|
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)
|
public static Boolean WasMoved(this Installation installation)
|
||||||
{
|
{
|
||||||
if (installation.IsRelativeRoot())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var existingInstallation = Db.GetInstallationById(installation.Id);
|
var existingInstallation = Db.GetInstallationById(installation.Id);
|
||||||
|
|
||||||
return existingInstallation is not null
|
return existingInstallation is not null
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
@ -13,20 +14,23 @@ public static class SessionMethods
|
||||||
&& folder is not null
|
&& folder is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessTo(folder.Parent())
|
&& 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 });
|
&& Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Update(this Session? session, Folder? folder)
|
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
|
return user is not null
|
||||||
&& folder is not null
|
&& folder is not null
|
||||||
|
&& original is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessTo(folder)
|
&& user.HasAccessTo(folder)
|
||||||
&& (folder.IsRelativeRoot() || user.HasAccessTo(folder.Parent()))
|
&& folder
|
||||||
&& Db.Update(folder);
|
.WithParentOf(original) // prevent moving
|
||||||
|
.Apply(Db.Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Delete(this Session? session, Folder? folder)
|
public static Boolean Delete(this Session? session, Folder? folder)
|
||||||
|
@ -49,24 +53,27 @@ public static class SessionMethods
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessTo(installation.Parent())
|
&& 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 })
|
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id })
|
||||||
&& await installation.CreateBucket()
|
&& await installation.CreateBucket()
|
||||||
&& await installation.RenewS3BucketUrl(); // generation of access _after_ generation of
|
&& 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)
|
public static Boolean Update(this Session? session, Installation? installation)
|
||||||
{
|
{
|
||||||
var user = session?.User;
|
var user = session?.User;
|
||||||
|
|
||||||
|
var original = Db.GetInstallationById(installation?.Id);
|
||||||
|
|
||||||
return user is not null
|
return user is not null
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
|
&& original is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& installation.Exists()
|
|
||||||
&& user.HasAccessTo(installation)
|
&& user.HasAccessTo(installation)
|
||||||
&& (installation.IsRelativeRoot() || user.HasAccessTo(installation.Parent())) // TODO: triple check this
|
&& installation
|
||||||
&& Db.Update(installation);
|
.WithParentOf(original) // prevent moving
|
||||||
|
.Apply(Db.Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Delete(this Session? session, Installation? installation)
|
public static Boolean Delete(this Session? session, Installation? installation)
|
||||||
|
@ -77,37 +84,37 @@ public static class SessionMethods
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessTo(installation)
|
&& user.HasAccessTo(installation)
|
||||||
// && installation.DeleteBucket().Result // TODO: await?
|
// && installation.DeleteBucket().Result // TODO
|
||||||
&& Db.Delete(installation);
|
&& Db.Delete(installation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Create(this Session? session, User? newUser)
|
public static Boolean Create(this Session? session, User? newUser)
|
||||||
{
|
{
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
if (sessionUser is null || newUser is null || !sessionUser.HasWriteAccess)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
newUser.ParentId = sessionUser.Id; // Important!
|
return sessionUser is not null
|
||||||
|
&& newUser is not null
|
||||||
return Db.Create(newUser);
|
&& sessionUser.HasWriteAccess
|
||||||
|
&& newUser
|
||||||
|
.WithParent(sessionUser)
|
||||||
|
.Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password))
|
||||||
|
.Apply(Db.Create);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Update(this Session? session, User? editedUser)
|
public static Boolean Update(this Session? session, User? editedUser)
|
||||||
{
|
{
|
||||||
var sessionUser = session?.User;
|
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
|
return editedUser is not null
|
||||||
// Password change is only allowed for oneself
|
&& sessionUser is not null
|
||||||
editedUser.Password = editedUser.Id != sessionUser.Id
|
&& originalUser is not null
|
||||||
? sessionUser.Password
|
&& sessionUser.HasWriteAccess
|
||||||
: sessionUser.SaltAndHashPassword(editedUser.Password);
|
&& sessionUser.HasAccessTo(editedUser)
|
||||||
|
&& editedUser
|
||||||
return sessionUser.HasWriteAccess
|
.WithParentOf(originalUser) // prevent moving
|
||||||
&& sessionUser.HasAccessTo(editedUser)
|
.WithPasswordOf(originalUser)
|
||||||
&& (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent()) || editedUser.Id == sessionUser.Id) // TODO: triple check this
|
.Apply(Db.Update);
|
||||||
&& Db.Update(editedUser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Delete(this Session? session, User? userToDelete)
|
public static Boolean Delete(this Session? session, User? userToDelete)
|
||||||
|
@ -117,7 +124,7 @@ public static class SessionMethods
|
||||||
return sessionUser is not null
|
return sessionUser is not null
|
||||||
&& userToDelete is not null
|
&& userToDelete is not null
|
||||||
&& sessionUser.HasWriteAccess
|
&& sessionUser.HasWriteAccess
|
||||||
&& sessionUser.HasAccessTo(userToDelete) // TODO: && user.HasAccessTo(installation.Parent()) ???
|
&& sessionUser.HasAccessTo(userToDelete)
|
||||||
&& Db.Delete(userToDelete);
|
&& Db.Delete(userToDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +134,8 @@ public static class SessionMethods
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
return sessionUser is not null
|
return sessionUser is not null
|
||||||
&& user is not null
|
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
|
&& user is not null
|
||||||
&& user.IsDescendantOf(sessionUser)
|
&& user.IsDescendantOf(sessionUser)
|
||||||
&& sessionUser.HasAccessTo(installation)
|
&& sessionUser.HasAccessTo(installation)
|
||||||
&& !user.HasAccessTo(installation)
|
&& !user.HasAccessTo(installation)
|
||||||
|
@ -140,8 +147,8 @@ public static class SessionMethods
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
return sessionUser is not null
|
return sessionUser is not null
|
||||||
&& user is not null
|
|
||||||
&& folder is not null
|
&& folder is not null
|
||||||
|
&& user is not null
|
||||||
&& user.IsDescendantOf(sessionUser)
|
&& user.IsDescendantOf(sessionUser)
|
||||||
&& sessionUser.HasAccessTo(folder)
|
&& sessionUser.HasAccessTo(folder)
|
||||||
&& !user.HasAccessTo(folder)
|
&& !user.HasAccessTo(folder)
|
||||||
|
@ -153,8 +160,8 @@ public static class SessionMethods
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
return sessionUser is not null
|
return sessionUser is not null
|
||||||
&& user is not null
|
|
||||||
&& installation is not null
|
&& installation is not null
|
||||||
|
&& user is not null
|
||||||
&& user.IsDescendantOf(sessionUser)
|
&& user.IsDescendantOf(sessionUser)
|
||||||
&& sessionUser.HasAccessTo(installation)
|
&& sessionUser.HasAccessTo(installation)
|
||||||
&& user.HasAccessTo(installation)
|
&& user.HasAccessTo(installation)
|
||||||
|
@ -166,8 +173,8 @@ public static class SessionMethods
|
||||||
var sessionUser = session?.User;
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
return sessionUser is not null
|
return sessionUser is not null
|
||||||
&& user is not null
|
|
||||||
&& folder is not null
|
&& folder is not null
|
||||||
|
&& user is not null
|
||||||
&& user.IsDescendantOf(sessionUser)
|
&& user.IsDescendantOf(sessionUser)
|
||||||
&& sessionUser.HasAccessTo(folder)
|
&& sessionUser.HasAccessTo(folder)
|
||||||
&& user.HasAccessTo(folder)
|
&& user.HasAccessTo(folder)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class TreeNodeMethods
|
||||||
|
{
|
||||||
|
|
||||||
|
public static T WithParentOf<T>(this T treeNode, T other) where T: TreeNode
|
||||||
|
{
|
||||||
|
treeNode.ParentId = other.ParentId;
|
||||||
|
return treeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T WithParent<T>(this T treeNode, T other) where T: TreeNode
|
||||||
|
{
|
||||||
|
treeNode.ParentId = other.Id;
|
||||||
|
return treeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T HideParent<T>(this T treeNode) where T: TreeNode
|
||||||
|
{
|
||||||
|
treeNode.ParentId = 0;
|
||||||
|
return treeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ public static class UserMethods
|
||||||
// to a child folder of a folder he has already access to
|
// to a child folder of a folder he has already access to
|
||||||
// TODO shouldn't we prevent doubling permissions? -K"
|
// TODO shouldn't we prevent doubling permissions? -K"
|
||||||
// TODO yes we should -ig (still TODO)
|
// 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<TreeNode> AccessibleFoldersAndInstallations(this User user)
|
public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user)
|
||||||
|
@ -51,9 +51,9 @@ public static class UserMethods
|
||||||
.InstallationAccess
|
.InstallationAccess
|
||||||
.Where(r => r.UserId == user.Id)
|
.Where(r => r.UserId == user.Id)
|
||||||
.Select(r => r.InstallationId)
|
.Select(r => r.InstallationId)
|
||||||
.Select(Db.GetInstallationById)
|
.Select(i => Db.GetInstallationById(i))
|
||||||
.NotNull()
|
.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<Folder> DirectlyAccessibleFolders(this User user)
|
public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user)
|
||||||
|
@ -62,9 +62,9 @@ public static class UserMethods
|
||||||
.FolderAccess
|
.FolderAccess
|
||||||
.Where(r => r.UserId == user.Id)
|
.Where(r => r.UserId == user.Id)
|
||||||
.Select(r => r.FolderId)
|
.Select(r => r.FolderId)
|
||||||
.Select(Db.GetFolderById)
|
.Select(i => Db.GetFolderById(i))
|
||||||
.NotNull()
|
.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<User> ChildUsers(this User parent)
|
public static IEnumerable<User> ChildUsers(this User parent)
|
||||||
|
@ -112,21 +112,17 @@ public static class UserMethods
|
||||||
|
|
||||||
public static User? Parent(this User u)
|
public static User? Parent(this User u)
|
||||||
{
|
{
|
||||||
return u.IsAbsoluteRoot()
|
return u.IsRoot()
|
||||||
? null
|
? null
|
||||||
: Db.GetUserById(u.ParentId);
|
: Db.GetUserById(u.ParentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean IsAbsoluteRoot(this User u)
|
public static Boolean IsRoot(this User user)
|
||||||
{
|
{
|
||||||
return u.ParentId == 0;
|
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)
|
public static Boolean HasDirectAccessTo(this User user, Folder folder)
|
||||||
{
|
{
|
||||||
return Db
|
return Db
|
||||||
|
@ -174,12 +170,6 @@ public static class UserMethods
|
||||||
.Contains(user);
|
.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)
|
public static String Salt(this User user)
|
||||||
{
|
{
|
||||||
// + id => salt unique per user
|
// + id => salt unique per user
|
||||||
|
@ -188,6 +178,14 @@ public static class UserMethods
|
||||||
return $"{user.Id}InnovEnergy";
|
return $"{user.Id}InnovEnergy";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static User WithPasswordOf(this User user, User other)
|
||||||
|
{
|
||||||
|
user.Password = other.Password;
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO?
|
// TODO?
|
||||||
private static Boolean IsValidEmail(String email)
|
private static Boolean IsValidEmail(String email)
|
||||||
|
|
|
@ -5,9 +5,9 @@ namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
public abstract partial class TreeNode
|
public abstract partial class TreeNode
|
||||||
{
|
{
|
||||||
[PrimaryKey, AutoIncrement]
|
[PrimaryKey, AutoIncrement]
|
||||||
public Int64 Id { get; set; }
|
public Int64 Id { get; set; }
|
||||||
public String Name { get; set; } = "";
|
public virtual String Name { get; set; } = ""; // overridden by User (unique)
|
||||||
public String Information { get; set; } = ""; // unstructured random info
|
public String Information { get; set; } = ""; // unstructured random info
|
||||||
|
|
||||||
[Indexed] // parent/child relation
|
[Indexed] // parent/child relation
|
||||||
public Int64 ParentId { get; set; }
|
public Int64 ParentId { get; set; }
|
||||||
|
|
|
@ -4,11 +4,13 @@ namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
public class User : TreeNode
|
public class User : TreeNode
|
||||||
{
|
{
|
||||||
[Indexed]
|
|
||||||
public String Email { get; set; } = null!;
|
public String Email { get; set; } = null!;
|
||||||
public Boolean HasWriteAccess { get; set; } = false;
|
public Boolean HasWriteAccess { get; set; } = false;
|
||||||
public String Language { get; set; } = null!;
|
public String Language { get; set; } = null!;
|
||||||
public String Password { get; set; } = null!;
|
public String Password { get; set; } = null!;
|
||||||
|
|
||||||
|
[Unique]
|
||||||
|
public override String Name { get; set; } = null!;
|
||||||
|
|
||||||
// TODO: must reset pwd
|
// TODO: must reset pwd
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using InnovEnergy.App.Backend.DataTypes;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.DataTypes.Methods;
|
|
||||||
using InnovEnergy.App.Backend.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,11 +20,6 @@ public static partial class Db
|
||||||
|
|
||||||
public static Boolean Create(User user)
|
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;
|
return Connection.Insert(user) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using InnovEnergy.App.Backend.DataTypes;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.DataTypes.Methods;
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
@ -15,10 +16,10 @@ public static partial class Db
|
||||||
|
|
||||||
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
|
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
|
||||||
|
|
||||||
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
||||||
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
||||||
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
|
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
|
||||||
public static TableQuery<User> Users => Connection.Table<User>();
|
public static TableQuery<User> Users => Connection.Table<User>();
|
||||||
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
|
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
|
||||||
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
|
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
|
||||||
|
|
||||||
|
@ -38,9 +39,10 @@ public static partial class Db
|
||||||
});
|
});
|
||||||
|
|
||||||
Observable.Interval(TimeSpan.FromDays(0.5))
|
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)
|
.SelectMany(Cleanup)
|
||||||
.Subscribe(); // and then daily
|
.Subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@ namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
public static partial class Db
|
public static partial class Db
|
||||||
{
|
{
|
||||||
public static Folder? GetFolderById(Int64 id)
|
public static Folder? GetFolderById(Int64? id)
|
||||||
{
|
{
|
||||||
return Folders
|
return Folders
|
||||||
.FirstOrDefault(f => f.Id == id);
|
.FirstOrDefault(f => f.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Installation? GetInstallationById(Int64 id)
|
public static Installation? GetInstallationById(Int64? id)
|
||||||
{
|
{
|
||||||
return Installations
|
return Installations
|
||||||
.FirstOrDefault(i => i.Id == id);
|
.FirstOrDefault(i => i.Id == id);
|
||||||
|
@ -25,22 +25,10 @@ public static partial class Db
|
||||||
.FirstOrDefault(u => u.Id == id);
|
.FirstOrDefault(u => u.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private!!
|
public static User? GetUserByName(String userName)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
return Users
|
return Users
|
||||||
.FirstOrDefault(u => u.Email == email);
|
.FirstOrDefault(u => u.Name == userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Session? GetSession(String token)
|
public static Session? GetSession(String token)
|
||||||
|
@ -62,25 +50,4 @@ public static partial class Db
|
||||||
|
|
||||||
return session;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,4 @@
|
||||||
using InnovEnergy.App.Backend.DataTypes;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.DataTypes.Methods;
|
|
||||||
using InnovEnergy.App.Backend.Relations;
|
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
@ -10,54 +7,24 @@ public static partial class Db
|
||||||
{
|
{
|
||||||
public static Boolean Update(Folder folder)
|
public static Boolean Update(Folder folder)
|
||||||
{
|
{
|
||||||
if (folder.IsRelativeRoot()) // TODO: triple check
|
return Connection.Update(folder) > 0;
|
||||||
{
|
|
||||||
var original = GetFolderById(folder.Id);
|
|
||||||
if (original is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
folder.ParentId = original.ParentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Connection.InsertOrReplace(folder) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Boolean Update(Installation installation)
|
public static Boolean Update(Installation installation)
|
||||||
{
|
{
|
||||||
if (installation.IsRelativeRoot()) // TODO: triple check
|
return Connection.Update(installation) > 0;
|
||||||
{
|
|
||||||
var original = GetInstallationById(installation.Id);
|
|
||||||
if (original is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
installation.ParentId = original.ParentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Connection.InsertOrReplace(installation) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Boolean Update(User user)
|
public static Boolean Update(User user)
|
||||||
{
|
{
|
||||||
var originalUser = GetUserById(user.Id);
|
var originalUser = GetUserById(user.Id);
|
||||||
|
|
||||||
return originalUser is not null
|
return originalUser is not null
|
||||||
&& user.Id == originalUser.Id // these columns must not be modified!
|
&& user.ParentId == originalUser.ParentId // these columns must not be modified!
|
||||||
&& user.ParentId == originalUser.ParentId
|
&& user.Name == originalUser.Name
|
||||||
&& user.Email == originalUser.Email
|
&& Connection.Update(user) > 0;
|
||||||
&& Connection.InsertOrReplace(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is for convenient testing! Todo throw me out?
|
|
||||||
/// Operation filter to add the requirement of the custom header
|
|
||||||
/// </summary>
|
|
||||||
public class HeaderFilter : IOperationFilter
|
|
||||||
{
|
|
||||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
|
||||||
{
|
|
||||||
operation.Parameters ??= new List<OpenApiParameter>();
|
|
||||||
|
|
||||||
operation.Parameters.Add(new OpenApiParameter
|
|
||||||
{
|
|
||||||
Name = "auth",
|
|
||||||
In = ParameterLocation.Header,
|
|
||||||
Content = new Dictionary<String, OpenApiMediaType>(),
|
|
||||||
Required = false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,16 +4,15 @@ namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// TODO: Trash
|
||||||
|
|
||||||
public static void Main(String[] args)
|
public static void Main(String[] args)
|
||||||
{
|
{
|
||||||
//Db.CreateFakeRelations();
|
//Db.CreateFakeRelations();
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
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.AddControllers();
|
||||||
builder.Services.AddSwaggerGen(c =>
|
builder.Services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
|
@ -30,9 +29,8 @@ public static class Program
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
//app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ;
|
app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ;
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
//app.UseAuthorization();
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
|
@ -1,31 +1,15 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:63205",
|
|
||||||
"sslPort": 44319
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"Backend": {
|
"Backend": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
"launchUrl": "swagger",
|
||||||
"applicationUrl": "https://localhost:7087;http://localhost:5031",
|
"applicationUrl": "https://localhost:7087;http://localhost:5031",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.DataTypes;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Relations;
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
@ -13,8 +12,8 @@ public class Session : Relation<String, Int64>
|
||||||
[Indexed] public Int64 UserId { get => Right; init => Right = value;}
|
[Indexed] public Int64 UserId { get => Right; init => Right = value;}
|
||||||
[Indexed] public DateTime LastSeen { get; set; }
|
[Indexed] public DateTime LastSeen { get; set; }
|
||||||
|
|
||||||
[Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge
|
[Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge
|
||||||
&& !User.Email.IsNullOrEmpty();
|
&& (User) is not null;
|
||||||
|
|
||||||
[Ignore] public User User => _User ??= Db.GetUserById(UserId)!;
|
[Ignore] public User User => _User ??= Db.GetUserById(UserId)!;
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -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<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return command
|
|
||||||
.ToLocalCommand()
|
|
||||||
.ExecuteBlocking(variables, logToConsole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncProcess ToAsyncProcess(this RemoteCommand command, Dictionary<String, String> variables = null)
|
|
||||||
{
|
|
||||||
return command
|
|
||||||
.ToLocalCommand()
|
|
||||||
.ToAsyncProcess(variables);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SyncProcess ToSyncProcess(this RemoteCommand command, Dictionary<String, String> 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using InnovEnergy.Lib.SysTools.Process;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
|
||||||
|
|
||||||
using Env = Dictionary<String, String>;
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.SysTools.Edges;
|
|
||||||
|
|
||||||
public static class StringToSysPath
|
|
||||||
{
|
|
||||||
public static SysPath ToPath(this String path) => new SysPath(path);
|
|
||||||
}
|
|
|
@ -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<String, String> variables = null, Boolean logToConsole =false)
|
|
||||||
{
|
|
||||||
var process = command.ToAsyncProcess(variables, logToConsole);
|
|
||||||
|
|
||||||
var stdOut = new List<String>();
|
|
||||||
var stdErr = new List<String>();
|
|
||||||
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<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return new AsyncProcess(command, variables, logToConsole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SyncProcess ToSyncProcess(this SysCommand command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return new SyncProcess(command, variables, logToConsole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static System.Diagnostics.Process ToConsoleProcess(this SysCommand command, Dictionary<String, String> 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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return SysCommandToProcess.ExecuteBlocking(command, variables, logToConsole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AsyncProcess ToAsyncProcess(this SysPath command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return new AsyncProcess(command, variables, logToConsole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SyncProcess ToSyncProcess(this SysPath command, Dictionary<String, String> variables = null, Boolean logToConsole = false)
|
|
||||||
{
|
|
||||||
return new SyncProcess(command, variables, logToConsole);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.SysTools;
|
namespace InnovEnergy.Lib.SysTools;
|
||||||
|
|
||||||
[Obsolete("Needs rework before use")]
|
|
||||||
public static class FileIo
|
public static class FileIo
|
||||||
{
|
{
|
||||||
public static Boolean Exists (this SysPath path) => path.FileExists() || path.DirectoryExists();
|
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));
|
SysPath Target(SysPath path) => targetDir.Append(path.RelativeTo(sourceDir));
|
||||||
|
|
||||||
sourceDir.Traverse(Directories)
|
sourceDir.TraverseDepthFirstPreOrder(Directories)
|
||||||
.Do(d => Target(d).CreateDirectory())
|
.Do(d => Target(d).CreateDirectory())
|
||||||
.SelectMany(Files)
|
.SelectMany(Files)
|
||||||
.ForEach(f => f.CopyFileTo(Target(f)));
|
.ForEach(f => f.CopyFileTo(Target(f)));
|
||||||
|
@ -116,7 +115,7 @@ public static class FileIo
|
||||||
|
|
||||||
public static IEnumerable<SysPath> DescendantDirectories(this SysPath path)
|
public static IEnumerable<SysPath> DescendantDirectories(this SysPath path)
|
||||||
{
|
{
|
||||||
return path.Traverse(Directories);
|
return path.TraverseDepthFirstPreOrder(Directories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<SysPath> DescendantFiles(this SysPath path)
|
public static IEnumerable<SysPath> DescendantFiles(this SysPath path)
|
||||||
|
|
|
@ -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<String, String>;
|
|
||||||
|
|
||||||
[Obsolete("Use CliWrap instead")]
|
|
||||||
public class AsyncProcess
|
|
||||||
{
|
|
||||||
private readonly Subject<String> _StandardIn;
|
|
||||||
private readonly Subject<String> _StandardOut;
|
|
||||||
private readonly Subject<String> _StandardErr;
|
|
||||||
private readonly ReplaySubject<Int32> _ExitCode;
|
|
||||||
|
|
||||||
public SysCommand Command { get; }
|
|
||||||
public Env Environment { get; }
|
|
||||||
|
|
||||||
public IObserver<String> StandardIn => _StandardIn;
|
|
||||||
public IObservable<String> StandardOut => _StandardOut;
|
|
||||||
public IObservable<String> StandardErr => _StandardErr;
|
|
||||||
public IObservable<Int32> 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<String>();
|
|
||||||
_StandardOut = new Subject<String>();
|
|
||||||
_StandardErr = new Subject<String>();
|
|
||||||
_ExitCode = new ReplaySubject<Int32>();
|
|
||||||
|
|
||||||
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<Object>()
|
|
||||||
.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
|
|
||||||
}
|
|
|
@ -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<String> stdOut,
|
|
||||||
IReadOnlyList<String> stdErr,
|
|
||||||
String output)
|
|
||||||
{
|
|
||||||
ExitCode = exitCode;
|
|
||||||
StdOut = stdOut;
|
|
||||||
StdErr = stdErr;
|
|
||||||
Output = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Int32 ExitCode { get; }
|
|
||||||
public String Output { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<String> StdOut { get; }
|
|
||||||
public IReadOnlyList<String> 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<ProcessResult> action)
|
|
||||||
{
|
|
||||||
if (Failed) action(this);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProcessResult OnSuccess(Action<ProcessResult> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<String, String>;
|
|
||||||
|
|
||||||
[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<String, Boolean> predicate)
|
|
||||||
{
|
|
||||||
foreach (var l in ReadStdOut())
|
|
||||||
{
|
|
||||||
if (predicate(l))
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("no matching output");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<String> ReadStdOut(TimeSpan timeOut = default) => ReadMany(timeOut).ThrownOnStdErr();
|
|
||||||
|
|
||||||
public IEnumerable<SyncProcessOutput> 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<String> ThrownOnStdErr(this IEnumerable<SyncProcessOutput> 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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}";
|
|
||||||
}
|
|
|
@ -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<RemotePath> 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<RemotePath> 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<RemotePath> 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<RemotePath> 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<RemotePath> 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<Byte> ReadBytes(this RemotePath path)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static IReadOnlyList<String> ReadLines(this RemotePath remotePath)
|
|
||||||
{
|
|
||||||
var (host, path) = remotePath;
|
|
||||||
|
|
||||||
return "cat"
|
|
||||||
.Arg(path)
|
|
||||||
.At(host)
|
|
||||||
.ExecuteBlocking()
|
|
||||||
.ThrowOnError()
|
|
||||||
.StdOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static IEnumerable<String> 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<String>) lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RemotePath AppendLines(this RemotePath rp, IEnumerable<String> lines)
|
|
||||||
{
|
|
||||||
return WriteLines(rp, lines, append: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RemotePath WriteLines(this RemotePath filePath, params String[] lines)
|
|
||||||
{
|
|
||||||
return WriteLines(filePath, (IEnumerable<String>) lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RemotePath WriteLines(this RemotePath rp, IEnumerable<String> lines)
|
|
||||||
{
|
|
||||||
return WriteLines(rp, lines, append: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RemotePath WriteLines(this RemotePath rp, IEnumerable<String> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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}";
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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}";
|
|
||||||
}
|
|
|
@ -6,22 +6,9 @@ namespace InnovEnergy.Lib.SysTools;
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public static class SysDirs
|
public static class SysDirs
|
||||||
{
|
{
|
||||||
|
public static SysPath CurrentDirectory => Environment.CurrentDirectory;
|
||||||
// TODO conditional compilation
|
public static SysPath TempDirectory => Path.GetTempPath();
|
||||||
|
|
||||||
/*
|
|
||||||
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 UserHomeDirectory { get; } = GetFolderPath(UserProfile);
|
public static SysPath UserHomeDirectory { get; } = GetFolderPath(UserProfile);
|
||||||
public static SysPath UserDesktopDirectory { get; } = GetFolderPath(DesktopDirectory);
|
public static SysPath UserDesktopDirectory { get; } = GetFolderPath(DesktopDirectory);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using InnovEnergy.Lib.SysTools.Remote;
|
using InnovEnergy.Lib.Utils;
|
||||||
using InnovEnergy.Lib.SysTools.Utils;
|
|
||||||
using static System.IO.Path;
|
using static System.IO.Path;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.SysTools;
|
namespace InnovEnergy.Lib.SysTools;
|
||||||
|
|
||||||
[Obsolete("Needs rework before use")]
|
|
||||||
public readonly struct SysPath
|
public readonly struct SysPath
|
||||||
{
|
{
|
||||||
private readonly String _Path;
|
private readonly String _Path;
|
||||||
|
@ -28,9 +27,9 @@ public readonly struct SysPath
|
||||||
throw new ArgumentException(nameof(path));
|
throw new ArgumentException(nameof(path));
|
||||||
|
|
||||||
path = path
|
path = path
|
||||||
.Replace(@"\", "/")
|
.Replace(@"\", "/")
|
||||||
.Trim()
|
.Trim()
|
||||||
.TrimEnd('/');
|
.TrimEnd('/');
|
||||||
|
|
||||||
if (path.Length == 0) _Path = "/"; // root
|
if (path.Length == 0) _Path = "/"; // root
|
||||||
else if (!path.Contains('/')) _Path = path; // env
|
else if (!path.Contains('/')) _Path = path; // env
|
||||||
|
@ -160,15 +159,15 @@ public readonly struct SysPath
|
||||||
var path = _Path.Split();
|
var path = _Path.Split();
|
||||||
|
|
||||||
var prefixSize = Enumerable
|
var prefixSize = Enumerable
|
||||||
.Zip(refPath, path, (l, r) => l == r)
|
.Zip(refPath, path, (l, r) => l == r)
|
||||||
.TakeWhile(e => e)
|
.TakeWhile(e => e)
|
||||||
.Count();
|
.Count();
|
||||||
|
|
||||||
var nUps = refPath.Length - prefixSize;
|
var nUps = refPath.Length - prefixSize;
|
||||||
|
|
||||||
var dirs = Enumerable
|
var dirs = Enumerable
|
||||||
.Repeat("..", nUps)
|
.Repeat("..", nUps)
|
||||||
.Concat(path.Skip(prefixSize));
|
.Concat(path.Skip(prefixSize));
|
||||||
|
|
||||||
var relative = String.Join(DirectorySeparatorChar.ToString(), dirs);
|
var relative = String.Join(DirectorySeparatorChar.ToString(), dirs);
|
||||||
|
|
||||||
|
@ -197,13 +196,12 @@ public readonly struct SysPath
|
||||||
return $"{_Path}/{right}";
|
return $"{_Path}/{right}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemotePath At(SshHost host) => new RemotePath(host, this);
|
|
||||||
|
|
||||||
#region overrides
|
#region overrides
|
||||||
|
|
||||||
public override Boolean Equals(Object obj) => _Path.Equals(obj?.ToString());
|
public override Boolean Equals(Object? obj) => _Path.Equals(obj?.ToString());
|
||||||
public override Int32 GetHashCode() => _Path.GetHashCode();
|
public override Int32 GetHashCode() => _Path.GetHashCode();
|
||||||
public override String ToString() => _Path;
|
public override String ToString() => _Path;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.SysTools.Utils;
|
|
||||||
|
|
||||||
internal static class ConsoleUtils
|
|
||||||
{
|
|
||||||
public static T WriteLine<T>(this T t, ConsoleColor color)
|
|
||||||
{
|
|
||||||
var c = Console.ForegroundColor;
|
|
||||||
|
|
||||||
Console.ForegroundColor = color;
|
|
||||||
Console.WriteLine(t);
|
|
||||||
Console.ForegroundColor = c;
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T WriteLine<T>(this T t)
|
|
||||||
{
|
|
||||||
Console.WriteLine(t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T WriteLine<T>(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<T>(this T t, params String[] more)
|
|
||||||
{
|
|
||||||
Console.WriteLine(t + " " + more.JoinWith(" "));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static T Write<T>(this T t, ConsoleColor color)
|
|
||||||
{
|
|
||||||
var c = Console.ForegroundColor;
|
|
||||||
|
|
||||||
Console.ForegroundColor = color;
|
|
||||||
Console.Write(t);
|
|
||||||
Console.ForegroundColor = c;
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Write<T>(this T t)
|
|
||||||
{
|
|
||||||
Console.Write(t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Write<T>(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<T>(this T t, params String[] more)
|
|
||||||
{
|
|
||||||
Console.Write(t + " " + more.JoinWith(" "));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.SysTools.Utils;
|
|
||||||
|
|
||||||
internal static class EnumerableUtils
|
|
||||||
{
|
|
||||||
|
|
||||||
public static IEnumerable<T> Pad<T>(this IEnumerable<T> 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<T, IReadOnlyList<T>> IndexColumn<T>(this IEnumerable<IEnumerable<T>> src, UInt16 index)
|
|
||||||
{
|
|
||||||
var d = new Dictionary<T, IReadOnlyList<T>>();
|
|
||||||
|
|
||||||
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<TLeft, TRight>(IEnumerable<TLeft> left,
|
|
||||||
IEnumerable<TRight> 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<T> Enumerator<T>(this T t)
|
|
||||||
{
|
|
||||||
yield return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> Enumerable<T>(this T t)
|
|
||||||
{
|
|
||||||
yield return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> src) => src.SelectMany(s => s);
|
|
||||||
|
|
||||||
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
|
|
||||||
{
|
|
||||||
foreach (var e in enumerable)
|
|
||||||
action(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<T> WhereNot<T>(this IEnumerable<T> enumerable, Func<T,Boolean> predicate)
|
|
||||||
{
|
|
||||||
return enumerable.Where(e => !predicate(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> 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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<String> 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<String> strings)
|
|
||||||
{
|
|
||||||
return String.Join("", strings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String JoinWith(this IEnumerable<String> strings, String separator)
|
|
||||||
{
|
|
||||||
return String.Join(separator, strings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String JoinToLines(this IEnumerable<String> 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<String> 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<T>(this IEnumerable<T> args, String separator)
|
|
||||||
{
|
|
||||||
return String.Join(separator, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<String> GetLinesStartingWith(this IEnumerable<String> lines, String prefix)
|
|
||||||
{
|
|
||||||
return lines.Where(l => l.StartsWith(prefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String GetLineStartingWith(this IEnumerable<String> lines, String prefix)
|
|
||||||
{
|
|
||||||
return lines
|
|
||||||
.GetLinesStartingWith(prefix)
|
|
||||||
.Single();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static ILookup<String, String> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -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<T, R>(this Dictionary<T, R> dict, T key)
|
|
||||||
{
|
|
||||||
return ValueOrDefault(dict, key, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static R ValueOrDefault<T, R>(this Dictionary<T, R> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,4 +3,7 @@ namespace InnovEnergy.Lib.Time.Unix;
|
||||||
public readonly partial struct UnixTime
|
public readonly partial struct UnixTime
|
||||||
{
|
{
|
||||||
public DateTime ToUtcDateTime() => DateTime.UnixEpoch + TimeSpan.FromSeconds(Ticks);
|
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);
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -4,8 +4,5 @@ public readonly partial struct UnixTimeSpan
|
||||||
{
|
{
|
||||||
public UInt32 Ticks { get; }
|
public UInt32 Ticks { get; }
|
||||||
|
|
||||||
public TimeSpan ToTimeSpan()
|
|
||||||
{
|
|
||||||
return TimeSpan.FromSeconds(Ticks);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace InnovEnergy.Lib.Time.Unix;
|
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 Seconds(this Int32 s) => UnixTimeSpan.FromSeconds(s);
|
||||||
public static UnixTimeSpan Minutes(this Int32 m) => UnixTimeSpan.FromMinutes(m);
|
public static UnixTimeSpan Minutes(this Int32 m) => UnixTimeSpan.FromMinutes(m);
|
||||||
|
|
|
@ -4,7 +4,7 @@ using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct Angle
|
public readonly partial struct Angle
|
||||||
{
|
{
|
||||||
public static String Unit => "rad";
|
public static String Unit => "rad";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Angle;
|
using T = Angle;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(AngleConverter))]
|
[JsonConverter(typeof(AngleConverter))]
|
||||||
public readonly partial struct Angle
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -2,7 +2,7 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct ApparentPower
|
public readonly partial struct ApparentPower
|
||||||
{
|
{
|
||||||
public static String Unit => "VA";
|
public static String Unit => "VA";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = ApparentPower;
|
using T = ApparentPower;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(ApparentPowerConverter))]
|
[JsonConverter(typeof(ApparentPowerConverter))]
|
||||||
public readonly partial struct ApparentPower
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -6,20 +6,20 @@ public record Ac1Bus : AcPhase
|
||||||
{
|
{
|
||||||
public Frequency Frequency { get; init; }
|
public Frequency Frequency { get; init; }
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "RedundantCast")]
|
// [SuppressMessage("ReSharper", "RedundantCast")]
|
||||||
public static Ac1Bus operator |(Ac1Bus left, Ac1Bus right)
|
// public static Ac1Bus operator |(Ac1Bus left, Ac1Bus right)
|
||||||
{
|
// {
|
||||||
var f = left.Frequency | right.Frequency;
|
// var f = left.Frequency | right.Frequency;
|
||||||
var p = (AcPhase)left | (AcPhase)right;
|
// var p = (AcPhase)left | (AcPhase)right;
|
||||||
|
//
|
||||||
return new Ac1Bus
|
// return new Ac1Bus
|
||||||
{
|
// {
|
||||||
Frequency = f,
|
// Frequency = f,
|
||||||
Current = p.Current,
|
// Current = p.Current,
|
||||||
Voltage = p.Voltage,
|
// Voltage = p.Voltage,
|
||||||
Phi = p.Phi
|
// Phi = p.Phi
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,45 +22,45 @@ public record AcPhase : IBus
|
||||||
public Angle Phi { get; init; }
|
public Angle Phi { get; init; }
|
||||||
|
|
||||||
public ApparentPower ApparentPower => Voltage.Value * Current.Value ;
|
public ApparentPower ApparentPower => Voltage.Value * Current.Value ;
|
||||||
public Power ActivePower => ApparentPower.Value * PowerFactor.Value;
|
public Power ActivePower => ApparentPower.Value * PowerFactor;
|
||||||
public ReactivePower ReactivePower => ApparentPower.Value * Sin(Phi);
|
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)
|
// public static AcPhase operator |(AcPhase left, AcPhase right)
|
||||||
{
|
// {
|
||||||
// the Voltages of two phases are expected to be in phase and equal
|
// // the Voltages of two phases are expected to be in phase and equal
|
||||||
|
//
|
||||||
var v = left.Voltage | right.Voltage;
|
// var v = left.Voltage | right.Voltage;
|
||||||
|
//
|
||||||
// currents (RMS) can be different and out of phase
|
// // currents (RMS) can be different and out of phase
|
||||||
// https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/
|
// // https://www.johndcook.com/blog/2020/08/17/adding-phase-shifted-sine-waves/
|
||||||
|
//
|
||||||
// IF
|
// // IF
|
||||||
// left(t) = ILeft sin(ωt)
|
// // left(t) = ILeft sin(ωt)
|
||||||
// right(t) = IRight sin(ωt + φ).
|
// // right(t) = IRight sin(ωt + φ).
|
||||||
// sum(t) = left(t) + right(t) = ISum sin(ωt + ψ).
|
// // sum(t) = left(t) + right(t) = ISum sin(ωt + ψ).
|
||||||
|
//
|
||||||
// THEN
|
// // THEN
|
||||||
// ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ).
|
// // ψ = arctan( IRight * sin(φ) / (ILeft + IRight cos(φ)) ).
|
||||||
// C = IRight * sin(φ) / sin(ψ).
|
// // C = IRight * sin(φ) / sin(ψ).
|
||||||
|
//
|
||||||
// in this calculation left(t) has zero phase shift.
|
// // in this calculation left(t) has zero phase shift.
|
||||||
// we can shift both waves by -left.Phi, so
|
// // we can shift both waves by -left.Phi, so
|
||||||
// φ := right.phi - left.phi
|
// // φ := right.phi - left.phi
|
||||||
|
//
|
||||||
|
//
|
||||||
var phi = right.Phi - left.Phi;
|
// var phi = right.Phi - left.Phi;
|
||||||
var phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi));
|
// var phiSum = ATan2(right.Current * Sin(phi), left.Current + right.Current * Cos(phi));
|
||||||
var iSum = right.Current * Sin(phi) / Sin(phiSum);
|
// var iSum = right.Current * Sin(phi) / Sin(phiSum);
|
||||||
|
//
|
||||||
return new AcPhase
|
// return new AcPhase
|
||||||
{
|
// {
|
||||||
Voltage = v,
|
// Voltage = v,
|
||||||
Current = iSum,
|
// Current = iSum,
|
||||||
Phi = phiSum
|
// Phi = phiSum
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,8 +2,7 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
[Generate]
|
||||||
[Sum]
|
|
||||||
public readonly partial struct Current
|
public readonly partial struct Current
|
||||||
{
|
{
|
||||||
public static String Unit => "A";
|
public static String Unit => "A";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Current;
|
using T = Current;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(CurrentConverter))]
|
[JsonConverter(typeof(CurrentConverter))]
|
||||||
public readonly partial struct Current
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
using InnovEnergy.Lib.Time.Unix;
|
using InnovEnergy.Lib.Time.Unix;
|
||||||
using InnovEnergy.Lib.Units.Generator;
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct Energy
|
public readonly partial struct Energy
|
||||||
{
|
{
|
||||||
public static String Unit => "kWh";
|
public static String Unit => "kWh";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Energy;
|
using T = Energy;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(EnergyConverter))]
|
[JsonConverter(typeof(EnergyConverter))]
|
||||||
public readonly partial struct Energy
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -2,7 +2,8 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Equal]
|
|
||||||
|
[Generate]
|
||||||
public readonly partial struct Frequency
|
public readonly partial struct Frequency
|
||||||
{
|
{
|
||||||
public static String Unit => "Hz";
|
public static String Unit => "Hz";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Equal
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Frequency;
|
using T = Frequency;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(FrequencyConverter))]
|
[JsonConverter(typeof(FrequencyConverter))]
|
||||||
public readonly partial struct Frequency
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.Units.Generator;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Struct)]
|
|
||||||
internal class EqualAttribute: Attribute
|
|
||||||
{}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
|
internal class GenerateAttribute : Attribute
|
||||||
|
{}
|
|
@ -1,5 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.Units.Generator;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Struct)]
|
|
||||||
internal class MeanAttribute: Attribute
|
|
||||||
{}
|
|
|
@ -1,5 +0,0 @@
|
||||||
namespace InnovEnergy.Lib.Units.Generator;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Struct)]
|
|
||||||
internal class SumAttribute: Attribute
|
|
||||||
{}
|
|
|
@ -5,11 +5,13 @@ using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Template;
|
using T = Template;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(TemplateConverter))]
|
[JsonConverter(typeof(TemplateConverter))]
|
||||||
public readonly partial struct Template
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
scriptDir=$( dirname -- "$0"; )
|
scriptDir=$( dirname -- "$0"; )
|
||||||
cd "$scriptDir/.." || exit
|
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
|
do
|
||||||
path="${match%:*}"
|
path="${match%:*}"
|
||||||
type="${match#*:}"
|
type="${match#*:}"
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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<Number>
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +1,12 @@
|
||||||
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Percent;
|
[Generate]
|
||||||
|
public readonly partial struct Percent
|
||||||
public readonly struct Percent
|
|
||||||
{
|
{
|
||||||
public static String Unit => "%";
|
public static String Unit => "%";
|
||||||
public static String Symbol => "%"; // ??
|
public static String Symbol => "%"; // ??
|
||||||
|
|
||||||
public Percent(Decimal value) => Value = value;
|
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();
|
|
||||||
}
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
using InnovEnergy.Lib.Time.Unix;
|
using InnovEnergy.Lib.Time.Unix;
|
||||||
using InnovEnergy.Lib.Units.Generator;
|
using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct Power
|
public readonly partial struct Power
|
||||||
{
|
{
|
||||||
public static String Unit => "W";
|
public static String Unit => "W";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Power;
|
using T = Power;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(PowerConverter))]
|
[JsonConverter(typeof(PowerConverter))]
|
||||||
public readonly partial struct Power
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -3,7 +3,7 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct ReactivePower
|
public readonly partial struct ReactivePower
|
||||||
{
|
{
|
||||||
public static String Unit => "var";
|
public static String Unit => "var";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = ReactivePower;
|
using T = ReactivePower;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(ReactivePowerConverter))]
|
[JsonConverter(typeof(ReactivePowerConverter))]
|
||||||
public readonly partial struct ReactivePower
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -3,9 +3,8 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
|
|
||||||
// TODO: op parallel is wrong
|
|
||||||
|
|
||||||
[Sum]
|
[Generate]
|
||||||
public readonly partial struct Resistance
|
public readonly partial struct Resistance
|
||||||
{
|
{
|
||||||
public static String Unit => "Ω";
|
public static String Unit => "Ω";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Sum
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Resistance;
|
using T = Resistance;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(ResistanceConverter))]
|
[JsonConverter(typeof(ResistanceConverter))]
|
||||||
public readonly partial struct Resistance
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
|
||||||
|
|
||||||
public readonly struct State : IReadOnlyList<String>
|
|
||||||
{
|
|
||||||
public IReadOnlyList<String> Values { get; }
|
|
||||||
|
|
||||||
public State(IReadOnlyList<String> 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<String>)values){}
|
|
||||||
public State(params State[] states) : this((IReadOnlyList<String>)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<String> s) => new((IReadOnlyList<String>)s);
|
|
||||||
public static implicit operator State(String[] s) => new((IReadOnlyList<String>)s);
|
|
||||||
public static implicit operator State(List<Enum> 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<String> 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<T> From<T>(T t) where T : Enum
|
|
||||||
{
|
|
||||||
return new State<T>(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
|
||||||
|
|
||||||
public readonly struct State<T> : IReadOnlyList<T> where T:Enum
|
|
||||||
{
|
|
||||||
public IReadOnlyList<T> Values { get; }
|
|
||||||
|
|
||||||
public State(IReadOnlyList<T> values) => Values = values;
|
|
||||||
|
|
||||||
public State(params T[] values) : this((IReadOnlyList<T>)values){}
|
|
||||||
public State(params State<T>[] states) : this((IReadOnlyList<T>)states.SelectMany(s => s.Values).ToList()){}
|
|
||||||
|
|
||||||
public static implicit operator State<T>(T s) => new((IReadOnlyList<T>)s);
|
|
||||||
public static implicit operator State<T>(List<T> s) => new((IReadOnlyList<T>)s);
|
|
||||||
public static implicit operator State<T>(T[] s) => new((IReadOnlyList<T>)s);
|
|
||||||
|
|
||||||
public static State<T> operator |(State<T> left, State<T> right) => new State<T>(left, right);
|
|
||||||
|
|
||||||
public IEnumerator<T> 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];
|
|
||||||
}
|
|
|
@ -2,14 +2,14 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T=Temperature;
|
using T = Temperature;
|
||||||
|
|
||||||
[Mean]
|
|
||||||
|
[Generate]
|
||||||
public readonly partial struct Temperature
|
public readonly partial struct Temperature
|
||||||
{
|
{
|
||||||
public static String Unit => "°C";
|
public static String Unit => "°C";
|
||||||
public static String Symbol => "T";
|
public static String Symbol => "T";
|
||||||
|
|
||||||
public Temperature(Decimal value) => Value = value;
|
public Temperature(Decimal value) => Value = value;
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Mean
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Temperature;
|
using T = Temperature;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(TemperatureConverter))]
|
[JsonConverter(typeof(TemperatureConverter))]
|
||||||
public readonly partial struct Temperature
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -21,18 +21,14 @@ public static class Units
|
||||||
public static Byte DisplaySignificantDigits { get; set; } = 3;
|
public static Byte DisplaySignificantDigits { get; set; } = 3;
|
||||||
public static Byte JsonSignificantDigits { get; set; } = 3;
|
public static Byte JsonSignificantDigits { get; set; } = 3;
|
||||||
|
|
||||||
|
public static Current A (this Decimal value) => value;
|
||||||
public const Decimal MaxRelativeError = 0.05m; // 5%
|
public static Voltage V (this Decimal value) => value;
|
||||||
|
public static Power W (this Decimal value) => value;
|
||||||
public static Current A (this Decimal value) => new Current(value);
|
public static ReactivePower Var (this Decimal value) => value;
|
||||||
public static Voltage V (this Decimal value) => new Voltage(value);
|
public static ApparentPower Va (this Decimal value) => value;
|
||||||
public static Power W (this Decimal value) => new Power(value);
|
public static Resistance Ohm (this Decimal value) => value;
|
||||||
public static ReactivePower Var (this Decimal value) => new ReactivePower(value);
|
public static Frequency Hz (this Decimal value) => value;
|
||||||
public static ApparentPower Va (this Decimal value) => new ApparentPower(value);
|
public static Angle Rad (this Decimal value) => value;
|
||||||
public static Resistance Ohm (this Decimal value) => new Resistance(value);
|
public static Temperature Celsius(this Decimal value) => value;
|
||||||
public static Frequency Hz (this Decimal value) => new Frequency(value);
|
public static Energy KWh (this Decimal value) => value;
|
||||||
public static Angle Rad (this Decimal value) => new Angle(value);
|
|
||||||
public static Temperature Celsius(this Decimal value) => new Temperature(value);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,7 +2,8 @@ using InnovEnergy.Lib.Units.Generator;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
[Equal]
|
|
||||||
|
[Generate]
|
||||||
public readonly partial struct Voltage
|
public readonly partial struct Voltage
|
||||||
{
|
{
|
||||||
public static String Unit => "V";
|
public static String Unit => "V";
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
#nullable enable // Auto-generated code requires an explicit '#nullable' directive in source.
|
||||||
#define Equal
|
#define Generate
|
||||||
|
|
||||||
using static System.Math;
|
using static System.Math;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
|
||||||
namespace InnovEnergy.Lib.Units;
|
namespace InnovEnergy.Lib.Units;
|
||||||
|
|
||||||
using T = Voltage;
|
using T = Voltage;
|
||||||
|
|
||||||
|
[GeneratedCode("generate.sh", "1")]
|
||||||
[JsonConverter(typeof(VoltageConverter))]
|
[JsonConverter(typeof(VoltageConverter))]
|
||||||
public readonly partial struct Voltage
|
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(scalar * t.Value);
|
||||||
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
public static T operator /(T t, Decimal scalar) => new T(t.Value / scalar);
|
||||||
|
|
||||||
// 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
|
// 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;
|
||||||
|
|
|
@ -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)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue