RobotNet/RobotNet.ScriptManager/Services/ScriptStateManager.cs
2025-10-15 15:15:53 +07:00

299 lines
9.4 KiB
C#

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<ProcessorHub> processorHubContext,
IHubContext<HMIHub> hmiHubContext,
IHubContext<ConsoleHub> consoleHubContext,
ILogger<ScriptStateManager> 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);
});
}
}