using Microsoft.AspNetCore.SignalR; using RobotNet.Script.Shares; using RobotNet.ScriptManager.Helpers; using RobotNet.ScriptManager.Hubs; using RobotNet.ScriptManager.Models; using System.Diagnostics; namespace RobotNet.ScriptManager.Services; public class ScriptStateManager(ScriptTaskManager taskManager, ScriptMissionManager missionManager, ScriptGlobalsManager globalsManager, ScriptConnectionManager connectionManager, IHubContext processorHubContext, IHubContext hmiHubContext, IHubContext consoleHubContext, ILogger logger) : LoopService(500) { public string StateMesssage { get; private set; } = ""; public ProcessorState State { get; private set; } = ProcessorState.Idle; public ProcessorRequest Request { get; private set; } = ProcessorRequest.None; private readonly ConsoleLog consoleLog = new(consoleHubContext, logger); private readonly Mutex mutex = new(); private CancellationTokenSource? runningCancellation; public bool Build(ref string message) { bool result = false; if (mutex.WaitOne(1000)) { if (Request != ProcessorRequest.None) { message = $"Không thể thực hiện build vì Processor đang thực hiện {Request}"; } else if (State == ProcessorState.Running) { message = "Không thể thực hiện build vì Processor đang Running}"; } else { result = true; SetRequest(ProcessorRequest.Build); } mutex.ReleaseMutex(); } else { message = "Không thể thực hiện build vì request timeout"; } return result; } public bool Run(ref string message) { bool result = false; if (mutex.WaitOne(1000)) { if (Request != ProcessorRequest.None) { message = $"Không thể thực hiện run vì Processor đang thực hiện {Request}"; } else if (State != ProcessorState.Ready) { message = $"Không thể thực hiện run vì Processor đang ở trạng thái {State}, không phải Ready"; } else { result = true; SetRequest(ProcessorRequest.Run); } mutex.ReleaseMutex(); } else { message = "Không thể thực hiện run vì request timeout"; } return result; } public bool Stop(ref string message) { bool result = false; if (mutex.WaitOne(1000)) { if (Request != ProcessorRequest.None) { message = $"Không thể thực hiện stop vì Processor đang thực hiện {Request}"; } else if (State != ProcessorState.Running) { message = $"Không thể thực hiện stop vì Processor đang ở trạng thái {State}, không phải Running"; } else { result = true; SetRequest(ProcessorRequest.Stop); } mutex.ReleaseMutex(); } else { message = "Không thể thực hiện stop vì request timeout"; } return result; } public bool Reset(ref string message) { bool result = false; if (mutex.WaitOne(1000)) { if (Request != ProcessorRequest.None) { message = $"Không thể thực hiện reset vì Processor đang thực hiện {Request}"; } else if (State != ProcessorState.Ready && State != ProcessorState.Error && State != ProcessorState.BuildError && State != ProcessorState.Idle) { message = $"Không thể thực hiện reset vì Processor đang ở trạng thái {State}, không phải Ready hoặc Error hoặc BuildError"; } else { result = true; SetRequest(ProcessorRequest.Reset); } mutex.ReleaseMutex(); } else { message = "Không thể thực hiện reset vì request timeout"; } return result; } private void SetRequest(ProcessorRequest request) { if (Request == request) return; Request = request; _ = Task.Factory.StartNew(async () => { await processorHubContext.Clients.All.SendAsync("RequestChanged", Request); await hmiHubContext.Clients.All.SendAsync("RequestChanged", Request); }); } private void SetState(ProcessorState state) { if (State == state) return; State = state; _ = Task.Factory.StartNew(async () => { await processorHubContext.Clients.All.SendAsync("StateChanged", State); await hmiHubContext.Clients.All.SendAsync("StateChanged", State); }); } protected override async Task BeforExecuteAsync(CancellationToken cancellationToken) { missionManager.ResetMissionDb(); SetRequest(ProcessorRequest.Build); await base.BeforExecuteAsync(cancellationToken); } protected override void Execute(CancellationToken stoppingToken) { switch (State) { case ProcessorState.Idle: case ProcessorState.BuildError: case ProcessorState.Error: if (Request == ProcessorRequest.Build) { SetState(ProcessorState.Building); SetRequest(ProcessorRequest.None); BuildHandler(); } else if (Request == ProcessorRequest.Reset) { missionManager.Reset(); taskManager.Reset(); connectionManager.Reset(); SetState(ProcessorState.Idle); SetRequest(ProcessorRequest.None); } break; case ProcessorState.Ready: if (Request == ProcessorRequest.Build) { SetState(ProcessorState.Building); SetRequest(ProcessorRequest.None); BuildHandler(); } else if (Request == ProcessorRequest.Run) { SetState(ProcessorState.Running); SetRequest(ProcessorRequest.None); RunHandler(); } else if (Request == ProcessorRequest.Reset) { missionManager.Reset(); taskManager.Reset(); connectionManager.Reset(); SetState(ProcessorState.Idle); SetRequest(ProcessorRequest.None); } break; case ProcessorState.Running: if (Request == ProcessorRequest.Stop) { connectionManager.Reset(); SetState(ProcessorState.Stopping); SetRequest(ProcessorRequest.None); StopHandler(); } break; case ProcessorState.Building: case ProcessorState.Stopping: case ProcessorState.Starting: default: SetRequest(ProcessorRequest.None); break; } } private void BuildHandler() { _ = Task.Factory.StartNew(() => { consoleLog.LogInfo($"Start build all scripts"); var watch = new Stopwatch(); watch.Start(); var code = ScriptConfiguration.GetScriptCode(); string error = string.Empty; try { if (CSharpSyntaxHelper.VerifyScript(code, out error, out var variables, out var tasks, out var missions)) { globalsManager.LoadVariables(variables); taskManager.LoadTasks(tasks); missionManager.LoadMissions(missions); watch.Stop(); consoleLog.LogInfo($"Build all scripts successfully in {watch.ElapsedMilliseconds} ms"); SetState(ProcessorState.Ready); return; } } catch (Exception ex) { error = ex.ToString(); } watch.Stop(); SetState(ProcessorState.BuildError); consoleLog.LogError(error); }); } private void RunHandler() { _ = Task.Factory.StartNew(() => { runningCancellation = new(); taskManager.StartAll(runningCancellation.Token); missionManager.Start(runningCancellation.Token); SetState(ProcessorState.Running); }); } private void StopHandler() { _ = Task.Factory.StartNew(() => { runningCancellation?.Cancel(); taskManager.StopAll(); missionManager.Stop(); SetState(ProcessorState.Ready); }); } }