259 lines
9.3 KiB
C#
259 lines
9.3 KiB
C#
using System.Collections.Concurrent;
|
|
using RobotNet10.ScriptEngine.Models;
|
|
using RobotNet10.ScriptEngine.Shared;
|
|
using RobotNet10.Shared;
|
|
|
|
namespace RobotNet10.ScriptEngine;
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
public class VariableManager
|
|
{
|
|
/// <summary>
|
|
/// Gets the underlying dictionary of variables. This is used to pass to ScriptGlobals.
|
|
/// </summary>
|
|
public IDictionary<string, object?> Globals => _globals;
|
|
|
|
private readonly ConcurrentDictionary<string, object?> _globals = [];
|
|
private readonly ConcurrentDictionary<string, ScriptVariableModel> _models = [];
|
|
|
|
/// <summary>
|
|
/// Gets all variable models.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, ScriptVariableModel> Models => _models;
|
|
|
|
/// <summary>
|
|
/// Resets all variables and models. Clears both the globals dictionary and models dictionary.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
_globals.Clear();
|
|
_models.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads variables from a collection of ScriptVariableModel. This clears existing variables and models first.
|
|
/// </summary>
|
|
/// <param name="variables">The collection of variable models to load.</param>
|
|
public void Load(IEnumerable<ScriptVariableModel> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all variables that are marked as PublicRead, converted to ScriptVariableDto for UI display.
|
|
/// </summary>
|
|
/// <returns>A collection of ScriptVariableDto representing all public-readable variables.</returns>
|
|
public IEnumerable<ScriptVariableDto> 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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets specific variables by names that are marked as PublicRead, converted to ScriptVariableDto for UI display.
|
|
/// </summary>
|
|
/// <param name="names">The names of variables to retrieve.</param>
|
|
/// <returns>A collection of ScriptVariableDto representing the requested public-readable variables.</returns>
|
|
public IEnumerable<ScriptVariableDto> 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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of a variable by name.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <returns>The value of the variable, or null if not found.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of a variable by name with type conversion.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type to convert to.</typeparam>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <returns>The value of the variable converted to type T, or default(T) if not found.</returns>
|
|
public T? GetVariable<T>(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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the value of a variable by name from a string value. Only variables marked as PublicWrite can be set.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The string value to set (will be converted to the variable's type).</param>
|
|
/// <returns>MessageResult indicating success or failure.</returns>
|
|
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}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the value of a variable by name. Only variables marked as PublicWrite can be set from outside scripts.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set.</param>
|
|
/// <param name="force">If true, allows setting even if PublicWrite is false (for internal use).</param>
|
|
/// <returns>True if the variable was set successfully, false otherwise.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a variable exists.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <returns>True if the variable exists, false otherwise.</returns>
|
|
public bool HasVariable(string name)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
return false;
|
|
|
|
return _globals.ContainsKey(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all variable names.
|
|
/// </summary>
|
|
/// <returns>A collection of all variable names.</returns>
|
|
public IEnumerable<string> GetVariableNames()
|
|
{
|
|
return _globals.Keys;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all variables as a read-only dictionary snapshot.
|
|
/// </summary>
|
|
/// <returns>A read-only dictionary containing all variables.</returns>
|
|
public IReadOnlyDictionary<string, object?> GetAllVariables()
|
|
{
|
|
return new Dictionary<string, object?>(_globals);
|
|
}
|
|
}
|