Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using RobotNet10.Components.Clients;
using RobotNet10.ScriptEngine.Shared;
namespace RobotNet10.ScriptEditor.Clients;
/// <summary>
/// SignalR HubClient for ConsoleHub - handles console logging events.
/// </summary>
public class ConsoleHubClient : HubClient
{
public event Action<string>? ErrorReceived;
public event Action<string>? InfoReceived;
public event Action<string>? WarningReceived;
public ConsoleHubClient(NavigationManager navigationManager)
: base(navigationManager.ToAbsoluteUri(HubEndpoints.ScriptConsoleHubPath))
{
Connection.On<string>("Error", message => ErrorReceived?.Invoke(message));
Connection.On<string>("Info", message => InfoReceived?.Invoke(message));
Connection.On<string>("Warning", message => WarningReceived?.Invoke(message));
}
/// <summary>
/// Registers to receive console messages for a specific task.
/// </summary>
public Task RegisterTaskAsync(string name) => Connection.InvokeAsync("RegisterTask", name);
/// <summary>
/// Unregisters from receiving console messages for a specific task.
/// </summary>
public Task UnregisterTaskAsync(string name) => Connection.InvokeAsync("UnregisterTask", name);
/// <summary>
/// Registers to receive console messages for a specific mission.
/// </summary>
public Task RegisterMissionAsync(Guid missionId) => Connection.InvokeAsync("RegisterMission", missionId);
/// <summary>
/// Unregisters from receiving console messages for a specific mission.
/// </summary>
public Task UnregisterMissionAsync(Guid missionId) => Connection.InvokeAsync("UnregisterMission", missionId);
/// <summary>
/// Registers to receive all console messages.
/// </summary>
public Task RegisterAllAsync() => Connection.InvokeAsync("RegisterAll");
/// <summary>
/// Unregisters from receiving all console messages.
/// </summary>
public Task UnregisterAllAsync() => Connection.InvokeAsync("UnregisterAll");
}

View File

