Initial commit
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user