mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-04 07:16:35 +08:00
暂时实现了简陋的脚本AST分析解释,后面再绑定到控件上
This commit is contained in:
@@ -66,6 +66,7 @@ namespace Serein.NodeFlow.Env
|
||||
NodeMVVMManagement.RegisterModel(NodeControlType.ExpCondition, typeof(SingleConditionNode)); // 条件表达式节点
|
||||
NodeMVVMManagement.RegisterModel(NodeControlType.ConditionRegion, typeof(CompositeConditionNode)); // 条件区域
|
||||
NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点
|
||||
NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -401,9 +402,13 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
IOC.Reset(); // 开始运行时清空ioc中注册的实例
|
||||
|
||||
IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this);
|
||||
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||
IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this); // 注册流程实例
|
||||
if (this.UIContextOperation is not null)
|
||||
{
|
||||
// 注册封装好的UI线程上下文
|
||||
IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false);
|
||||
}
|
||||
|
||||
await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ namespace Serein.NodeFlow.Env
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
|
||||
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleGlobalDataNode)}" => NodeControlType.GlobalData, // 数据节点
|
||||
$"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleScriptNode)}" => NodeControlType.Script, // 数据节点
|
||||
_ => NodeControlType.None,
|
||||
};
|
||||
|
||||
|
||||
@@ -56,19 +56,6 @@ namespace Serein.NodeFlow
|
||||
#endif
|
||||
await startNode.StartFlowAsync(context); // 开始运行时从选定节点开始运行
|
||||
context.Exit();
|
||||
|
||||
/*
|
||||
|
||||
foreach (var node in NodeModels.Values)
|
||||
{
|
||||
if (node is not null)
|
||||
{
|
||||
node.ReleaseFlowData(); // 退出时释放对象
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +241,7 @@ namespace Serein.NodeFlow
|
||||
|
||||
try
|
||||
{
|
||||
//await TestScript(env);
|
||||
await startNode.StartFlowAsync(Context); // 开始运行时从起始节点开始运行
|
||||
|
||||
if (flipflopNodes.Count > 0)
|
||||
@@ -293,6 +281,39 @@ namespace Serein.NodeFlow
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
#if false
|
||||
|
||||
public async Task TestScript(IFlowEnvironment environment)
|
||||
{
|
||||
SingleScriptNode singleScriptNode = new SingleScriptNode(environment);
|
||||
string script =
|
||||
"""
|
||||
|
||||
//let argData1 = flow.GetArgIndex(0); // 通过索引的方式,获取当前节点入参第一个参数
|
||||
//let argData2 = flow.GetArgName("name"); // 通过名称的方式,获取当前节点入参的第二个参数
|
||||
//let nodeData = flow.GetFlowData(); // 获取上一个节点的数据
|
||||
//let state = flow.GetGlobalData("key name"); // 获取全局数据
|
||||
|
||||
//let result1 = flow.CallNode("node guid",); // 立即调用某个节点,获取数据
|
||||
//let result2 = flow.CallFunc();
|
||||
|
||||
class User{
|
||||
int ID;
|
||||
string Name;
|
||||
}
|
||||
let user = new User();
|
||||
user.ID = 12345;
|
||||
user.Name = "张三";
|
||||
return user;
|
||||
""";
|
||||
singleScriptNode.Script = script;
|
||||
singleScriptNode.LoadScript();
|
||||
var result = await singleScriptNode.ExecutingAsync(new DynamicContext(environment));
|
||||
SereinEnv.WriteLine(InfoType.INFO, result?.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
||||
|
||||
/// <summary>
|
||||
|
||||
143
NodeFlow/Model/SingleScriptNode.cs
Normal file
143
NodeFlow/Model/SingleScriptNode.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Script;
|
||||
using Serein.Script.Node;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
[NodeProperty(ValuePath = NodeValuePath.Node)]
|
||||
public partial class SingleScriptNode : NodeModelBase
|
||||
{
|
||||
[PropertyInfo(IsNotification = true)]
|
||||
private string _script;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 流程脚本节点
|
||||
/// </summary>
|
||||
public partial class SingleScriptNode : NodeModelBase
|
||||
{
|
||||
private IScriptFlowApi ScriptFlowApi { get; }
|
||||
|
||||
private ASTNode mainNode;
|
||||
|
||||
/// <summary>
|
||||
/// 构建流程脚本节点
|
||||
/// </summary>
|
||||
/// <param name="environment"></param>
|
||||
public SingleScriptNode(IFlowEnvironment environment):base(environment)
|
||||
{
|
||||
//ScriptFlowApi = environment.IOC.Get<ScriptFlowApi>();
|
||||
ScriptFlowApi = new ScriptFlowApi(environment, this);
|
||||
|
||||
|
||||
MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi));
|
||||
if (method != null)
|
||||
{
|
||||
SereinScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口
|
||||
}
|
||||
|
||||
// 挂载静态方法
|
||||
var tempMethods = typeof(BaseFunc).GetMethods().Where(method =>
|
||||
!(method.Name.Equals("GetHashCode")
|
||||
|| method.Name.Equals("Equals")
|
||||
|| method.Name.Equals("ToString")
|
||||
|| method.Name.Equals("GetType")
|
||||
)).Select(method => (method.Name, method)).ToArray();
|
||||
|
||||
foreach ((string name, MethodInfo method) item in tempMethods)
|
||||
{
|
||||
SereinScriptInterpreter.AddFunction(item.name, item.method); // 加载基础方法
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载脚本代码
|
||||
/// </summary>
|
||||
public void LoadScript()
|
||||
{
|
||||
try
|
||||
{
|
||||
mainNode = new SereinScriptParser(Script).Parse();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行脚本
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
{
|
||||
|
||||
mainNode ??= new SereinScriptParser(Script).Parse();
|
||||
SereinScriptInterpreter scriptInterpreter = new SereinScriptInterpreter();
|
||||
var result = await scriptInterpreter.InterpretAsync(mainNode); // 从入口节点执行
|
||||
scriptInterpreter.ResetVar();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public IScriptFlowApi GetFlowApi()
|
||||
{
|
||||
return ScriptFlowApi;
|
||||
}
|
||||
|
||||
private static class BaseFunc
|
||||
{
|
||||
public static Type TypeOf(object type)
|
||||
{
|
||||
return type.GetType();
|
||||
}
|
||||
|
||||
|
||||
public static void Print(object value)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, value?.ToString());
|
||||
}
|
||||
|
||||
#region 数据转换
|
||||
public static int ToInt(object value)
|
||||
{
|
||||
return int.Parse(value.ToString());
|
||||
}
|
||||
public static double ToDouble(object value)
|
||||
{
|
||||
return double.Parse(value.ToString());
|
||||
}
|
||||
public static bool ToBool(object value)
|
||||
{
|
||||
return bool.Parse(value.ToString());
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static async Task Delay(object value)
|
||||
{
|
||||
if (value is int @int)
|
||||
{
|
||||
Console.WriteLine($"等待{@int}ms");
|
||||
await Task.Delay(@int);
|
||||
}
|
||||
else if (value is TimeSpan timeSpan)
|
||||
{
|
||||
|
||||
Console.WriteLine($"等待{timeSpan}");
|
||||
await Task.Delay(timeSpan);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
NodeFlow/ScriptFlowApi.cs
Normal file
66
NodeFlow/ScriptFlowApi.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
/// <summary>
|
||||
/// 脚本代码中关于流程运行的API
|
||||
/// </summary>
|
||||
public class ScriptFlowApi : IScriptFlowApi
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment Env { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
/// </summary>
|
||||
public NodeModelBase NodeModel { get; private set; }
|
||||
|
||||
IDynamicContext IScriptFlowApi.Context { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
/// <summary>
|
||||
/// 创建流程脚本接口
|
||||
/// </summary>
|
||||
/// <param name="environment">运行环境</param>
|
||||
/// <param name="nodeModel">节点</param>
|
||||
public ScriptFlowApi(IFlowEnvironment environment, NodeModelBase nodeModel)
|
||||
{
|
||||
Env = environment;
|
||||
NodeModel = nodeModel;
|
||||
}
|
||||
|
||||
Task<object> IScriptFlowApi.CallNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetDataOfParams(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetDataOfParams(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetFlowData()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
object IScriptFlowApi.GetGlobalData(string keyName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
|
||||
<ProjectReference Include="..\Library.Framework\Serein.Library.Framework.csproj" />
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
<ProjectReference Include="..\Serein.Script\Serein.Script.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user