@@ -0,0 +1,181 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using RobotNet10.Components.Clients;
using RobotNet10.ScriptEngine.Shared;
namespace RobotNet10.ScriptEditor.Clients;
/// <summary>
/// SignalR HubClient for FileManagerHub - handles file management operations.
/// Note: Events are only triggered when other clients perform actions through Hub methods,
/// not from FileManager class directly (FileManager doesn't have HubContext).
/// </summary>
public class FileManagerHubClient : HubClient
{
/// <summary>
/// Triggered when edit permission is revoked (sent to the previous connection).
/// </summary>
public event Action<string?>? EditPermissionRevoked;
/// <summary>
/// Triggered when another client saves a file.
/// </summary>
public event Action<string, string?>? FileSaved;
/// <summary>
/// Triggered when another client creates a file.
/// </summary>
public event Action<string, string?>? FileCreated;
/// <summary>
/// Triggered when another client creates a folder.
/// </summary>
public event Action<string, string?>? FolderCreated;
/// <summary>
/// Triggered when another client deletes a file.
/// </summary>
public event Action<string, string?>? FileDeleted;
/// <summary>
/// Triggered when another client deletes a folder.
/// </summary>
public event Action<string, string?>? FolderDeleted;
/// <summary>
/// Triggered when any client creates a backup.
/// </summary>
public event Action<string, string?>? BackupCreated;
/// <summary>
/// Triggered when any client restores a backup.
/// </summary>
public event Action<string, string?>? BackupRestored;
/// <summary>
/// Triggered when any client deletes a backup.
/// </summary>
public event Action<string, string?>? BackupDeleted;
public FileManagerHubClient(NavigationManager navigationManager)
: base(navigationManager.ToAbsoluteUri(HubEndpoints.ScriptFileManagerHubPath))
{
Connection.On<string?>("EditPermissionRevoked", userId => EditPermissionRevoked?.Invoke(userId));
Connection.On<string, string?>("FileSaved", (path, userId) => FileSaved?.Invoke(path, userId));
Connection.On<string, string?>("FileCreated", (path, userId) => FileCreated?.Invoke(path, userId));
Connection.On<string, string?>("FolderCreated", (path, userId) => FolderCreated?.Invoke(path, userId));
Connection.On<string, string?>("FileDeleted", (path, userId) => FileDeleted?.Invoke(path, userId));
Connection.On<string, string?>("FolderDeleted", (path, userId) => FolderDeleted?.Invoke(path, userId));
Connection.On<string, string?>("BackupCreated", (fileName, userId) => BackupCreated?.Invoke(fileName, userId));
Connection.On<string, string?>("BackupRestored", (fileName, userId) => BackupRestored?.Invoke(fileName, userId));
Connection.On<string, string?>("BackupDeleted", (fileName, userId) => BackupDeleted?.Invoke(fileName, userId));
// Request edit permission when reconnected
// This will always succeed and force out any previous connection
Connection.Reconnected += async _ =>
{
if (IsConnected)
{
try
{
await RequestEditPermissionAsync();
}
catch (Exception)
{
// Ignore errors - permission request should always succeed
}
}
};
}
public override async Task StartAsync()
{
await base.StartAsync();
// Automatically request edit permission when connected
// This will always succeed and force out any previous connection
if (IsConnected)
{
try
{
await RequestEditPermissionAsync();
}
catch (Exception)
{
// Ignore errors - permission request should always succeed
}
}
}
/// <summary>
/// Gets the root folder structure with all files and folders.
/// </summary>
public Task<ScriptFolderDto> GetRootFolderAsync() => Connection.InvokeAsync<ScriptFolderDto>("GetRootFolder");
/// <summary>
/// Gets the current state of the FileManager.
/// </summary>
public Task<ScriptEngineState> GetStateAsync() => Connection.InvokeAsync<ScriptEngineState>("GetState");
/// <summary>
/// Requests edit permission. If another connection has permission, it will be revoked and notified.
/// </summary>
public Task<bool> RequestEditPermissionAsync() => Connection.InvokeAsync<bool>("RequestEditPermission");
/// <summary>
/// Revokes edit permission for the current connection.
/// </summary>
public Task RevokeEditPermissionAsync() => Connection.InvokeAsync("RevokeEditPermission");
/// <summary>
/// Checks if the current connection has edit permission.
/// </summary>
public Task<bool> HasEditPermissionAsync() => Connection.InvokeAsync<bool>("HasEditPermission");
/// <summary>
/// Saves file content.
/// </summary>
public Task SaveFileAsync(string relativePath, string content) => Connection.InvokeAsync("SaveFile", relativePath, content);
/// <summary>
/// Creates a new file.
/// </summary>
public Task CreateFileAsync(string relativePath, string content) => Connection.InvokeAsync("CreateFile", relativePath, content);
/// <summary>
/// Creates a new folder.
/// </summary>
public Task CreateFolderAsync(string relativePath) => Connection.InvokeAsync("CreateFolder", relativePath);
/// <summary>
/// Deletes a file.
/// </summary>
public Task DeleteFileAsync(string relativePath) => Connection.InvokeAsync("DeleteFile", relativePath);
/// <summary>
/// Deletes a folder.
/// </summary>
public Task DeleteFolderAsync(string relativePath) => Connection.InvokeAsync("DeleteFolder", relativePath);
/// <summary>
/// Creates a backup of all scripts.
/// </summary>
public Task<string> CreateBackupAsync(string? backupName = null) => Connection.InvokeAsync<string>("CreateBackup", backupName);
/// <summary>
/// Lists available backup files.
/// </summary>
public Task<ScriptBackupInfo[]> ListBackupsAsync() => Connection.InvokeAsync<ScriptBackupInfo[]>("ListBackups");
/// <summary>
/// Restores scripts from a backup.
/// </summary>
public Task RestoreBackupAsync(string backupFileName, bool replaceExisting = true) =>
Connection.InvokeAsync("RestoreBackup", backupFileName, replaceExisting);
/// <summary>
/// Deletes a backup file.
/// </summary>
public Task DeleteBackupAsync(string backupFileName) => Connection.InvokeAsync("DeleteBackup", backupFileName);
}

