diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 81da808..2b45879 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -714,14 +714,7 @@ namespace Serein.Library.Api event EnvOutHandler OnEnvOut; #endregion - #region 流程接口 - - /// - /// 设置输出 - /// - // - // - ///void SetConsoleOut(); // Action output, Action clearMsg + #region 基本接口 /// /// 输出信息 @@ -730,21 +723,6 @@ namespace Serein.Library.Api /// void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial); - ///// - ///// 使用JSON处理库输出对象信息 - ///// - ///// - //void WriteLineObjToJson(object obj); - - /// - /// 启动远程服务 - /// - Task StartRemoteServerAsync(int port = 7525); - - /// - /// 停止远程服务 - /// - void StopRemoteServer(); /// /// 加载项目文件 @@ -764,47 +742,60 @@ namespace Serein.Library.Api /// Task GetProjectInfoAsync(); + /// + /// 从节点信息集合批量加载节点控件 + /// + /// 节点集合信息 + /// + Task LoadNodeInfosAsync(List nodeInfos); + + + #endregion + + #region 远程相关 + /// + /// 启动远程服务 + /// + Task StartRemoteServerAsync(int port = 7525); + + /// + /// 停止远程服务 + /// + void StopRemoteServer(); + + /// + /// (适用于远程连接后获取环境的运行状态)获取当前环境的信息 + /// + /// + Task GetEnvInfoAsync(); + /// /// 加载远程环境 /// /// 远程环境地址 /// 远程环境端口 /// 密码 - Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres,int port, string token); + Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token); /// /// 退出远程环境 /// void ExitRemoteEnv(); - /// - /// 从文件中加载Dll - /// - /// - void LoadLibrary(string dllPath); + /// - /// 移除DLL + /// (用于远程)通知节点属性变更 /// - /// 程序集的名称 - bool TryUnloadLibrary(string assemblyFullName); - - /// - /// 开始运行 - /// - Task StartFlowAsync(); - - /// - /// 从选定的节点开始运行 - /// - /// + /// 节点Guid + /// 属性路径 + /// 属性值 /// - Task StartAsyncInSelectNode(string startNodeGuid); + Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value); - /// - /// 结束运行 - /// - Task ExitFlowAsync(); + #endregion + + #region 流程节点操作接口 /// /// 移动了某个节点(远程插件使用) @@ -851,12 +842,7 @@ namespace Serein.Library.Api ConnectionArgSourceType argSourceType, int argIndex); - /// - /// 从节点信息集合批量加载节点控件 - /// - /// 节点集合信息 - /// - Task LoadNodeInfosAsync(List nodeInfos); + /// /// 创建节点 @@ -866,15 +852,6 @@ namespace Serein.Library.Api /// 节点绑定的方法说明 Task CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null); - ///// - ///// 将节点放置在容器中/从容器中取出 - ///// - ///// 子节点(主要节点) - ///// 父节点 - ///// 是否组合(反之为分解节点组合关系) - ///// - //Task ChangeNodeContainerChildAsync(string childNodeGuid,string parentNodeGuid,bool isAssembly); - /// /// 将节点放置在容器中 /// @@ -921,23 +898,21 @@ namespace Serein.Library.Api Task RemoveNodeAsync(string nodeGuid); /// - /// 激活未启动的全局触发器 + /// 改变可选参数的数目 /// - /// - void ActivateFlipflopNode(string nodeGuid); - - /// - /// 终结一个全局触发器,在它触发后将不会再次监听消息(表现为已经启动的触发器至少会再次处理一次消息,后面版本再修正这个非预期行为) - /// - /// - void TerminateFlipflopNode(string nodeGuid); + /// 对应的节点Guid + /// true,增加参数;false,减少参数 + /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) + /// + Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex); + #endregion #region 节点中断、表达式 #if false -/// + /// /// 设置节点中断 /// /// 更改中断状态的节点Guid @@ -978,24 +953,7 @@ namespace Serein.Library.Api #endif #endregion - /// - /// (用于远程)通知节点属性变更 - /// - /// 节点Guid - /// 属性路径 - /// 属性值 - /// - Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value); - - /// - /// 改变可选参数的数目 - /// - /// 对应的节点Guid - /// true,增加参数;false,减少参数 - /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) - /// - Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex); - + #region 流程运行相关 /// /// 获取方法描述信息 @@ -1016,17 +974,34 @@ namespace Serein.Library.Api bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del); - #region 远程相关 /// - /// (适用于远程连接后获取环境的运行状态)获取当前环境的信息 + /// 开始运行 /// + Task StartFlowAsync(); + + /// + /// 从选定的节点开始运行 + /// + /// /// - Task GetEnvInfoAsync(); - #endregion + Task StartAsyncInSelectNode(string startNodeGuid); - #endregion + /// + /// 结束运行 + /// + Task ExitFlowAsync(); - #region 启动器调用 + /// + /// 激活未启动的全局触发器 + /// + /// + void ActivateFlipflopNode(string nodeGuid); + + /// + /// 终结一个全局触发器,在它触发后将不会再次监听消息(表现为已经启动的触发器至少会再次处理一次消息,后面版本再修正这个非预期行为) + /// + /// + void TerminateFlipflopNode(string nodeGuid); /// /// 流程启动器调用,监视数据更新通知 @@ -1054,31 +1029,19 @@ namespace Serein.Library.Api #endregion - #region 流程运行时 - - #region 全局数据/方法信息 - + #region 类库依赖相关 /// - /// 添加或更新全局数据 + /// 从文件中加载Dll /// - /// 数据名称 - /// 数据集 - /// - object AddOrUpdateGlobalData(string keyName, object data); + /// + void LoadLibrary(string dllPath); /// - /// 获取全局数据 + /// 移除DLL /// - /// 数据名称 - /// - object GetGlobalData(string keyName); - - #endregion - - - #region 加载依赖 - + /// 程序集的名称 + bool TryUnloadLibrary(string assemblyFullName); /// /// 运行时加载 /// @@ -1093,7 +1056,6 @@ namespace Serein.Library.Api /// 是否递归加载 void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true); - #endregion #endregion #region UI视觉 diff --git a/Library/Api/IScriptFlowApi.cs b/Library/Api/IScriptFlowApi.cs index 27f48e9..0cc4e4a 100644 --- a/Library/Api/IScriptFlowApi.cs +++ b/Library/Api/IScriptFlowApi.cs @@ -12,7 +12,7 @@ namespace Serein.Library.Api public interface IScriptFlowApi { /// - /// 当前流程 + /// 当前流程运行环境 /// IFlowEnvironment Env { get; } /// @@ -20,34 +20,28 @@ namespace Serein.Library.Api /// NodeModelBase NodeModel { get; } - /// - /// 动态流程上下文 - /// - IDynamicContext Context { get; set; } - /// /// 根据索引从入参数据获取数据 /// + /// /// /// - object GetArgData(int index); + object GetArgData(IDynamicContext context, int index); /// - /// 根据入参名称从入参数据获取数据 + /// 获取流程当前传递的数据 /// - /// + /// /// - // object GetDataOfParams(string name); + object GetFlowData(IDynamicContext context); + /// /// 获取全局数据 /// /// /// object GetGlobalData(string keyName); - /// - /// 获取流程当前传递的数据 - /// - /// - object GetFlowData(); + + /// /// 立即调用某个节点并获取其返回值 /// diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs index b7491a1..5516b4b 100644 --- a/Library/Api/ISereinIoc.cs +++ b/Library/Api/ISereinIoc.cs @@ -35,6 +35,22 @@ namespace Serein.Library.Api /// ISereinIOC Register(params object[] parameters) where TImplementation : TService; + /// + /// 指定一个Key登记一个持久化的实例。 + /// + /// 登记使用的名称 + /// 实例对象 + /// 是否注册成功 + bool RegisterPersistennceInstance(string key, object instance); + + /// + /// 指定一个Key登记一个实例。 + /// + /// 登记使用的名称 + /// 实例对象 + /// 是否注册成功 + bool RegisterInstance(string key, object instance); + /// /// 获取类型的实例。如果需要获取的类型以“接口-实现类”的方式注册,请使用接口的类型。 /// @@ -53,14 +69,7 @@ namespace Serein.Library.Api /// T Get(string key); - /// - /// 指定一个Key登记一个实例。如果实例中需要注入的依赖项,需要将needInjectProperty设置为true。 - /// - /// 注入名称 - /// 实例对象 - /// 是否需要注入依赖项 - /// 是否注册成功 - bool CustomRegisterInstance(string key, object instance, bool needInjectProperty = true); + /// /// 创建实例并注入依赖项,不会注册到IOC容器中。 diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index 492aa70..87d5c4a 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -1,4 +1,6 @@ -using System; +using Newtonsoft.Json.Linq; +using Serein.Library.Utils; +using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; @@ -33,52 +35,67 @@ namespace Serein.Library private bool _isEnable = true; /// - /// 中断级别,暂时停止继续执行后继分支。 + /// 是否中断节点。 /// - //[PropertyInfo] - //private InterruptClass _interruptClass = InterruptClass.None; - - /// - /// 中断级别,暂时停止继续执行后继分支。 - /// - [PropertyInfo(IsNotification = true, CustomCodeAtEnd = "// NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);")] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);" + [PropertyInfo(IsNotification = true, CustomCodeAtEnd = "ChangeInterruptState(value);")] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);" private bool _isInterrupt = false; + } + + /// + /// 节点中断 + /// + public partial class NodeDebugSetting + { /// /// 取消中断的回调函数 /// - [PropertyInfo] - private Action _cancelInterruptCallback; + private Action _cancelInterrupt { get; set; } + /// + /// 取消中断 + /// + public Action CancelInterrupt => _cancelInterrupt; + /// + /// 中断节点 + /// + public Func _getInterruptTask; /// - /// 中断Task(用来中断) + /// 获取中断的Task /// - [PropertyInfo] - private Func _getInterruptTask; + public Func GetInterruptTask => _getInterruptTask; - } - - /// - /// 中断级别,暂时停止继续执行后继分支。 + /// 改变中断状态 /// - //public enum InterruptClass - //{ - // /// - // /// 不中断 - // /// - // None, - // /// - // /// 分支中断,中断进入当前节点的分支。 - // /// - // Branch, - // /// - // /// 全局中断,中断全局所有节点的运行。(暂未实现相关) - // /// - // Global, - //} + public void ChangeInterruptState(bool state) + { + if (state && _getInterruptTask is null) + { + // 设置获取中断的委托 + _getInterruptTask = () => NodeModel.Env.IOC.Get().WaitTriggerAsync(NodeModel.Guid); + } + else if (!state) + { + if (_getInterruptTask is null) + { + + } + else + { + // 设置解除中断的委托 + _cancelInterrupt = () => NodeModel.Env.IOC.Get().InvokeTrigger(NodeModel.Guid); + _cancelInterrupt.Invoke(); + _getInterruptTask = null; + } + + } + } + + } +} diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index fb8746d..5a14cf7 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -36,7 +36,6 @@ namespace Serein.Library } - /// /// 保存自定义信息 /// @@ -60,21 +59,11 @@ namespace Serein.Library /// public virtual void Remove() { - - } - - /// - /// 移除该节点 - /// - public virtual void RemoveFromEnv() - { - if (this.DebugSetting.CancelInterruptCallback != null) + if (this.DebugSetting.CancelInterrupt != null) { - this.DebugSetting.CancelInterruptCallback?.Invoke(); + this.DebugSetting.CancelInterrupt?.Invoke(); } - this.DebugSetting.GetInterruptTask = null; this.DebugSetting.NodeModel = null; - this.DebugSetting.CancelInterruptCallback = null; this.DebugSetting = null; foreach (var pd in this.MethodDetails.ParameterDetailss) { @@ -87,7 +76,7 @@ namespace Serein.Library pd.ArgDataSourceNodeGuid = null; pd.ExplicitTypeName = null; } - this.MethodDetails.ParameterDetailss = null; + this.MethodDetails.ParameterDetailss = null; this.MethodDetails.ActingInstance = null; this.MethodDetails.NodeModel = null; this.MethodDetails.ReturnType = null; @@ -131,7 +120,6 @@ namespace Serein.Library } } - /// /// 导出为节点信息 /// @@ -231,10 +219,6 @@ namespace Serein.Library } #endregion - #region 程序集更新,更新节点方法描述、以及所有入参描述的类型 - - #endregion - #region 调试中断 /// @@ -243,15 +227,13 @@ namespace Serein.Library public void CancelInterrupt() { this.DebugSetting.IsInterrupt = false; - DebugSetting.CancelInterruptCallback?.Invoke(); + DebugSetting.CancelInterrupt?.Invoke(); } #endregion #region 节点方法的执行 - - /// /// 是否应该退出执行 /// @@ -271,6 +253,7 @@ namespace Serein.Library { return true; } + // 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。 if (flowCts != null) { @@ -365,15 +348,21 @@ namespace Serein.Library /// 节点传回数据对象 public virtual async Task ExecutingAsync(IDynamicContext context) { - //if(context.NextOrientation == ConnectionInvokeType.IsError) - //{ - //} - #region 调试中断 - if (DebugSetting.IsInterrupt) // 执行触发检查是否需要中断 + #region 调试中断 + if(context.NextOrientation == ConnectionInvokeType.IsError) { - //var cancelType = await this.DebugSetting.GetInterruptTask(); // 等待中断结束 - await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支"); + } + + // 执行触发检查是否需要中断 + if (DebugSetting.IsInterrupt) + { + context.Env.TriggerInterrupt(Guid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); // 通知运行环境该节点中断了 + await DebugSetting.GetInterruptTask.Invoke(); + //await fit.WaitTriggerAsync(Guid); // 创建一个等待的中断任务 + SereinEnv.WriteLine(InfoType.INFO, $"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支"); + var flowCts = context.Env.IOC.Get(NodeStaticConfig.FlipFlopCtsName); + if (IsBradk(context, flowCts)) return null; // 流程已终止,取消后续的执行 } #endregion @@ -405,9 +394,10 @@ namespace Serein.Library { if (MethodDetails.ParameterDetailss.Length == 0) { - return new object[0];// md.ActingInstance + return new object[0]; // 无参数 } + #region 定义返回的参数数组 object[] args; Array paramsArgs = null; // 初始化可选参数 int paramsArgIndex = 0; // 可选参数下标,与 object[] paramsArgs 一起使用 @@ -423,13 +413,16 @@ namespace Serein.Library { // 不存在可选参数 args = new object[MethodDetails.ParameterDetailss.Length]; // 调用方法的入参数组 - } + } + #endregion + // 常规参数的获取 for (int i = 0; i < args.Length; i++) { var pd = MethodDetails.ParameterDetailss[i]; args[i] = await pd.ToMethodArgData(context); // 获取数据 } + // 可选参数的获取 if(MethodDetails.ParamsArgIndex >= 0) { for (int i = 0; i < paramsArgs.Length; i++) @@ -440,11 +433,13 @@ namespace Serein.Library } args[args.Length - 1] = paramsArgs; } - return args; } + + + /// /// 更新节点数据,并检查监视表达式是否生效 /// @@ -518,26 +513,6 @@ namespace Serein.Library //} } - ///// - ///// 释放对象 - ///// - //public void ReleaseFlowData() - //{ - // if (typeof(IDisposable).IsAssignableFrom(FlowData?.GetType()) && FlowData is IDisposable disposable) - // { - // disposable?.Dispose(); - // } - // this.FlowData = null; - //} - - ///// - ///// 获取节点数据 - ///// - ///// - //public object GetFlowData() - //{ - // return this.FlowData; - //} #endregion } diff --git a/Library/Utils/FlowInterruptTool.cs b/Library/Utils/FlowInterruptTool.cs new file mode 100644 index 0000000..60cb032 --- /dev/null +++ b/Library/Utils/FlowInterruptTool.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Subjects; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Utils +{ + /// + /// 流程运行中断工具 + /// + public class FlowInterruptTool + { + // 使用并发字典管理每个信号对应的广播列表 + private readonly ConcurrentDictionary> _subscribers = new ConcurrentDictionary>(); + + /// + /// 获取或创建指定信号的 Subject(消息广播者) + /// + /// 枚举信号标识符 + /// 对应的 Subject + private Subject GetOrCreateSubject(string signal) + { + return _subscribers.GetOrAdd(signal, _ => new Subject()); + } + + /// + /// 订阅指定信号的消息 + /// + /// 枚举信号标识符 + /// 订阅者 + /// 取消订阅的句柄 + private IDisposable Subscribe(string signal, Action action) + { + IObserver observer = new Observer(action); + var subject = GetOrCreateSubject(signal); + return subject.Subscribe(observer); // 返回取消订阅的句柄 + } + + /// + /// 等待触发 + /// + /// + /// + public async Task WaitTriggerAsync(string signal) + { + var taskCompletionSource = new TaskCompletionSource(); + var subscription = Subscribe(signal, taskCompletionSource.SetResult); + var result = await taskCompletionSource.Task; + subscription.Dispose(); // 取消订阅 + return result; + } + + + /// + /// 手动触发信号,并广播给所有订阅者 + /// + /// 枚举信号标识符 + /// 是否成功触发 + public bool InvokeTrigger(string signal) + { + if (_subscribers.TryGetValue(signal, out var subject)) + { + subject.OnNext(true); // 广播给所有订阅者 + subject.OnCompleted(); // 通知订阅结束 + return true; + } + return false; + } + /// + /// 取消所有任务 + /// + + public void CancelAllTrigger() + { + foreach (var subject in _subscribers.Values) + { + subject.OnCompleted(); // 通知所有订阅者结束 + } + _subscribers.Clear(); + } + } +} diff --git a/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs b/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs index 85c8919..d14778b 100644 --- a/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs +++ b/Library/Utils/FlowTrigger/ChannelFlowInterrupt.cs @@ -14,147 +14,7 @@ namespace Serein.Library.Utils { - /// - /// 信号触发器类,带有消息广播功能。 - /// 使用枚举作为标记,创建 - /// - public class ValueTaskFlowTrigger - { - // 使用并发字典管理每个信号对应的广播列表 - private readonly ConcurrentDictionary>> _subscribers = new ConcurrentDictionary>>(); - /// - /// 获取或创建指定信号的 Subject(消息广播者) - /// - /// 枚举信号标识符 - /// 对应的 Subject - private Subject> GetOrCreateSubject(TSignal signal) - { - return _subscribers.GetOrAdd(signal, _ => new Subject>()); - } - - /// - /// 订阅指定信号的消息 - /// - /// 枚举信号标识符 - /// 订阅者 - /// 取消订阅的句柄 - private IDisposable Subscribe(TSignal signal, Action> action) - { - IObserver> observer = new Observer>(action); - var subject = GetOrCreateSubject(signal); - return subject.Subscribe(observer); // 返回取消订阅的句柄 - } - - - - /// - /// 等待触发器并指定超时的时间 - /// - /// 返回值类型 - /// 等待信号 - /// 超时时间 - /// - public async ValueTask> WaitTriggerWithTimeoutAsync(TSignal signal, TimeSpan outTime) - { - var subject = GetOrCreateSubject(signal); - var cts = new CancellationTokenSource(); - - // 异步任务:超时后自动触发信号 - _ = Task.Run(async () => - { - try - { - await Task.Delay(outTime, cts.Token); - if (!cts.IsCancellationRequested) // 如果还没有被取消 - { - var outResult = new TriggerResult() - { - Type = TriggerDescription.Overtime - }; - subject.OnNext(outResult); // 广播给所有订阅者 - subject.OnCompleted(); // 通知订阅结束 - } - } - catch (OperationCanceledException) - { - // 超时任务被取消 - } - finally - { - cts?.Dispose(); // 确保 cts 被释放 - } - }, cts.Token); - var result = await WaitTriggerAsync(signal); // 返回一个可以超时触发的等待任务 - return result; - } - - /// - /// 等待触发 - /// - /// - /// - /// - public async ValueTask> WaitTriggerAsync(TSignal signal) - { - - var taskCompletionSource = new TaskCompletionSource>(); - var subscription = Subscribe(signal, taskCompletionSource.SetResult); - var result = await taskCompletionSource.Task; - subscription.Dispose(); // 取消订阅 - if (result.Value is TResult data) - { - return new TriggerResult() - { - Value = data, - Type = TriggerDescription.External, - }; - } - else - { - return new TriggerResult() - { - Type = TriggerDescription.TypeInconsistency, - }; - } - } - - - /// - /// 手动触发信号,并广播给所有订阅者 - /// - /// 触发类型 - /// 枚举信号标识符 - /// 传递的数据 - /// 是否成功触发 - public Task InvokeTriggerAsync(TSignal signal, TResult value) - { - if (_subscribers.TryGetValue(signal, out var subject)) - { - var result = new TriggerResult() - { - Type = TriggerDescription.External, - Value = value - }; - subject.OnNext(result); // 广播给所有订阅者 - subject.OnCompleted(); // 通知订阅结束 - return Task.FromResult(true); - } - return Task.FromResult(false); - } - /// - /// 取消所有任务 - /// - - public void CancelAllTrigger() - { - foreach (var subject in _subscribers.Values) - { - subject.OnCompleted(); // 通知所有订阅者结束 - } - _subscribers.Clear(); - } - } diff --git a/Library/Utils/FlowTrigger/TaskFlowTrigger.cs b/Library/Utils/FlowTrigger/TaskFlowTrigger.cs index 10c1fd8..100a6c2 100644 --- a/Library/Utils/FlowTrigger/TaskFlowTrigger.cs +++ b/Library/Utils/FlowTrigger/TaskFlowTrigger.cs @@ -23,7 +23,7 @@ namespace Serein.Library.Utils { // 使用并发字典管理每个信号对应的广播列表 private readonly ConcurrentDictionary>> _subscribers = new ConcurrentDictionary>>(); - private readonly TriggerResultPool _triggerResultPool = new TriggerResultPool(); + private readonly TriggerResultPool _triggerResultPool = new TriggerResultPool(); /// /// 获取或创建指定信号的 Subject(消息广播者) /// diff --git a/Library/Utils/FlowTrigger/TriggerResult.cs b/Library/Utils/FlowTrigger/TriggerResult.cs index 1b89f19..30c8a57 100644 --- a/Library/Utils/FlowTrigger/TriggerResult.cs +++ b/Library/Utils/FlowTrigger/TriggerResult.cs @@ -76,18 +76,18 @@ namespace Serein.Library.Utils /// 使用 ObjectPool 来复用 TriggerResult 对象 /// // 示例 TriggerResult 对象池 - public class TriggerResultPool + public class TriggerResultPool { - private readonly ConcurrentExpandingObjectPool> _objectPool; + private readonly ConcurrentExpandingObjectPool> _objectPool; public TriggerResultPool(int defaultCapacity = 30) { - _objectPool = new ConcurrentExpandingObjectPool>(defaultCapacity); + _objectPool = new ConcurrentExpandingObjectPool>(defaultCapacity); } - public TriggerResult Get() => _objectPool.Get(); + public TriggerResult Get() => _objectPool.Get(); - public void Return(TriggerResult result) => _objectPool.Return(result); + public void Return(TriggerResult result) => _objectPool.Return(result); } diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index a71bf4d..8642eff 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -107,12 +107,20 @@ namespace Serein.Library.Utils #region 通过名称记录或获取一个实例 /// - /// 指定key值注册一个已经实例化的实例对象 + /// 指定key值注册一个已经实例化的实例对象,并持久化储存 /// /// /// - /// - public bool CustomRegisterInstance(string key, object instance, bool needInjectProperty = true) + public bool RegisterInstance(string key, object instance) + { + return RegisterPersistennceInstance(key, instance); + } + /// + /// 指定key值注册一个已经实例化的实例对象,并持久化储存 + /// + /// + /// + public bool RegisterPersistennceInstance(string key, object instance) { // 不存在时才允许创建 if (_dependencies.ContainsKey(key)) @@ -120,11 +128,11 @@ namespace Serein.Library.Utils return false; } _dependencies.TryAdd(key, instance); - if (needInjectProperty) - { - InjectDependencies(instance); // 注入实例需要的依赖项 - } - InjectUnfinishedDependencies(key, instance); // 检查是否存在其它实例需要该类型 + //if (needInjectProperty) + //{ + // InjectDependencies(instance); // 注入实例需要的依赖项 + //} + //InjectUnfinishedDependencies(key, instance); // 检查是否存在其它实例需要该类型 OnIOCMembersChanged?.Invoke(new IOCMembersChangedEventArgs(key, instance)); return true; } diff --git a/Net462DllTest/Trigger/PrakingDevice.cs b/Net462DllTest/Trigger/PrakingDevice.cs index edf4539..a7f0701 100644 --- a/Net462DllTest/Trigger/PrakingDevice.cs +++ b/Net462DllTest/Trigger/PrakingDevice.cs @@ -1,6 +1,6 @@ using Net462DllTest.LogicControl; using Serein.Library; -using Serein.Library.Utils.FlowTrigger; +using Serein.Library.Utils; namespace Net462DllTest.Trigger { diff --git a/Net462DllTest/Trigger/SiemensPlcDevice.cs b/Net462DllTest/Trigger/SiemensPlcDevice.cs index 5d1b5f6..8d510e6 100644 --- a/Net462DllTest/Trigger/SiemensPlcDevice.cs +++ b/Net462DllTest/Trigger/SiemensPlcDevice.cs @@ -8,7 +8,6 @@ using Net462DllTest.Signal; using Net462DllTest.Utils; using Serein.Library; using Serein.Library.Utils; -using Serein.Library.Utils.FlowTrigger; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Net462DllTest/Trigger/ViewManagement.cs b/Net462DllTest/Trigger/ViewManagement.cs index 077c7c7..5238d7d 100644 --- a/Net462DllTest/Trigger/ViewManagement.cs +++ b/Net462DllTest/Trigger/ViewManagement.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Windows.Forms; using System.Windows.Threading; using Serein.Library.Utils; -using Serein.Library.Utils.FlowTrigger; namespace Net462DllTest.Trigger { diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index cb88d7d..23e4e23 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -69,6 +69,16 @@ namespace Serein.NodeFlow.Env NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点 NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点 #endregion + + #region 注册基本服务类 + PersistennceInstance.Add(typeof(FlowInterruptTool).FullName, new FlowInterruptTool()); // 缓存流程实例 + PersistennceInstance.Add(typeof(IFlowEnvironment).FullName, (FlowEnvironment)this); // 缓存流程实例 + PersistennceInstance.Add(typeof(ISereinIOC).FullName, this); // 缓存容器服务 + PersistennceInstance.Add(typeof(UIContextOperation).FullName, uiContextOperation); // 缓存封装好的UI线程上下文 + + ReRegisterPersistennceInstance(); + + #endregion } #region 远程管理 @@ -303,6 +313,11 @@ namespace Serein.NodeFlow.Env /// private readonly SereinIOC sereinIOC; + /// + /// 本地运行环境缓存的持久化实例 + /// + private Dictionary PersistennceInstance { get; } = new Dictionary(); + /// /// 环境加载的节点集合 /// Node Guid - Node Model @@ -376,34 +391,23 @@ namespace Serein.NodeFlow.Env /// public async Task StartFlowAsync() { - flowStarter = new FlowStarter(); + flowStarter ??= new FlowStarter(); var nodes = NodeModels.Values.ToList(); - - List initMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init); List loadMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading); List exitMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit); Dictionary> autoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(); - - IOC.Reset(); // 开始运行时清空ioc中注册的实例 - - IOC.Register(); // 注册脚本接口 - IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this); // 注册流程实例 - if (this.UIContextOperation is not null) - { - // 注册封装好的UI线程上下文 - IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false); - } - _ = Task.Run(async () => - { - await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods); - if (FlipFlopState == RunState.Completion) - { - await ExitFlowAsync(); // 未运行触发器时,才会调用结束方法 - } - }); + IOC.Reset(); + await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods); + //_ = Task.Run(async () => + //{ + // //if (FlipFlopState == RunState.Completion) + // //{ + // // await ExitFlowAsync(); // 未运行触发器时,才会调用结束方法 + // //} + //}); return true; @@ -466,6 +470,7 @@ namespace Serein.NodeFlow.Env { flowStarter?.Exit(); UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); + IOC.Reset(); flowStarter = null; GC.Collect(); return Task.FromResult(true); @@ -829,7 +834,10 @@ namespace Serein.NodeFlow.Env foreach (var toNodeGuid in item.toNodeGuids) { var toNodeModel = GuidToModel(toNodeGuid); - if (toNodeModel is null) continue; + if (toNodeModel is null) { + // 防御性代码,加载正常保存的项目文件不会进入这里 + continue; + }; var isSuccessful = ConnectInvokeOfNode(fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 } } @@ -1396,29 +1404,6 @@ namespace Serein.NodeFlow.Env #region 流程依赖类库的接口 - /// - /// 添加或更新全局数据 - /// - /// 数据名称 - /// 数据集 - /// - public object AddOrUpdateGlobalData(string keyName, object data) - { - SereinEnv.AddOrUpdateFlowGlobalData(keyName, data); - return data; - } - - /// - /// 获取全局数据 - /// - /// 数据名称 - /// - public object? GetGlobalData(string keyName) - { - return SereinEnv.GetFlowGlobalData(keyName); - } - - /// /// 运行时加载 /// @@ -1796,6 +1781,20 @@ namespace Serein.NodeFlow.Env //} + /// + /// 向容器登记缓存的持久化实例 + /// + private void ReRegisterPersistennceInstance() + { + lock (PersistennceInstance) + { + foreach (var kvp in PersistennceInstance) + { + IOC.RegisterPersistennceInstance(kvp.Key, kvp.Value); + } + } + } + #endregion #region 视觉效果 @@ -1819,6 +1818,7 @@ namespace Serein.NodeFlow.Env ISereinIOC ISereinIOC.Reset() { sereinIOC.Reset(); + ReRegisterPersistennceInstance(); // 重置后重新登记 return this; } @@ -1866,10 +1866,17 @@ namespace Serein.NodeFlow.Env } - bool ISereinIOC.CustomRegisterInstance(string key, object instance, bool needInjectProperty) + bool ISereinIOC.RegisterPersistennceInstance(string key, object instance) { - return sereinIOC.CustomRegisterInstance(key, instance, needInjectProperty); + PersistennceInstance.TryAdd(key, instance); // 记录需要持久化的实例 + return sereinIOC.RegisterPersistennceInstance(key, instance); } + + bool ISereinIOC.RegisterInstance(string key, object instance) + { + return sereinIOC.RegisterInstance(key, instance); + } + object ISereinIOC.Instantiate(Type type) { diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 3e10fee..2a8685a 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -544,27 +544,7 @@ namespace Serein.NodeFlow.Env } #region 流程依赖类库的接口 - /// - /// 添加或更新全局数据 - /// - /// 数据名称 - /// 数据集 - /// - public object AddOrUpdateGlobalData(string keyName, object data) - { - return currentFlowEnvironment.AddOrUpdateGlobalData(keyName, data); - } - - /// - /// 获取全局数据 - /// - /// 数据名称 - /// - public object GetGlobalData(string keyName) - { - return currentFlowEnvironment.GetGlobalData(keyName); - } - + /// /// 运行时加载 @@ -594,9 +574,14 @@ namespace Serein.NodeFlow.Env return IOC.Build(); } - public bool CustomRegisterInstance(string key, object instance, bool needInjectProperty = true) + public bool RegisterPersistennceInstance(string key, object instance) { - return IOC.CustomRegisterInstance(key, instance, needInjectProperty); + return IOC.RegisterPersistennceInstance(key, instance); + } + + public bool RegisterInstance(string key, object instance) + { + return IOC.RegisterInstance(key, instance); } public object Get(Type type) diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index 8893c6b..13878d6 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -1297,29 +1297,6 @@ namespace Serein.NodeFlow.Env - /// - /// 添加或更新全局数据 - /// - /// 数据名称 - /// 数据集 - /// - public object AddOrUpdateGlobalData(string keyName, object data) - { - this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:AddOrUpdateGlobalData"); - return null; - } - /// - /// 获取全局数据 - /// - /// 数据名称 - /// - public object GetGlobalData(string keyName) - { - this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:GetGlobalData"); - return null; - } - - #endregion diff --git a/NodeFlow/FlowInterruptTool.cs b/NodeFlow/FlowInterruptTool.cs new file mode 100644 index 0000000..f85b1e1 --- /dev/null +++ b/NodeFlow/FlowInterruptTool.cs @@ -0,0 +1,13 @@ +using Serein.Library.Utils; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Subjects; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.NodeFlow +{ + +} diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index f82a05d..4958779 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -15,9 +15,8 @@ namespace Serein.NodeFlow /// public class FlowStarter { - /// - /// 控制全局触发器的结束 + /// 控制所有全局触发器的结束 /// private CancellationTokenSource? _flipFlopCts; @@ -31,15 +30,6 @@ namespace Serein.NodeFlow /// private Func? ExitAction { get; set; } - private void CheckStartState() - { - if (IsStopStart) - { - throw new Exception("停止启动"); - - } - } - /// /// 从选定的节点开始运行 /// @@ -76,6 +66,10 @@ namespace Serein.NodeFlow List exitMethods) { + #region 注册基本类 + env.IOC.Register(); // 注册脚本接口 + #endregion + env.FlowState = RunState.Running; // 开始运行 NodeModelBase? startNode = nodes.FirstOrDefault(node => node.IsStart); if (startNode is null) { @@ -116,13 +110,12 @@ namespace Serein.NodeFlow thisRuningMds.AddRange(loadingMethods.Where(md => md?.ActingInstanceType is not null)); thisRuningMds.AddRange(exitMethods.Where(md => md?.ActingInstanceType is not null)); - // .AddRange(initMethods).AddRange(loadingMethods).a + foreach (var nodeMd in thisRuningMds) { nodeMd.ActingInstance = null; } - env.IOC.CustomRegisterInstance(typeof(ISereinIOC).FullName, env); // 初始化ioc容器中的类型对象 foreach (var md in thisRuningMds) { @@ -206,12 +199,12 @@ namespace Serein.NodeFlow #region 设置流程退出时的回调函数 ExitAction = async () => { - env.IOC.Run(web => { - web?.Stop(); - }); - env.IOC.Run(server => { - server?.Stop(); - }); + //env.IOC.Run(web => { + // web?.Stop(); + //}); + //env.IOC.Run(server => { + // server?.Stop(); + //}); foreach (MethodDetails? md in exitMethods) { @@ -230,7 +223,7 @@ namespace Serein.NodeFlow TerminateAllGlobalFlipflop(); // 确保所有触发器不再运行 SereinEnv.ClearFlowGlobalData(); // 清空全局数据缓存 NativeDllHelper.FreeLibrarys(); // 卸载所有已加载的 Native Dll - + env.IOC.Run(fit => fit.CancelAllTrigger());// 取消所有中断 env.FlowState = RunState.Completion; env.FlipFlopState = RunState.Completion; @@ -249,7 +242,7 @@ namespace Serein.NodeFlow env.FlipFlopState = RunState.Running; // 如果存在需要启动的触发器,则开始启动 _flipFlopCts = new CancellationTokenSource(); - env.IOC.CustomRegisterInstance(NodeStaticConfig.FlipFlopCtsName, _flipFlopCts,false); + env.IOC.RegisterInstance(NodeStaticConfig.FlipFlopCtsName, _flipFlopCts); // 使用 TaskCompletionSource 创建未启动的触发器任务 var tasks = flipflopNodes.Select(async node => @@ -281,39 +274,6 @@ 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 dictGlobalFlipflop = []; /// @@ -403,7 +363,7 @@ namespace Serein.NodeFlow context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 { - await nextNodes[i].DebugSetting.GetInterruptTask(); + await nextNodes[i].DebugSetting.GetInterruptTask.Invoke(); await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支"); } await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 @@ -421,7 +381,7 @@ namespace Serein.NodeFlow context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 { - await nextNodes[i].DebugSetting.GetInterruptTask(); + await nextNodes[i].DebugSetting.GetInterruptTask.Invoke(); await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支"); } await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 @@ -448,12 +408,14 @@ namespace Serein.NodeFlow } - + /// + /// 结束流程 + /// public void Exit() { ExitAction?.Invoke(); - } + } } } diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 1f7c764..a8cc1ff 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -27,7 +27,7 @@ namespace Serein.NodeFlow.Model if (DebugSetting.IsInterrupt) // 执行触发前 { string guid = this.Guid.ToString(); - await this.DebugSetting.GetInterruptTask(); + await this.DebugSetting.GetInterruptTask.Invoke(); await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已取消,开始执行后继分支"); } #endregion diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index b1ab425..e0bd450 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -141,12 +141,23 @@ namespace Serein.NodeFlow.Model public override async Task ExecutingAsync(IDynamicContext context) { var @params = await GetParametersAsync(context); - ScriptFlowApi.Context= context; + //dynamic obj = ((object[])@params[0])[0]; + //try + //{ + // SereinEnv.WriteLine(InfoType.INFO, "Dynamic Object Value :" + obj.VarInfo); + //} + //catch (Exception ex) + //{ + // SereinEnv.WriteLine(ex); + //} + //ScriptFlowApi.Context = context; // 并发破坏了数据状态 context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改 mainNode ??= new SereinScriptParser(Script).Parse(); - IScriptInvokeContext scriptContext = new ScriptInvokeContext(); + IScriptInvokeContext scriptContext = new ScriptInvokeContext(context); + var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行 + //SereinEnv.WriteLine(InfoType.INFO, "FlowContext Guid : " + context.Guid); return result; } diff --git a/NodeFlow/ScriptFlowApi.cs b/NodeFlow/ScriptFlowApi.cs index 79684df..c6bf14f 100644 --- a/NodeFlow/ScriptFlowApi.cs +++ b/NodeFlow/ScriptFlowApi.cs @@ -24,11 +24,7 @@ namespace Serein.NodeFlow /// public NodeModelBase NodeModel { get; private set; } - public IDynamicContext? Context{ get; set; } - - private string _paramsKey => $"{Context?.Guid}_{NodeModel.Guid}_Params"; - - + /// /// 创建流程脚本接口 @@ -46,9 +42,10 @@ namespace Serein.NodeFlow throw new NotImplementedException(); } - public object? GetArgData(int index) + public object? GetArgData(IDynamicContext context, int index) { - var obj = Context?.GetFlowData(_paramsKey); + var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params"; + var obj = context?.GetFlowData(_paramsKey); if (obj is object[] @params && index < @params.Length) { return @params[index]; @@ -57,9 +54,9 @@ namespace Serein.NodeFlow } - public object? GetFlowData() + public object? GetFlowData(IDynamicContext context) { - return Context?.GetFlowData(NodeModel.Guid); + return context?.GetFlowData(NodeModel.Guid); } public object? GetGlobalData(string keyName) diff --git a/Serein.Script/SereinScriptInterpreter.cs b/Serein.Script/SereinScriptInterpreter.cs index 15723e7..a1ecace 100644 --- a/Serein.Script/SereinScriptInterpreter.cs +++ b/Serein.Script/SereinScriptInterpreter.cs @@ -26,6 +26,7 @@ namespace Serein.Script /// public interface IScriptInvokeContext { + IDynamicContext FlowContext { get; } /// /// 是否该退出了 /// @@ -61,6 +62,13 @@ namespace Serein.Script public class ScriptInvokeContext : IScriptInvokeContext { + public ScriptInvokeContext(IDynamicContext dynamicContext) + { + FlowContext = dynamicContext; + } + + public IDynamicContext FlowContext{ get; } + /// /// 定义的变量 /// @@ -76,6 +84,7 @@ namespace Serein.Script /// public bool IsCheckNullValue { get; set; } + object IScriptInvokeContext.GetVarValue(string varName) { _variables.TryGetValue(varName, out var value); @@ -309,6 +318,11 @@ namespace Serein.Script } private async Task InterpretFunctionCallAsync(IScriptInvokeContext context, FunctionCallNode functionCallNode) { + if (functionCallNode.FunctionName.Equals("GetFlowContext", StringComparison.OrdinalIgnoreCase)) + { + return context.FlowContext; + } + // 评估函数参数 var arguments = new object?[functionCallNode.Arguments.Count]; for (int i = 0; i < functionCallNode.Arguments.Count; i++) @@ -321,6 +335,7 @@ namespace Serein.Script object? instance = null; // 静态方法不需要传入实例,所以可以传入null + // 查找并执行对应的函数 if (_functionTable.TryGetValue(funcName, out DelegateDetails? function)) { diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index e8b4004..a90c015 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -1,36 +1,30 @@ using Newtonsoft.Json; using Serein.Library; +using Serein.Library.Utils; +using System.Diagnostics; using System.IO; using System.Windows; namespace Serein.Workbench { -#if DEBUG - public class People - { - public string Name { get; set; } - public int Id { get; set; } - public int Age { get; set; } - } -#endif - - /// /// Interaction logic for App.xaml /// public partial class App : Application { - void LoadLocalProject() + private async Task LoadLocalProjectAsync() { + #if DEBUG if (1 == 1) { - // 这里是我自己的测试代码,你可以删除 + // 这里是测试代码,可以删除 string filePath; filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\net8.0\PLCproject.dnf"; filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf"; filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf"; + //filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\test.dnf"; string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 App.FlowProjectData = JsonConvert.DeserializeObject(content); App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// @@ -42,7 +36,7 @@ namespace Serein.Workbench public static SereinProjectData? FlowProjectData { get; set; } public static string FileDataPath { get; set; } = ""; - private void Application_Startup(object sender, StartupEventArgs e) + private async void Application_Startup(object sender, StartupEventArgs e) { // 检查是否传入了参数 if (e.Args.Length == 1) @@ -71,7 +65,7 @@ namespace Serein.Workbench } } - this.LoadLocalProject(); + await this.LoadLocalProjectAsync(); } diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index e4833cd..af87f32 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -2904,11 +2904,7 @@ namespace Serein.Workbench //var t = guids.Select(kvp => (kvp.Key, kvp.Value)).ToArray(); //var result = flashText.ReplaceWords(jsonText, t); - - - - - StringBuilder sb = new StringBuilder(jsonText); + StringBuilder sb = new StringBuilder(jsonText); foreach (var kv in guids) { sb.Replace(kv.Key, kv.Value);