Initial commit

This commit is contained in:
2026-07-03 16:31:37 +07:00
commit 899c7c637d
1939 changed files with 641750 additions and 0 deletions

View File

@@ -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);
}
}