232 lines
9.2 KiB
C#
232 lines
9.2 KiB
C#
using Microsoft.AspNetCore.SignalR;
|
|
using Microsoft.CodeAnalysis;
|
|
using OpenIddict.Client;
|
|
using RobotNet.OpenIddictClient;
|
|
using RobotNet.Script.Shares;
|
|
using RobotNet.ScriptManager.Hubs;
|
|
using RobotNet.ScriptManager.Models;
|
|
using System.Collections.Concurrent;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
|
|
namespace RobotNet.ScriptManager.Services;
|
|
|
|
public class ScriptGlobalsManager
|
|
{
|
|
public IDictionary<string, object?> Globals => _globals;
|
|
private readonly ConcurrentDictionary<string, object?> _globals = new();
|
|
private readonly Dictionary<string, Type> _globalsTypes = [];
|
|
private readonly Dictionary<string, ScriptVariableSyntax> variables = [];
|
|
private readonly IHubContext<ConsoleHub> consoleHubContext;
|
|
private readonly ScriptRobotManager robotManager;
|
|
private readonly ScriptMapManager mapManager;
|
|
private readonly ScriptConnectionManager ConnectionManager;
|
|
private readonly IServiceScopeFactory scopeFactory;
|
|
|
|
public ScriptGlobalsManager(IConfiguration configuration, OpenIddictClientService openIddictClient, IHubContext<ConsoleHub> _consoleHubContext, ScriptConnectionManager connectionManager, IServiceScopeFactory _scopeFactory)
|
|
{
|
|
consoleHubContext = _consoleHubContext;
|
|
this.scopeFactory = _scopeFactory;
|
|
ConnectionManager = connectionManager;
|
|
|
|
var robotManagerOptions = configuration.GetSection("RobotManager").Get<OpenIddictResourceOptions>() ?? throw new InvalidOperationException("OpenID configuration not found or invalid format.");
|
|
robotManager = new ScriptRobotManager(openIddictClient, robotManagerOptions.Url, robotManagerOptions.Scopes);
|
|
|
|
var mapManagerOptions = configuration.GetSection("MapManager").Get<OpenIddictResourceOptions>() ?? throw new InvalidOperationException("OpenID configuration not found or invalid format.");
|
|
mapManager = new ScriptMapManager(openIddictClient, mapManagerOptions.Url, mapManagerOptions.Scopes);
|
|
}
|
|
|
|
public void LoadVariables(IEnumerable<ScriptVariableSyntax> variableSynctaxs)
|
|
{
|
|
_globals.Clear();
|
|
_globalsTypes.Clear();
|
|
variables.Clear();
|
|
foreach (var v in variableSynctaxs)
|
|
{
|
|
variables.Add(v.Name, new(v.Name, v.Type, v.DefaultValue, v.PublicRead, v.PublicWrite));
|
|
_globals.TryAdd(v.Name, v.DefaultValue);
|
|
_globalsTypes.TryAdd(v.Name, v.Type);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<ScriptVariableDto> GetVariablesData()
|
|
{
|
|
var vars = new List<ScriptVariableDto>();
|
|
foreach (var v in variables.Values)
|
|
{
|
|
if (v.PublicRead)
|
|
{
|
|
vars.Add(new ScriptVariableDto(v.Name, v.TypeName, _globals[v.Name]?.ToString() ?? "null"));
|
|
}
|
|
}
|
|
|
|
return vars;
|
|
}
|
|
|
|
public Type? GetVariableType(string name)
|
|
{
|
|
if (_globalsTypes.TryGetValue(name, out var type))
|
|
{
|
|
return type;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public IDictionary<string, object?> GetRobotNetTask(string name)
|
|
{
|
|
var globalRobotNet = new ScriptGlobalsRobotNetTask(new ScriptTaskLogger(consoleHubContext, name), robotManager, mapManager, ConnectionManager, scopeFactory);
|
|
|
|
return ConvertGlobalsToDictionary(globalRobotNet);
|
|
}
|
|
|
|
public IDictionary<string, object?> GetRobotNetMission(Guid missionId)
|
|
{
|
|
var globalRobotNet = new ScriptGlobalsRobotNetMission(missionId, new ScriptMissionLogger(consoleHubContext, missionId), robotManager, mapManager, ConnectionManager, scopeFactory);
|
|
|
|
return ConvertGlobalsToDictionary(globalRobotNet);
|
|
}
|
|
|
|
public void SetValue(string name, string value)
|
|
{
|
|
if (variables.TryGetValue(name, out var variable))
|
|
{
|
|
if (variable.PublicWrite)
|
|
{
|
|
if (_globalsTypes.TryGetValue(name, out var type))
|
|
{
|
|
try
|
|
{
|
|
var convertedValue = Convert.ChangeType(value, type);
|
|
_globals[name] = convertedValue;
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
throw new InvalidOperationException($"Cannot convert value '{value}' to type '{type.FullName}' for variable '{name}'.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new KeyNotFoundException($"Variable type for '{name}' not found.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new InvalidOperationException($"Variable '{name}' is not writable.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new KeyNotFoundException($"Variable '{name}' not found.");
|
|
}
|
|
}
|
|
|
|
public void ResetValue(string name)
|
|
{
|
|
if (variables.TryGetValue(name, out var variable))
|
|
{
|
|
if (_globalsTypes.TryGetValue(name, out var type))
|
|
{
|
|
_globals[name] = Convert.ChangeType(variable.DefaultValue, type);
|
|
}
|
|
else
|
|
{
|
|
throw new KeyNotFoundException($"Variable type for '{name}' not found.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new KeyNotFoundException($"Variable '{name}' not found.");
|
|
}
|
|
}
|
|
|
|
private static Dictionary<string, object?> ConvertGlobalsToDictionary(IRobotNetGlobals globals)
|
|
{
|
|
var robotnet = new Dictionary<string, object?>();
|
|
var type = typeof(IRobotNetGlobals);
|
|
|
|
|
|
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
|
{
|
|
robotnet.TryAdd(field.Name, field.GetValue(globals));
|
|
}
|
|
|
|
foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
|
{
|
|
if (prop.GetIndexParameters().Length == 0)
|
|
{
|
|
// Forward getter if available
|
|
if (prop.GetGetMethod() != null)
|
|
{
|
|
var getter = Delegate.CreateDelegate(
|
|
Expression.GetDelegateType([prop.PropertyType]),
|
|
globals,
|
|
prop.GetGetMethod()!
|
|
);
|
|
robotnet.TryAdd($"get_{prop.Name}", getter);
|
|
}
|
|
// Forward setter if available
|
|
if (prop.GetSetMethod() != null)
|
|
{
|
|
var setter = Delegate.CreateDelegate(
|
|
Expression.GetDelegateType([prop.PropertyType, typeof(void)]),
|
|
globals,
|
|
prop.GetSetMethod()!
|
|
);
|
|
robotnet.TryAdd($"set_{prop.Name}", setter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Handle indexers (properties with parameters)
|
|
var indexParams = prop.GetIndexParameters().Select(p => p.ParameterType).ToArray();
|
|
|
|
// Forward indexer getter if available
|
|
if (prop.GetGetMethod() != null)
|
|
{
|
|
var getterParamTypes = indexParams.Concat([prop.PropertyType]).ToArray();
|
|
var getterDelegate = Delegate.CreateDelegate(
|
|
Expression.GetDelegateType(getterParamTypes),
|
|
globals,
|
|
prop.GetGetMethod()!
|
|
);
|
|
robotnet.TryAdd($"get_{prop.Name}_indexer", getterDelegate);
|
|
}
|
|
|
|
// Forward indexer setter if available
|
|
if (prop.GetSetMethod() != null)
|
|
{
|
|
var setterParamTypes = indexParams.Concat([prop.PropertyType, typeof(void)]).ToArray();
|
|
var setterDelegate = Delegate.CreateDelegate(
|
|
Expression.GetDelegateType(setterParamTypes),
|
|
globals,
|
|
prop.GetSetMethod()!
|
|
);
|
|
robotnet.TryAdd($"set_{prop.Name}_indexer", setterDelegate);
|
|
}
|
|
}
|
|
}
|
|
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
|
{
|
|
if (!method.IsSpecialName)
|
|
{
|
|
var parameters = string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.FullName} {p.Name}"));
|
|
var args = string.Join(", ", method.GetParameters().Select(p => p.Name));
|
|
var returnType = method.ReturnType == typeof(void) ? "void" : method.ReturnType.FullName;
|
|
|
|
var paramTypes = string.Join(",", method.GetParameters().Select(p => p.ParameterType.FullName));
|
|
var methodKey = $"{method.Name}({paramTypes})";
|
|
|
|
var del = Delegate.CreateDelegate(
|
|
Expression.GetDelegateType([.. method.GetParameters().Select(p => p.ParameterType), method.ReturnType]),
|
|
globals,
|
|
method
|
|
);
|
|
// TODO: tôi muốn thay vì sử dụng tên phương thức, tôi muốn sử dụng tên phương thức với danh sánh kiểu dữ liệu của các tham số trong dấu ngoặc tròn
|
|
robotnet.TryAdd(methodKey, del);
|
|
}
|
|
}
|
|
return robotnet;
|
|
}
|
|
}
|