using System.Collections.Concurrent; using RobotNet10.ScriptEngine.Models; using RobotNet10.ScriptEngine.Shared; using RobotNet10.Shared; namespace RobotNet10.ScriptEngine; /// /// Manages global variables shared across all scripts in ScriptEngine. /// Variables are stored in a thread-safe ConcurrentDictionary and are runtime-only (not persisted to database). /// public class VariableManager { /// /// Gets the underlying dictionary of variables. This is used to pass to ScriptGlobals. /// public IDictionary Globals => _globals; private readonly ConcurrentDictionary _globals = []; private readonly ConcurrentDictionary _models = []; /// /// Gets all variable models. /// public IReadOnlyDictionary Models => _models; /// /// Resets all variables and models. Clears both the globals dictionary and models dictionary. /// public void Reset() { _globals.Clear(); _models.Clear(); } /// /// Loads variables from a collection of ScriptVariableModel. This clears existing variables and models first. /// /// The collection of variable models to load. public void Load(IEnumerable variables) { if (variables == null) throw new ArgumentNullException(nameof(variables)); _globals.Clear(); _models.Clear(); foreach (var variable in variables) { if (string.IsNullOrWhiteSpace(variable.Name)) continue; // Lưu metadata cho biến // Store metadata for variable _models.TryAdd(variable.Name, new ScriptVariableModel( variable.Name, variable.Type, variable.DefaultValue, variable.PublicRead, variable.PublicWrite)); // Khởi tạo biến với giá trị mặc định // Initialize variable with default value _globals.TryAdd(variable.Name, variable.DefaultValue); } } /// /// Gets all variables that are marked as PublicRead, converted to ScriptVariableDto for UI display. /// /// A collection of ScriptVariableDto representing all public-readable variables. public IEnumerable GetVariables() { return _models.Values .Where(variable => variable.PublicRead) .Select(variable => new ScriptVariableDto( variable.Name, variable.TypeName, _globals.TryGetValue(variable.Name, out var value) ? (value?.ToString() ?? "null") : "null", variable.PublicWrite)); } /// /// Gets specific variables by names that are marked as PublicRead, converted to ScriptVariableDto for UI display. /// /// The names of variables to retrieve. /// A collection of ScriptVariableDto representing the requested public-readable variables. public IEnumerable GetVariables(string[] names) { if (names == null) throw new ArgumentNullException(nameof(names)); return _models.Values .Where(variable => variable.PublicRead && names.Contains(variable.Name)) .Select(variable => new ScriptVariableDto( variable.Name, variable.TypeName, _globals.TryGetValue(variable.Name, out var value) ? (value?.ToString() ?? "null") : "null", variable.PublicWrite)); } /// /// Gets the value of a variable by name. /// /// The name of the variable. /// The value of the variable, or null if not found. public object? GetVariable(string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Variable name cannot be null or empty", nameof(name)); _globals.TryGetValue(name, out var value); return value; } /// /// Gets the value of a variable by name with type conversion. /// /// The type to convert to. /// The name of the variable. /// The value of the variable converted to type T, or default(T) if not found. public T? GetVariable(string name) { var value = GetVariable(name); if (value == null) return default; if (value is T typedValue) return typedValue; try { return (T)Convert.ChangeType(value, typeof(T)); } catch { return default; } } /// /// Sets the value of a variable by name from a string value. Only variables marked as PublicWrite can be set. /// /// The name of the variable. /// The string value to set (will be converted to the variable's type). /// MessageResult indicating success or failure. public MessageResult SetValue(string name, string value) { if (string.IsNullOrWhiteSpace(name)) return new MessageResult(false, "Variable name cannot be null or empty"); if (!_models.TryGetValue(name, out var metadata)) return new MessageResult(false, $"Variable '{name}' not found"); if (!metadata.PublicWrite) return new MessageResult(false, $"Variable '{name}' is not writable"); try { object? convertedValue = null; if (!string.IsNullOrEmpty(value) && value != "null") { if (metadata.Type == typeof(string)) { convertedValue = value; } else if (metadata.Type.IsEnum) { convertedValue = Enum.Parse(metadata.Type, value, true); } else { convertedValue = Convert.ChangeType(value, metadata.Type); } } if (SetVariable(name, convertedValue, false)) { return new MessageResult(true, $"Variable '{name}' set successfully"); } else { return new MessageResult(false, $"Failed to set variable '{name}'"); } } catch (Exception ex) { return new MessageResult(false, $"Failed to set variable '{name}': {ex.Message}"); } } /// /// Sets the value of a variable by name. Only variables marked as PublicWrite can be set from outside scripts. /// /// The name of the variable. /// The value to set. /// If true, allows setting even if PublicWrite is false (for internal use). /// True if the variable was set successfully, false otherwise. public bool SetVariable(string name, object? value, bool force = false) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Variable name cannot be null or empty", nameof(name)); // Check if variable exists and if it's writable (unless force is true) if (_models.TryGetValue(name, out var metadata)) { if (!force && !metadata.PublicWrite) return false; // Variable exists but is not writable // Validate type if metadata exists if (value != null && !metadata.Type.IsInstanceOfType(value)) { try { value = Convert.ChangeType(value, metadata.Type); } catch { return false; // Type conversion failed } } } _globals.AddOrUpdate(name, value, (key, oldValue) => value); return true; } /// /// Checks if a variable exists. /// /// The name of the variable. /// True if the variable exists, false otherwise. public bool HasVariable(string name) { if (string.IsNullOrWhiteSpace(name)) return false; return _globals.ContainsKey(name); } /// /// Gets all variable names. /// /// A collection of all variable names. public IEnumerable GetVariableNames() { return _globals.Keys; } /// /// Gets all variables as a read-only dictionary snapshot. /// /// A read-only dictionary containing all variables. public IReadOnlyDictionary GetAllVariables() { return new Dictionary(_globals); } }