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 Globals => _globals; private readonly ConcurrentDictionary _globals = new(); private readonly Dictionary _globalsTypes = []; private readonly Dictionary variables = []; private readonly IHubContext consoleHubContext; private readonly ScriptRobotManager robotManager; private readonly ScriptMapManager mapManager; private readonly ScriptConnectionManager ConnectionManager; private readonly IServiceScopeFactory scopeFactory; public ScriptGlobalsManager(IConfiguration configuration, OpenIddictClientService openIddictClient, IHubContext _consoleHubContext, ScriptConnectionManager connectionManager, IServiceScopeFactory _scopeFactory) { consoleHubContext = _consoleHubContext; this.scopeFactory = _scopeFactory; ConnectionManager = connectionManager; var robotManagerOptions = configuration.GetSection("RobotManager").Get() ?? throw new InvalidOperationException("OpenID configuration not found or invalid format."); robotManager = new ScriptRobotManager(openIddictClient, robotManagerOptions.Url, robotManagerOptions.Scopes); var mapManagerOptions = configuration.GetSection("MapManager").Get() ?? throw new InvalidOperationException("OpenID configuration not found or invalid format."); mapManager = new ScriptMapManager(openIddictClient, mapManagerOptions.Url, mapManagerOptions.Scopes); } public void LoadVariables(IEnumerable 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 GetVariablesData() { var vars = new List(); 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 GetRobotNetTask(string name) { var globalRobotNet = new ScriptGlobalsRobotNetTask(new ScriptTaskLogger(consoleHubContext, name), robotManager, mapManager, ConnectionManager, scopeFactory); return ConvertGlobalsToDictionary(globalRobotNet); } public IDictionary 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 ConvertGlobalsToDictionary(IRobotNetGlobals globals) { var robotnet = new Dictionary(); 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; } }