View File

@@ -0,0 +1,59 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using RobotNet10.Components.Clients;
using RobotNet10.ScriptEngine.Shared;
using RobotNet10.Shared;
namespace RobotNet10.ScriptEditor.Clients;
/// <summary>
/// SignalR HubClient for InstanceMissionHub - handles instance mission operations.
/// </summary>
public class InstanceMissionHubClient : HubClient
{
public InstanceMissionHubClient(NavigationManager navigationManager)
: base(navigationManager.ToAbsoluteUri(HubEndpoints.InstanceMissionHubPath))
{
}
/// <summary>
/// Searches instance missions with pagination and text search.
/// </summary>
public Task<SearchResult<InstanceMissionDto>> SearchInstanceMissionsAsync(SearchRequest request)
{
return Connection.InvokeAsync<SearchResult<InstanceMissionDto>>("SearchInstanceMissions", request);
}
/// <summary>
/// Gets the log for a specific instance mission.
/// </summary>
public Task<string?> GetInstanceMissionLogAsync(Guid missionId)
{
return Connection.InvokeAsync<string?>("GetInstanceMissionLog", missionId);
}
/// <summary>
/// Cancels a running or paused mission.
/// </summary>
public Task<MessageResult> CancelMissionAsync(Guid missionId, string reason)
{
return Connection.InvokeAsync<MessageResult>("CancelMission", missionId, reason);
}
/// <summary>
/// Pauses a running mission.
/// </summary>
public Task<MessageResult> PauseMissionAsync(Guid missionId)
{
return Connection.InvokeAsync<MessageResult>("PauseMission", missionId);
}
/// <summary>
/// Resumes a paused mission.
/// </summary>
public Task<MessageResult> ResumeMissionAsync(Guid missionId)
{
return Connection.InvokeAsync<MessageResult>("ResumeMission", missionId);
}
}

View File

@@ -0,0 +1,187 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using RobotNet10.Components.Clients;
using RobotNet10.ScriptEngine.Shared;
using RobotNet10.Shared;
namespace RobotNet10.ScriptEditor.Clients;
/// <summary>
/// SignalR HubClient for ScriptManagerHub - handles ScriptEngine management operations.
/// </summary>
public class ScriptManagerHubClient : HubClient
{
public event Action<ScriptEngineState>? StateChanged;
/// <summary>
/// Gets the current ScriptEngine state. Updated when connected.
/// </summary>
public ScriptEngineState State { get; private set; }
public ScriptManagerHubClient(NavigationManager navigationManager)
: base(navigationManager.ToAbsoluteUri(HubEndpoints.ScriptManagerHubPath))
{
Connection.On<ScriptEngineState>("StateChanged", state =>
{
State = state;
StateChanged?.Invoke(state);
});
// Reload state when reconnected
Connection.Reconnected += async _ =>
{
if (IsConnected)
{
try
{
var newState = await GetStateAsync();
if (State != newState)
{
State = newState;
StateChanged?.Invoke(newState);
}
else
{
// Even if state hasn't changed, trigger event to update UI
StateChanged?.Invoke(newState);
}
}
catch (Exception)
{
// Ignore errors when reloading state on reconnect
}
}
};
}
public override async Task StartAsync()
{
await base.StartAsync();
// Lưu state khi connected và trigger StateChanged event
if (IsConnected)
{
var newState = await GetStateAsync();
if (State != newState)
{
State = newState;
StateChanged?.Invoke(newState);
}
else
{
// Even if state hasn't changed, trigger event to update UI
StateChanged?.Invoke(newState);
}
}
}
/// <summary>
/// Gets the current state of the ScriptEngine.
/// </summary>
public Task<ScriptEngineState> GetStateAsync() => Connection.InvokeAsync<ScriptEngineState>("GetState");
/// <summary>
/// Builds scripts from all files. Only allowed when state is Idle or BuildError.
/// </summary>
public Task<MessageResult> BuildAsync() => Connection.InvokeAsync<MessageResult>("Build");
/// <summary>
/// Starts the ScriptEngine. Only allowed when state is Ready.
/// </summary>
public Task<MessageResult> StartEingineAsync() => Connection.InvokeAsync<MessageResult>("Start");
/// <summary>
/// Stops the ScriptEngine. Only allowed when state is Running.
/// </summary>
public Task<MessageResult> StopEingineAsync() => Connection.InvokeAsync<MessageResult>("Stop");
/// <summary>
/// Resets the ScriptEngine. Allowed from Idle, Ready, BuildError, Running, or Fault.
/// </summary>
public Task<MessageResult> ResetAsync() => Connection.InvokeAsync<MessageResult>("Reset");
#region VariableManager Methods
/// <summary>
/// Gets all script variables.
/// </summary>
public Task<ScriptVariableDto[]> GetScriptVariablesAsync() =>
Connection.InvokeAsync<ScriptVariableDto[]>("GetScriptVariables");
/// <summary>
/// Finds specific script variables by names.
/// </summary>
public Task<ScriptVariableDto[]> FindScriptVariablesAsync(string[] names) =>
Connection.InvokeAsync<ScriptVariableDto[]>("FindScriptVariables", names);
/// <summary>
/// Sets the value of a script variable by name.
/// </summary>
public Task<MessageResult> SetValueAsync(string name, string value) =>
Connection.InvokeAsync<MessageResult>("SetValue", name, value);
#endregion
#region TaskManager Methods
/// <summary>
/// Gets all script tasks.
/// </summary>
public Task<ScriptTaskDto[]> GetScriptTasksAsync() =>
Connection.InvokeAsync<ScriptTaskDto[]>("GetScriptTasks");
/// <summary>
/// Finds specific script tasks by names.
/// </summary>
public Task<ScriptTaskDto[]> FindScriptTasksAsync(string[] names) =>
Connection.InvokeAsync<ScriptTaskDto[]>("FindScriptTasks", names);
/// <summary>
/// Enables a task (resumes if paused, starts if stopped).
/// </summary>
public Task<MessageResult> EnableTaskAsync(string name) =>
Connection.InvokeAsync<MessageResult>("EnableTask", name);
/// <summary>
/// Disables a task (pauses if running).
/// </summary>
public Task<MessageResult> DisableTaskAsync(string name) =>
Connection.InvokeAsync<MessageResult>("DisableTask", name);
#endregion
#region MissionManager Methods
/// <summary>
/// Gets all script missions.
/// </summary>
public Task<ScriptMissionDto[]> GetScriptMissionsAsync() =>
Connection.InvokeAsync<ScriptMissionDto[]>("GetScriptMissions");
/// <summary>
/// Finds specific script missions by names.
/// </summary>
public Task<ScriptMissionDto[]> FindScriptMissionsAsync(string[] names) =>
Connection.InvokeAsync<ScriptMissionDto[]>("FindScriptMissions", names);
/// <summary>
/// Creates a mission instance with parameters provided as a dictionary.
/// </summary>
/// <param name="name">The name of the mission model.</param>
/// <param name="args">Dictionary of parameter values keyed by parameter name (as strings).</param>
/// <returns>MessageResult containing the mission ID if successful.</returns>
public Task<MessageResult<Guid>> CreateMissionAsync(string name, IDictionary<string, string> args) =>
Connection.InvokeAsync<MessageResult<Guid>>("CreateMission", name, args);
/// <summary>
/// Cancels a running or paused mission.
/// </summary>
/// <param name="id">The ID of the mission instance to cancel.</param>
/// <param name="reason">The reason for cancellation.</param>
/// <returns>True if the mission was canceled successfully, false otherwise.</returns>
public Task<bool> CancelMissionAsync(Guid id, string reason) =>
Connection.InvokeAsync<bool>("CancelMission", id, reason);
#endregion
}