示例工程版本提升至net462,项目添加了部分空引用检测逻辑。累了,消不完的空引用警告(T.T)

This commit is contained in:
fengjiayi
2024-09-30 22:20:02 +08:00
parent 8ecbdfa7a6
commit e4aa3b6185
45 changed files with 562 additions and 581 deletions

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Serein.Library.Framework</RootNamespace> <RootNamespace>Serein.Library.Framework</RootNamespace>
<AssemblyName>Serein.Library.Framework</AssemblyName> <AssemblyName>Serein.Library.Framework</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<TargetFrameworkProfile /> <TargetFrameworkProfile />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks> <TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath> <BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
</PropertyGroup> </PropertyGroup>

View File

@@ -7,196 +7,193 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils namespace Serein.Library.Utils
{ {
/// <summary> /// <summary>
/// 流程中断管理 /// 流程中断管理
/// </summary> /// </summary>
public class ChannelFlowInterrupt public class ChannelFlowInterrupt
{
/// <summary>
/// 中断取消类型
/// </summary>
public enum CancelType
{
Manual,
Error,
Overtime
}
// 使用并发字典管理每个信号对应的 Channel
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{ {
try /// <summary>
/// 中断取消类型
/// </summary>
public enum CancelType
{ {
await Task.Delay(outTime, cts.Token); Manual,
if (!cts.Token.IsCancellationRequested) Error,
Overtime
}
// 使用并发字典管理每个信号对应的 Channel
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns>
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{ {
await channel.Writer.WriteAsync(CancelType.Overtime); try
{
await Task.Delay(outTime, cts.Token);
if (!cts.Token.IsCancellationRequested)
{
await channel.Writer.WriteAsync(CancelType.Overtime);
}
}
catch (OperationCanceledException)
{
// 超时任务被取消
}
finally
{
cts?.Dispose();
}
}, cts.Token);
// 等待信号传入(超时或手动触发)
try
{
var result = await channel.Reader.ReadAsync();
return result;
} }
catch
{
return CancelType.Error;
}
} }
catch (OperationCanceledException)
{
// 超时任务被取消
}
finally
{
cts?.Dispose();
}
}, cts.Token);
// 等待信号传入(超时或手动触发)
try
{
var result = await channel.Reader.ReadAsync();
return result;
}
catch
{
return CancelType.Error;
}
}
/// <summary> /// <summary>
/// 创建信号,直到手动触发(异步方法) /// 创建信号,直到手动触发(异步方法)
/// </summary> /// </summary>
/// <param name="signal">信号标识符</param> /// <param name="signal">信号标识符</param>
/// <param name="outTime">超时时间</param> /// <param name="outTime">超时时间</param>
/// <returns>等待任务</returns> /// <returns>等待任务</returns>
public async Task<CancelType> GetOrCreateChannelAsync(string signal) public async Task<CancelType> GetOrCreateChannelAsync(string signal)
{
try
{
var channel = GetOrCreateChannel(signal);
// 等待信号传入(超时或手动触发)
var result = await channel.Reader.ReadAsync();
return result;
}
catch
{
return CancelType.Manual;
}
}
/// <summary>
/// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
/// </summary>
/// <param name="signal">信号标识符</param>
/// <param name="timeout">超时时间</param>
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
{
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{ {
try try
{ {
await Task.Delay(timeout, token); var channel = GetOrCreateChannel(signal);
await channel.Writer.WriteAsync(CancelType.Overtime); // 等待信号传入(超时或手动触发)
var result = await channel.Reader.ReadAsync();
return result;
} }
catch (OperationCanceledException ex) catch
{ {
// 任务被取消 return CancelType.Manual;
await Console.Out.WriteLineAsync(ex.Message);
} }
});
// 同步阻塞直到信号触发或超时
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
return result;
}
/// <summary>
/// 触发信号
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>是否成功触发</returns>
public bool TriggerSignal(string signal)
{
//if (_channels.TryGetValue(signal, out var channel))
//{
// // 手动触发信号
// channel.Writer.TryWrite(CancelType.Manual);
// return true;
//}
//return false;
try
{
if (_channels.TryGetValue(signal, out var channel))
{
// 手动触发信号
channel.Writer.TryWrite(CancelType.Manual);
// 完成写入,标记该信号通道不再接受新写入
channel.Writer.Complete();
// 触发后移除信号
_channels.TryRemove(signal, out _);
return true;
} }
return false;
}
catch
{
return false; /// <summary>
} /// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
/// </summary>
} /// <param name="signal">信号标识符</param>
/// <param name="timeout">超时时间</param>
/// <summary> public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
/// 取消所有任务
/// </summary>
public void CancelAllTasks()
{
foreach (var channel in _channels.Values)
{
try
{
channel.Writer.Complete();
}
finally
{ {
var channel = GetOrCreateChannel(signal);
var cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// 异步任务:超时后自动触发信号
_ = Task.Run(async () =>
{
try
{
await Task.Delay(timeout, token);
await channel.Writer.WriteAsync(CancelType.Overtime);
}
catch (OperationCanceledException ex)
{
// 任务被取消
await Console.Out.WriteLineAsync(ex.Message);
}
});
// 同步阻塞直到信号触发或超时
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
return result;
} }
}
_channels.Clear();
}
/// <summary> /// <summary>
/// 获取或创建指定信号的 Channel /// 触发信号
/// </summary> /// </summary>
/// <param name="signal">信号字符串</param> /// <param name="signal">信号字符串</param>
/// <returns>对应的 Channel</returns> /// <returns>是否成功触发</returns>
private Channel<CancelType> GetOrCreateChannel(string signal) public bool TriggerSignal(string signal)
{ {
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>()); //if (_channels.TryGetValue(signal, out var channel))
} //{
} // // 手动触发信号
// channel.Writer.TryWrite(CancelType.Manual);
// return true;
//}
//return false;
try
{
if (_channels.TryGetValue(signal, out var channel))
{
// 手动触发信号
channel.Writer.TryWrite(CancelType.Manual);
// 完成写入,标记该信号通道不再接受新写入
channel.Writer.Complete();
// 触发后移除信号
_channels.TryRemove(signal, out _);
return true;
}
return false;
}
catch
{
return false;
}
}
/// <summary>
/// 取消所有任务
/// </summary>
public void CancelAllTasks()
{
foreach (var channel in _channels.Values)
{
try
{
channel.Writer.Complete();
}
finally
{
}
}
_channels.Clear();
}
/// <summary>
/// 获取或创建指定信号的 Channel
/// </summary>
/// <param name="signal">信号字符串</param>
/// <returns>对应的 Channel</returns>
private Channel<CancelType> GetOrCreateChannel(string signal)
{
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>());
}
}
} }
#endregion #endregion

View File

@@ -345,6 +345,7 @@ namespace Serein.Library.Web
} }
catch (JsonReaderException ex) catch (JsonReaderException ex)
{ {
Console.WriteLine(ex);
return value; return value;
} }
catch (JsonSerializationException ex) catch (JsonSerializationException ex)

View File

@@ -1,4 +1,4 @@
using Net461DllTest.LogicControl; using Net462DllTest.LogicControl;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.NodeFlow.Tool; using Serein.Library.NodeFlow.Tool;
using System; using System;
@@ -7,7 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.Device namespace Net462DllTest.Device
{ {
[AutoRegister] [AutoRegister]
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand> public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>

View File

@@ -1,13 +1,13 @@
using IoTClient; using IoTClient;
using IoTClient.Clients.PLC; using IoTClient.Clients.PLC;
using IoTClient.Enums; using IoTClient.Enums;
using Net461DllTest.Enums; using Net462DllTest.Enums;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.NodeFlow.Tool; using Serein.Library.NodeFlow.Tool;
using System; using System;
namespace Net461DllTest.Device namespace Net462DllTest.Device
{ {
/// <summary> /// <summary>

View File

@@ -1,4 +1,4 @@
using Net461DllTest.View; using Net462DllTest.View;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -6,7 +6,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.Signal namespace Net462DllTest.Signal
{ {
public enum FromValue public enum FromValue
{ {

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.Enums namespace Net462DllTest.Enums
{ {
public enum PlcState public enum PlcState
{ {

View File

@@ -1,12 +1,12 @@
using Net461DllTest.Signal; using Net462DllTest.Signal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Net461DllTest.Signal.PlcValueAttribute; using static Net462DllTest.Signal.PlcValueAttribute;
namespace Net461DllTest.Enums namespace Net462DllTest.Enums
{ {

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Net461DllTest.ViewModel; using Net462DllTest.ViewModel;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Enums; using Serein.Library.Enums;
@@ -13,7 +13,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.LogicControl namespace Net462DllTest.LogicControl
{ {
public enum ParkingCommand public enum ParkingCommand

View File

@@ -1,9 +1,9 @@
using IoTClient.Clients.PLC; using IoTClient.Clients.PLC;
using IoTClient.Common.Enums; using IoTClient.Common.Enums;
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Enums; using Net462DllTest.Enums;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Net461DllTest.Web; using Net462DllTest.Web;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Enums; using Serein.Library.Enums;
@@ -18,7 +18,7 @@ using System.ComponentModel;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.LogicControl namespace Net462DllTest.LogicControl
{ {
[AutoRegister] [AutoRegister]
[DynamicFlow] [DynamicFlow]

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Net461DllTest.ViewModel; using Net462DllTest.ViewModel;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Enums; using Serein.Library.Enums;
@@ -10,7 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
namespace Net461DllTest.LogicControl namespace Net462DllTest.LogicControl
{ {
/// <summary> /// <summary>

View File

@@ -6,9 +6,9 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid> <ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>Net461DllTest</RootNamespace> <RootNamespace>Net462DllTest</RootNamespace>
<AssemblyName>Net461DllTest</AssemblyName> <AssemblyName>Net462DllTest</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>

View File

@@ -1,4 +1,4 @@
namespace Net461DllTest.Signal namespace Net462DllTest.Signal
{ {
public enum CommandSignal public enum CommandSignal
{ {

View File

@@ -1,8 +1,8 @@
using Serein.Library.Attributes; using Serein.Library.Attributes;
using System; using System;
using static Net461DllTest.Signal.PlcValueAttribute; using static Net462DllTest.Signal.PlcValueAttribute;
namespace Net461DllTest.Signal namespace Net462DllTest.Signal
{ {
[AttributeUsage(AttributeTargets.Field)] [AttributeUsage(AttributeTargets.Field)]

View File

@@ -1,7 +1,7 @@
using IoTClient; using IoTClient;
using IoTClient.Clients.PLC; using IoTClient.Clients.PLC;
using IoTClient.Enums; using IoTClient.Enums;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.Utils namespace Net462DllTest.Utils
{ {
internal static class MyPlcExtension internal static class MyPlcExtension
{ {

View File

@@ -1,4 +1,4 @@
namespace Net461DllTest namespace Net462DllTest
{ {
partial class FromWorkBenchView partial class FromWorkBenchView
{ {

View File

@@ -1,6 +1,6 @@
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Net461DllTest.ViewModel; using Net462DllTest.ViewModel;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using System; using System;
@@ -13,7 +13,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace Net461DllTest namespace Net462DllTest
{ {
public partial class FromWorkBenchView : Form public partial class FromWorkBenchView : Form
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Net461DllTest.View namespace Net462DllTest.View
{ {
partial class TestFormView partial class TestFormView
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
namespace Net461DllTest.View namespace Net462DllTest.View
{ {
public partial class TestFormView : Form public partial class TestFormView : Form
{ {

View File

@@ -1,5 +1,5 @@
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -7,7 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.ViewModel namespace Net462DllTest.ViewModel
{ {
public class FromWorkBenchViewModel public class FromWorkBenchViewModel
{ {

View File

@@ -1,5 +1,5 @@
using Net461DllTest.Device; using Net462DllTest.Device;
using Net461DllTest.Signal; using Net462DllTest.Signal;
using Serein.Library.Attributes; using Serein.Library.Attributes;
using Serein.Library.Web; using Serein.Library.Web;
using System; using System;
@@ -8,7 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Net461DllTest.Web namespace Net462DllTest.Web
{ {
[AutoHosting] [AutoHosting]
public class CommandController : ControllerBase public class CommandController : ControllerBase

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>

View File

@@ -41,12 +41,12 @@ namespace Serein.NodeFlow.Base
/// <summary> /// <summary>
/// 节点guid /// 节点guid
/// </summary> /// </summary>
public string Guid { get; set; } public string Guid { get; set; } = string.Empty;
/// <summary> /// <summary>
/// 显示名称 /// 显示名称
/// </summary> /// </summary>
public string DisplayName { get; set; } public string DisplayName { get; set; } = string.Empty;
/// <summary> /// <summary>
/// 是否为起点控件 /// 是否为起点控件
@@ -76,7 +76,7 @@ namespace Serein.NodeFlow.Base
/// <summary> /// <summary>
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
/// </summary> /// </summary>
public Exception RuningException { get; set; } = null; public Exception? RuningException { get; set; } = null;
/// <summary> /// <summary>

View File

@@ -74,17 +74,17 @@ namespace Serein.NodeFlow.Base
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo) internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
{ {
var node = this; this.Guid = nodeInfo.Guid;
if (node != null) if(this.MethodDetails is not null)
{ {
node.Guid = nodeInfo.Guid;
for (int i = 0; i < nodeInfo.ParameterData.Length; i++) for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
{ {
Parameterdata? pd = nodeInfo.ParameterData[i]; Parameterdata? pd = nodeInfo.ParameterData[i];
node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State; this.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
node.MethodDetails.ExplicitDatas[i].DataValue = pd.Value; this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
} }
} }
return this; return this;
} }
@@ -132,7 +132,7 @@ namespace Serein.NodeFlow.Base
if (upstreamNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 if (upstreamNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
{ {
var cancelType = await upstreamNodes[i].DebugSetting.GetInterruptTask(); var cancelType = await upstreamNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{upstreamNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); await Console.Out.WriteLineAsync($"[{upstreamNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
} }
upstreamNodes[i].PreviousNode = currentNode; upstreamNodes[i].PreviousNode = currentNode;
await upstreamNodes[i].StartExecute(context); // 执行流程节点的上游分支 await upstreamNodes[i].StartExecute(context); // 执行流程节点的上游分支
@@ -165,7 +165,7 @@ namespace Serein.NodeFlow.Base
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前 if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
{ {
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); await Console.Out.WriteLineAsync($"[{nextNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
} }
nextNodes[i].PreviousNode = currentNode; nextNodes[i].PreviousNode = currentNode;
stack.Push(nextNodes[i]); stack.Push(nextNodes[i]);
@@ -194,18 +194,22 @@ namespace Serein.NodeFlow.Base
// this.NextOrientation = ConnectionType.None; // this.NextOrientation = ConnectionType.None;
// return null; // return null;
//} //}
await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
} }
#endregion #endregion
MethodDetails md = MethodDetails; MethodDetails? md = MethodDetails;
//var del = md.MethodDelegate.Clone(); //var del = md.MethodDelegate.Clone();
if (md is null)
{
throw new Exception($"节点{this.Guid}不存在方法信息请检查是否需要重写节点的ExecutingAsync");
}
if (!context.Env.TryGetDelegate(md.MethodName, out var del)) if (!context.Env.TryGetDelegate(md.MethodName, out var del))
{ {
throw new Exception("不存在对应委托"); throw new Exception($"节点{this.Guid}不存在对应委托");
} }
md.ActingInstance ??= context.Env.IOC.Get(MethodDetails.ActingInstanceType); md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
object instance = md.ActingInstance; object instance = md.ActingInstance;
var haveParameter = md.ExplicitDatas.Length > 0; var haveParameter = md.ExplicitDatas.Length > 0;
@@ -281,7 +285,7 @@ namespace Serein.NodeFlow.Base
if (ed.IsExplicitData) // 判断是否使用显示的输入参数 if (ed.IsExplicitData) // 判断是否使用显示的输入参数
{ {
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && flowData is not null)
{ {
// 执行表达式从上一节点获取对象 // 执行表达式从上一节点获取对象
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _); inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
@@ -315,8 +319,6 @@ namespace Serein.NodeFlow.Base
} }
} }
//var attribute = ed.DataType.GetCustomAttribute<EnumTypeConvertorAttribute>();
//if (attribute is not null && attribute.EnumType.IsEnum) // 获取枚举转换器中记录的枚举
if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举 if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举
{ {
if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项 if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
@@ -339,41 +341,43 @@ namespace Serein.NodeFlow.Base
try try
{ {
string? valueStr = inputParameter?.ToString();
parameters[i] = ed.DataType switch parameters[i] = ed.DataType switch
{ {
//Type t when t == previousDataType => inputParameter, // 上下文
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
Type t when t == typeof(IDynamicContext) => context, // 上下文 Type t when t == typeof(IDynamicContext) => context, // 上下文
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
Type t when t == typeof(string) => inputParameter?.ToString(),
Type t when t == typeof(char) && !string.IsNullOrEmpty(valueStr) => char.Parse(valueStr),
Type t when t == typeof(bool) && !string.IsNullOrEmpty(valueStr) => inputParameter is not null && bool.Parse(valueStr),
Type t when t == typeof(float) && !string.IsNullOrEmpty(valueStr) => float.Parse(valueStr),
Type t when t == typeof(decimal) && !string.IsNullOrEmpty(valueStr) => decimal.Parse(valueStr),
Type t when t == typeof(double) && !string.IsNullOrEmpty(valueStr) => double.Parse(valueStr),
Type t when t == typeof(sbyte) && !string.IsNullOrEmpty(valueStr) => sbyte.Parse(valueStr),
Type t when t == typeof(byte) && !string.IsNullOrEmpty(valueStr) => byte.Parse(valueStr),
Type t when t == typeof(short) && !string.IsNullOrEmpty(valueStr) => short.Parse(valueStr),
Type t when t == typeof(ushort) && !string.IsNullOrEmpty(valueStr) => ushort.Parse(valueStr),
Type t when t == typeof(int) && !string.IsNullOrEmpty(valueStr) => int.Parse(valueStr),
Type t when t == typeof(uint) && !string.IsNullOrEmpty(valueStr) => uint.Parse(valueStr),
Type t when t == typeof(long) && !string.IsNullOrEmpty(valueStr) => long.Parse(valueStr),
Type t when t == typeof(ulong) && !string.IsNullOrEmpty(valueStr) => ulong.Parse(valueStr),
Type t when t == typeof(nint) && !string.IsNullOrEmpty(valueStr) => nint.Parse(valueStr),
Type t when t == typeof(nuint) && !string.IsNullOrEmpty(valueStr) => nuint.Parse(valueStr),
//Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
Type t when t == typeof(MethodDetails) => md, // 节点方法描述 Type t when t == typeof(MethodDetails) => md, // 节点方法描述
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类 Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
Type t when t == typeof(Guid) => new Guid(inputParameter?.ToString()),
Type t when t == typeof(DateTime) => DateTime.Parse(inputParameter?.ToString()),
Type t when t == typeof(string) => inputParameter?.ToString(),
Type t when t == typeof(char) => char.Parse(inputParameter?.ToString()),
Type t when t == typeof(bool) => inputParameter is null ? false : bool.Parse(inputParameter?.ToString()),
Type t when t == typeof(float) => inputParameter is null ? 0F : float.Parse(inputParameter?.ToString()),
Type t when t == typeof(decimal) => inputParameter is null ? 0 : decimal.Parse(inputParameter?.ToString()),
Type t when t == typeof(double) => inputParameter is null ? 0 : double.Parse(inputParameter?.ToString()),
Type t when t == typeof(sbyte) => inputParameter is null ? 0 : sbyte.Parse(inputParameter?.ToString()),
Type t when t == typeof(byte) => inputParameter is null ? 0 : byte.Parse(inputParameter?.ToString()),
Type t when t == typeof(short) => inputParameter is null ? 0 : short.Parse(inputParameter?.ToString()),
Type t when t == typeof(ushort) => inputParameter is null ? 0U : ushort.Parse(inputParameter?.ToString()),
Type t when t == typeof(int) => inputParameter is null ? 0 : int.Parse(inputParameter?.ToString()),
Type t when t == typeof(uint) => inputParameter is null ? 0U : uint.Parse(inputParameter?.ToString()),
Type t when t == typeof(long) => inputParameter is null ? 0L : long.Parse(inputParameter?.ToString()),
Type t when t == typeof(ulong) => inputParameter is null ? 0UL : ulong.Parse(inputParameter?.ToString()),
Type t when t == typeof(nint) => inputParameter is null ? 0 : nint.Parse(inputParameter?.ToString()),
Type t when t == typeof(nuint) => inputParameter is null ? 0 : nuint.Parse(inputParameter?.ToString()),
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(), Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter, Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
_ => inputParameter, _ => inputParameter,
// Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
}; };
} }
catch (Exception ex) // 节点参数类型转换异常 catch (Exception ex) // 节点参数类型转换异常
{ {
parameters[i] = null; parameters[i] = new object();
Console.WriteLine(ex); Console.WriteLine(ex);
} }
} }
@@ -395,21 +399,25 @@ namespace Serein.NodeFlow.Base
} }
} }
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type) private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? data, int monitorType)
{ {
MonitorObjectEventArgs.ObjSourceType sourceType; MonitorObjectEventArgs.ObjSourceType sourceType;
string key; string? key;
if(type == 0) if(monitorType == 0)
{ {
key = data.GetType().FullName; key = data?.GetType()?.FullName;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
} }
else else
{ {
key = nodeModel.Guid; key = nodeModel.Guid;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
} }
if (string.IsNullOrEmpty(key))
{
return;
}
if (context.Env.CheckObjMonitorState(key, out List<string> exps)) // 如果新的数据处于查看状态通知UI进行更新交给运行环境判断 if (context.Env.CheckObjMonitorState(key, out List<string> exps)) // 如果新的数据处于查看状态通知UI进行更新交给运行环境判断
{ {
context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态通知UI更新数据显示 context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态通知UI更新数据显示

View File

@@ -46,7 +46,7 @@ namespace Serein.NodeFlow
public FlowEnvironment() public FlowEnvironment()
{ {
sereinIOC = new SereinIOC(); sereinIOC = new SereinIOC();
//ChannelFlowInterrupt = new ChannelFlowInterrupt(); ChannelFlowInterrupt = new ChannelFlowInterrupt();
//LoadedAssemblyPaths = new List<string>(); //LoadedAssemblyPaths = new List<string>();
//LoadedAssemblies = new List<Assembly>(); //LoadedAssemblies = new List<Assembly>();
//MethodDetailss = new List<MethodDetails>(); //MethodDetailss = new List<MethodDetails>();
@@ -69,67 +69,67 @@ namespace Serein.NodeFlow
/// <summary> /// <summary>
/// 加载Dll /// 加载Dll
/// </summary> /// </summary>
public event LoadDllHandler OnDllLoad; public event LoadDllHandler? OnDllLoad;
/// <summary> /// <summary>
/// 移除DLL /// 移除DLL
/// </summary> /// </summary>
public event RemoteDllHandler OnDllRemote; public event RemoteDllHandler? OnDllRemote;
/// <summary> /// <summary>
/// 项目加载完成 /// 项目加载完成
/// </summary> /// </summary>
public event ProjectLoadedHandler OnProjectLoaded; public event ProjectLoadedHandler? OnProjectLoaded;
/// <summary> /// <summary>
/// 节点连接属性改变事件 /// 节点连接属性改变事件
/// </summary> /// </summary>
public event NodeConnectChangeHandler OnNodeConnectChange; public event NodeConnectChangeHandler? OnNodeConnectChange;
/// <summary> /// <summary>
/// 节点创建事件 /// 节点创建事件
/// </summary> /// </summary>
public event NodeCreateHandler OnNodeCreate; public event NodeCreateHandler? OnNodeCreate;
/// <summary> /// <summary>
/// 移除节点事件 /// 移除节点事件
/// </summary> /// </summary>
public event NodeRemoteHandler OnNodeRemote; public event NodeRemoteHandler? OnNodeRemote;
/// <summary> /// <summary>
/// 起始节点变化事件 /// 起始节点变化事件
/// </summary> /// </summary>
public event StartNodeChangeHandler OnStartNodeChange; public event StartNodeChangeHandler? OnStartNodeChange;
/// <summary> /// <summary>
/// 流程运行完成事件 /// 流程运行完成事件
/// </summary> /// </summary>
public event FlowRunCompleteHandler OnFlowRunComplete; public event FlowRunCompleteHandler? OnFlowRunComplete;
/// <summary> /// <summary>
/// 被监视的对象改变事件 /// 被监视的对象改变事件
/// </summary> /// </summary>
public event MonitorObjectChangeHandler OnMonitorObjectChange; public event MonitorObjectChangeHandler? OnMonitorObjectChange;
/// <summary> /// <summary>
/// 节点中断状态改变事件 /// 节点中断状态改变事件
/// </summary> /// </summary>
public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange; public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange;
/// <summary> /// <summary>
/// 节点触发了中断 /// 节点触发了中断
/// </summary> /// </summary>
public event ExpInterruptTriggerHandler OnInterruptTrigger; public event ExpInterruptTriggerHandler? OnInterruptTrigger;
/// <summary> /// <summary>
/// 容器改变 /// 容器改变
/// </summary> /// </summary>
public event IOCMembersChangedHandler OnIOCMembersChanged; public event IOCMembersChangedHandler? OnIOCMembersChanged;
/// <summary> /// <summary>
/// 节点需要定位 /// 节点需要定位
/// </summary> /// </summary>
public event NodeLocatedHandler OnNodeLocate; public event NodeLocatedHandler? OnNodeLocate;
#endregion #endregion
#region #region
@@ -196,12 +196,12 @@ namespace Serein.NodeFlow
/// <summary> /// <summary>
/// 起始节点私有属性 /// 起始节点私有属性
/// </summary> /// </summary>
private NodeModelBase _startNode; private NodeModelBase? _startNode = null;
/// <summary> /// <summary>
/// 起始节点 /// 起始节点
/// </summary> /// </summary>
private NodeModelBase StartNode private NodeModelBase? StartNode
{ {
get get
{ {
@@ -209,6 +209,10 @@ namespace Serein.NodeFlow
} }
set set
{ {
if (value is null)
{
return;
}
if (_startNode is not null) if (_startNode is not null)
{ {
_startNode.IsStart = false; _startNode.IsStart = false;
@@ -481,17 +485,19 @@ namespace Serein.NodeFlow
public bool RemoteDll(string assemblyFullName) public bool RemoteDll(string assemblyFullName)
{ {
var library = NodeLibrarys.FirstOrDefault(nl => nl.Assembly.FullName.Equals(assemblyFullName)); var library = NodeLibrarys.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName));
if(library is null) if(library is null)
{ {
return false; return false;
} }
var groupedNodes = Nodes.Values
var nodes = Nodes.Values.ToDictionary( .Where(node => node.MethodDetails is not null)
key => key.MethodDetails.MethodName, .ToArray()
value => value .GroupBy(node => node.MethodDetails!.MethodName)
); .ToDictionary(
if(nodes.Count == 0) key => key.Key,
group => group.Count());
if(Nodes.Count == 0)
{ {
return true; // 当前无节点,可以直接删除 return true; // 当前无节点,可以直接删除
} }
@@ -500,9 +506,12 @@ namespace Serein.NodeFlow
{ {
foreach(var md in mds) foreach(var md in mds)
{ {
if (nodes.ContainsKey(md.MethodName)) if(groupedNodes.TryGetValue(md.MethodName,out int count))
{ {
return false; // 创建过相关的节点 if (count > 0)
{
return false; // 创建过相关的节点
}
} }
} }
MethodDetailss.Remove(library); MethodDetailss.Remove(library);
@@ -510,7 +519,7 @@ namespace Serein.NodeFlow
} }
else else
{ {
return true; return true;
} }
} }
@@ -685,9 +694,10 @@ namespace Serein.NodeFlow
} }
} }
public bool TryGetDelegate(string methodName, out Delegate del) public bool TryGetDelegate(string methodName, out Delegate? del)
{ {
if (MethodDelegates.TryGetValue(methodName, out del))
if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out del))
{ {
return del != null; return del != null;
} }
@@ -742,7 +752,7 @@ namespace Serein.NodeFlow
return false; return false;
} }
nodeModel.DebugSetting.InterruptClass = interruptClass; nodeModel.DebugSetting.InterruptClass = interruptClass;
OnNodeInterruptStateChange.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass)); OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
return true; return true;
} }
@@ -958,11 +968,19 @@ namespace Serein.NodeFlow
{ {
// 加载DLL创建 MethodDetails、实例作用对象、委托方法 // 加载DLL创建 MethodDetails、实例作用对象、委托方法
var assemblyName = type.Assembly.GetName().Name; var assemblyName = type.Assembly.GetName().Name;
if (string.IsNullOrEmpty(assemblyName))
{
continue;
}
var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type); var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type);
foreach(var method in methods) foreach(var method in methods)
{ {
(var md, var del) = MethodDetailsHelperTmp.CreateMethodDetails(type, method, assemblyName); (var md, var del) = MethodDetailsHelperTmp.CreateMethodDetails(type, method, assemblyName);
if(md is null || del is null)
{
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
continue;
}
if (MethodDelegates.TryAdd(md.MethodName, del)) if (MethodDelegates.TryAdd(md.MethodName, del))
{ {
methodDetails.Add(md); methodDetails.Add(md);

View File

@@ -30,7 +30,7 @@ namespace Serein.NodeFlow.Model
{ {
return []; return [];
} }
internal override NodeInfo ToInfo() internal override NodeInfo? ToInfo()
{ {
if (MethodDetails is null) return null; if (MethodDetails is null) return null;

View File

@@ -52,7 +52,7 @@ public static class MethodDetailsHelperTmp
/// 创建方法信息 /// 创建方法信息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static (MethodDetails,Delegate) CreateMethodDetails(Type type, MethodInfo method, string assemblyName) public static (MethodDetails?,Delegate?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
{ {
var methodName = method.Name; var methodName = method.Name;

View File

@@ -1,13 +1,4 @@
using System; using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
@@ -78,7 +69,7 @@ namespace Serein.NodeFlow.Tool
// 如果类型已经缓存,直接返回缓存的类型 // 如果类型已经缓存,直接返回缓存的类型
if (typeCache.ContainsKey(typeName)) if (typeCache.ContainsKey(typeName))
{ {
return Activator.CreateInstance(typeCache[typeName]); return Activator.CreateInstance(typeCache[typeName])!;
} }
// 定义动态程序集和模块 // 定义动态程序集和模块
@@ -98,7 +89,7 @@ namespace Serein.NodeFlow.Tool
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型 if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
{ {
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0]; var nestedPropValue = (propValue as IList<Dictionary<string, object>>)![0];
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element"); var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型 propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
} }
@@ -152,7 +143,7 @@ namespace Serein.NodeFlow.Tool
typeCache[typeName] = dynamicType; typeCache[typeName] = dynamicType;
// 创建对象实例 // 创建对象实例
return Activator.CreateInstance(dynamicType); return Activator.CreateInstance(dynamicType)!;
} }
// 方法 2: 递归设置对象的属性值 // 方法 2: 递归设置对象的属性值
@@ -169,10 +160,10 @@ namespace Serein.NodeFlow.Tool
if (value is Dictionary<string, object> nestedProperties) if (value is Dictionary<string, object> nestedProperties)
{ {
// 创建嵌套对象 // 创建嵌套对象
var nestedObj = Activator.CreateInstance(propInfo.PropertyType); var nestedObj = Activator.CreateInstance(propInfo!.PropertyType);
// 递归设置嵌套对象的值 // 递归设置嵌套对象的值
SetPropertyValues(nestedObj, nestedProperties); SetPropertyValues(nestedObj!, nestedProperties);
// 将嵌套对象赋值给属性 // 将嵌套对象赋值给属性
propInfo.SetValue(obj, nestedObj); propInfo.SetValue(obj, nestedObj);
@@ -180,7 +171,7 @@ namespace Serein.NodeFlow.Tool
else else
{ {
// 直接赋值给属性 // 直接赋值给属性
propInfo.SetValue(obj, value); propInfo!.SetValue(obj, value);
} }
} }
} }
@@ -222,7 +213,7 @@ namespace Serein.NodeFlow.Tool
if (propValue is Dictionary<string, object> nestedProperties) if (propValue is Dictionary<string, object> nestedProperties)
{ {
var nestedObj = Activator.CreateInstance(propInfo.PropertyType); var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
if (SetPropertyValuesWithValidation(nestedObj, nestedProperties)) if (nestedObj is not null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
{ {
propInfo.SetValue(obj, nestedObj); propInfo.SetValue(obj, nestedObj);
} }
@@ -235,22 +226,24 @@ namespace Serein.NodeFlow.Tool
{ {
// 获取目标类型的数组元素类型 // 获取目标类型的数组元素类型
var elementType = propInfo.PropertyType.GetElementType(); var elementType = propInfo.PropertyType.GetElementType();
var array = Array.CreateInstance(elementType, list.Count); if (elementType is not null)
for (int i = 0; i < list.Count; i++)
{ {
var item = Activator.CreateInstance(elementType); var array = Array.CreateInstance(elementType, list.Count);
if (SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
propInfo.SetValue(obj, array); for (int i = 0; i < list.Count; i++)
{
var item = Activator.CreateInstance(elementType);
if (item is not null && SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
propInfo.SetValue(obj, array);
}
} }
else else
{ {

View File

@@ -39,7 +39,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
return false; return false;
} }
private object GetMemberValue(object? obj, string memberPath) private object? GetMemberValue(object? obj, string memberPath)
{ {
string[] members = memberPath[1..].Split('.'); string[] members = memberPath[1..].Split('.');
foreach (var member in members) foreach (var member in members)

View File

@@ -29,7 +29,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
} }
} }
public static SereinConditionResolver ConditionParse(object data, string expression) public static SereinConditionResolver ConditionParse(object? data, string expression)
{ {
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值 if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
{ {
@@ -91,7 +91,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <summary> /// <summary>
/// 解析对象表达式 /// 解析对象表达式
/// </summary> /// </summary>
private static SereinConditionResolver ParseObjectExpression(object data, string expression) private static SereinConditionResolver ParseObjectExpression(object? data, string expression)
{ {
var parts = expression.Split(' '); var parts = expression.Split(' ');
string operatorStr = parts[0]; // 获取操作类型 string operatorStr = parts[0]; // 获取操作类型

View File

@@ -41,7 +41,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
/// <exception cref="NotSupportedException"></exception> /// <exception cref="NotSupportedException"></exception>
public static object Evaluate(string expression, object targetObJ, out bool isChange) public static object? Evaluate(string expression, object targetObJ, out bool isChange)
{ {
var parts = expression.Split([' '], 2); var parts = expression.Split([' '], 2);
if (parts.Length != 2) if (parts.Length != 2)
@@ -84,7 +84,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="methodCall">方法名称</param> /// <param name="methodCall">方法名称</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
private static object InvokeMethod(object target, string methodCall) private static object? InvokeMethod(object target, string methodCall)
{ {
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries); var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
if (methodParts.Length != 2) if (methodParts.Length != 2)
@@ -98,12 +98,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
.Select(p => p.Trim()) .Select(p => p.Trim())
.ToArray(); .ToArray();
var method = target.GetType().GetMethod(methodName); var method = target.GetType().GetMethod(methodName) ?? throw new ArgumentException($"Method {methodName} not found on target.");
if (method is null)
{
throw new ArgumentException($"Method {methodName} not found on target.");
}
var parameterValues = method.GetParameters() var parameterValues = method.GetParameters()
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType)) .Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
.ToArray(); .ToArray();
@@ -119,15 +114,14 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="memberPath">属性路径</param> /// <param name="memberPath">属性路径</param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
private static object GetMember(object target, string memberPath) private static object? GetMember(object? target, string memberPath)
{ {
if (target is null) return null;
// 分割成员路径,按 '.' 处理多级访问 // 分割成员路径,按 '.' 处理多级访问
var members = memberPath.Split('.'); var members = memberPath.Split('.');
foreach (var member in members) foreach (var member in members)
{ {
if (target == null) return null;
// 检查成员是否包含数组索引,例如 "cars[0]" // 检查成员是否包含数组索引,例如 "cars[0]"
var arrayIndexStart = member.IndexOf('['); var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1) if (arrayIndexStart != -1)
@@ -148,22 +142,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
} }
// 获取数组或集合对象 // 获取数组或集合对象
var arrayProperty = target.GetType().GetProperty(arrayName); var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty != null) if (arrayProperty is null)
{ {
target = arrayProperty.GetValue(target); var arrayField = target?.GetType().GetField(arrayName);
} if (arrayField is null)
else
{
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
{ {
throw new ArgumentException($"Member {arrayName} not found on target."); throw new ArgumentException($"Member {arrayName} not found on target.");
} }
else
{
target = arrayField.GetValue(target);
}
}
else
{
target = arrayProperty.GetValue(target);
} }
// 访问数组或集合中的指定索引 // 访问数组或集合中的指定索引
@@ -191,22 +185,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
else else
{ {
// 处理非数组情况的属性或字段 // 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member); var property = target?.GetType().GetProperty(member);
if (property != null) if (property is null)
{ {
target = property.GetValue(target); var field = target?.GetType().GetField(member);
} if (field is null)
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
{ {
throw new ArgumentException($"Member {member} not found on target."); throw new ArgumentException($"Member {member} not found on target.");
} }
else
{
target = field.GetValue(target);
}
}
else
{
target = property.GetValue(target);
} }
} }
} }
@@ -220,7 +214,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <param name="assignment">属性路径 </param> /// <param name="assignment">属性路径 </param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
private static object SetMember(object target, string assignment) private static object? SetMember(object? target, string assignment)
{ {
var parts = assignment.Split(new[] { '=' }, 2); var parts = assignment.Split(new[] { '=' }, 2);
if (parts.Length != 2) if (parts.Length != 2)
@@ -238,7 +232,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
// 检查是否包含数组索引 // 检查是否包含数组索引
var arrayIndexStart = member.IndexOf('['); var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1) if (arrayIndexStart != -1)
{ {
// 解析数组名和索引 // 解析数组名和索引
var arrayName = member.Substring(0, arrayIndexStart); var arrayName = member.Substring(0, arrayIndexStart);
@@ -255,24 +249,24 @@ namespace Serein.NodeFlow.Tool.SereinExpression
} }
// 获取数组或集合 // 获取数组或集合
var arrayProperty = target.GetType().GetProperty(arrayName); var arrayProperty = target?.GetType().GetProperty(arrayName);
if (arrayProperty != null) if (arrayProperty is null)
{ {
target = arrayProperty.GetValue(target); var arrayField = target?.GetType().GetField(arrayName);
} if (arrayField is null)
else
{
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
{ {
throw new ArgumentException($"Member {arrayName} not found on target."); throw new ArgumentException($"Member {arrayName} not found on target.");
} }
else
{
target = arrayField.GetValue(target);
}
} }
else
{
target = arrayProperty.GetValue(target);
}
// 获取目标数组或集合中的指定元素 // 获取目标数组或集合中的指定元素
if (target is Array array) if (target is Array array)
@@ -299,46 +293,47 @@ namespace Serein.NodeFlow.Tool.SereinExpression
else else
{ {
// 处理非数组情况的属性或字段 // 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member); var property = target?.GetType().GetProperty(member);
if (property != null) if (property is null)
{ {
target = property.GetValue(target); var field = target?.GetType().GetField(member);
} if (field is null)
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
{ {
throw new ArgumentException($"Member {member} not found on target."); throw new ArgumentException($"Member {member} not found on target.");
} }
else
{
target = field.GetValue(target);
}
}
else
{
target = property.GetValue(target);
} }
} }
} }
// 设置值
var lastMember = members.Last(); var lastMember = members.Last();
var lastProperty = target.GetType().GetProperty(lastMember); var lastProperty = target?.GetType().GetProperty(lastMember);
if (lastProperty != null) if (lastProperty is null)
{ {
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType); var lastField = target?.GetType().GetField(lastMember);
lastProperty.SetValue(target, convertedValue); if (lastField is null)
} {
else throw new ArgumentException($"Member {lastMember} not found on target.");
{ }
var lastField = target.GetType().GetField(lastMember); else
if (lastField != null)
{ {
var convertedValue = Convert.ChangeType(value, lastField.FieldType); var convertedValue = Convert.ChangeType(value, lastField.FieldType);
lastField.SetValue(target, convertedValue); lastField.SetValue(target, convertedValue);
} }
else }
{ else
throw new ArgumentException($"Member {lastMember} not found on target."); {
} var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
lastProperty.SetValue(target, convertedValue);
} }
return target; return target;

View File

@@ -18,7 +18,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework",
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net461DllTest", "Net461DllTest\Net461DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -12,24 +12,24 @@ namespace Serein.WorkBench
/// </summary> /// </summary>
public partial class App : Application public partial class App : Application
{ {
public class TestObject //public class TestObject
{ //{
public NestedObject Data { get; set; } // public NestedObject Data { get; set; }
public class NestedObject // public class NestedObject
{ // {
public int Code { get; set; } // public int Code { get; set; }
public int Code2 { get; set; } // public int Code2 { get; set; }
public string Tips { get; set; } // public string Tips { get; set; }
} // }
public string ToUpper(string input) // public string ToUpper(string input)
{ // {
return input.ToUpper(); // return input.ToUpper();
} // }
} //}
@@ -125,7 +125,7 @@ namespace Serein.WorkBench
/// 成功加载的工程文件 /// 成功加载的工程文件
/// </summary> /// </summary>
public static SereinProjectData? FlowProjectData { get; set; } public static SereinProjectData? FlowProjectData { get; set; }
public static string FileDataPath = ""; public static string FileDataPath { get; set; } = "";
private void Application_Startup(object sender, StartupEventArgs e) private void Application_Startup(object sender, StartupEventArgs e)
{ {
// 检查是否传入了参数 // 检查是否传入了参数
@@ -163,7 +163,7 @@ namespace Serein.WorkBench
string filePath; string filePath;
//filePath = @"F:\临时\project\tmp\project.dnf"; //filePath = @"F:\临时\project\tmp\project.dnf";
//filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf"; //filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf";
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net461DllTest\bin\Debug\project.dnf"; filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf";
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf"; //string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content); App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);

View File

@@ -139,15 +139,15 @@ namespace Serein.WorkBench
/// <summary> /// <summary>
/// 组合变换容器 /// 组合变换容器
/// </summary> /// </summary>
private TransformGroup canvasTransformGroup; private readonly TransformGroup canvasTransformGroup;
/// <summary> /// <summary>
/// 缩放画布 /// 缩放画布
/// </summary> /// </summary>
private ScaleTransform scaleTransform; private readonly ScaleTransform scaleTransform;
/// <summary> /// <summary>
/// 平移画布 /// 平移画布
/// </summary> /// </summary>
private TranslateTransform translateTransform; private readonly TranslateTransform translateTransform;
#endregion #endregion
public MainWindow() public MainWindow()
@@ -162,10 +162,17 @@ namespace Serein.WorkBench
InitFlowEnvironmentEvent(); // 配置环境事件 InitFlowEnvironmentEvent(); // 配置环境事件
logWindow = InitConsoleOut(); // 重定向 Console 输出 logWindow = InitConsoleOut(); // 重定向 Console 输出
InitCanvasUI(); // 配置画布 canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
if(App.FlowProjectData is not null) if (App.FlowProjectData is not null)
{ {
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目 FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
} }
@@ -198,17 +205,7 @@ namespace Serein.WorkBench
private void InitCanvasUI()
{
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
}
private LogWindow InitConsoleOut() private LogWindow InitConsoleOut()
{ {
@@ -243,7 +240,7 @@ namespace Serein.WorkBench
} }
var canvasData = project.Basic.Canvas; var canvasData = project.Basic.Canvas;
if (canvasData != null) if (canvasData is not null)
{ {
scaleTransform.ScaleX = 1; scaleTransform.ScaleX = 1;
scaleTransform.ScaleY = 1; scaleTransform.ScaleY = 1;
@@ -355,9 +352,9 @@ namespace Serein.WorkBench
End = toNode, End = toNode,
Type = connectionType Type = connectionType
}; };
if (toNode is FlipflopNodeControl flipflopControl) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 if (toNode is FlipflopNodeControl flipflopControl
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
{ {
var nodeModel = flipflopControl?.ViewModel?.Node;
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
} }
@@ -556,7 +553,7 @@ namespace Serein.WorkBench
} }
else else
{ {
if (ViewObjectViewer.MonitorKey.Equals(monitorKey)) // 相同对象 if (monitorKey.Equals(ViewObjectViewer.MonitorKey)) // 相同对象
{ {
ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新 ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新
} }
@@ -704,7 +701,7 @@ namespace Serein.WorkBench
/// <param name="power">抖动第一下偏移量</param> /// <param name="power">抖动第一下偏移量</param>
/// <param name="range">减弱幅度小于等于power大于0</param> /// <param name="range">减弱幅度小于等于power大于0</param>
/// <param name="speed">持续系数(大于0),越大时间越长,</param> /// <param name="speed">持续系数(大于0),越大时间越长,</param>
private static void ElasticAnimation(NodeControlBase? nodeControl, TranslateTransform translate, int power, int range = 1, double speed = 1) private static void ElasticAnimation(NodeControlBase nodeControl, TranslateTransform translate, int power, int range = 1, double speed = 1)
{ {
DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames(); DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames();
for (int i = power, j = 1; i >= 0; i -= range) for (int i = power, j = 1; i >= 0; i -= range)
@@ -883,10 +880,10 @@ namespace Serein.WorkBench
/// <param name="nodeControl"><para> 任何情景下都尽量避免直接操作 ViewModel 中的 NodeModel 节点,而是应该调用 FlowEnvironment 提供接口进行操作。</para> 因为 Workbench 应该更加关注UI视觉效果而非直接干扰流程环境运行的逻辑。<para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para> </param> /// <param name="nodeControl"><para> 任何情景下都尽量避免直接操作 ViewModel 中的 NodeModel 节点,而是应该调用 FlowEnvironment 提供接口进行操作。</para> 因为 Workbench 应该更加关注UI视觉效果而非直接干扰流程环境运行的逻辑。<para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para> </param>
private void ConfigureContextMenu(NodeControlBase nodeControl) private void ConfigureContextMenu(NodeControlBase nodeControl)
{ {
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
// var nodeModel = nodeControl.ViewModel.Node;
if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
{ {
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) => contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
@@ -894,7 +891,7 @@ namespace Serein.WorkBench
DisplayReturnTypeTreeViewer(returnType); DisplayReturnTypeTreeViewer(returnType);
})); }));
} }
var nodeGuid = nodeControl?.ViewModel?.Node?.Guid; var nodeGuid = nodeControl.ViewModel?.Node?.Guid;
#region - #region -
@@ -923,10 +920,10 @@ namespace Serein.WorkBench
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid))); contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid))); contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed))); contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsSucceed)));
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail))); contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsFail)));
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError))); contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsError)));
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream))); contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.Upstream)));
@@ -976,6 +973,10 @@ namespace Serein.WorkBench
{ {
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection))); contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
if (connection.ArrowPath is null || connection.BezierPath is null)
{
return;
}
connection.ArrowPath.ContextMenu = contextMenu; connection.ArrowPath.ContextMenu = contextMenu;
connection.BezierPath.ContextMenu = contextMenu; connection.BezierPath.ContextMenu = contextMenu;
} }
@@ -1198,8 +1199,8 @@ namespace Serein.WorkBench
// 准备放置条件表达式控件 // 准备放置条件表达式控件
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition) if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
{ {
ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement); ConditionRegionControl? conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
if (conditionRegion != null) if (conditionRegion is not null)
{ {
TryPlaceNodeInRegion(conditionRegion, nodeControl); TryPlaceNodeInRegion(conditionRegion, nodeControl);
//// 如果存在条件区域容器 //// 如果存在条件区域容器
@@ -1222,8 +1223,8 @@ namespace Serein.WorkBench
// 准备放置条件表达式控件 // 准备放置条件表达式控件
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition) if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
{ {
ConditionRegionControl conditionRegion = regionControl as ConditionRegionControl; ConditionRegionControl? conditionRegion = regionControl as ConditionRegionControl;
if (conditionRegion != null) if (conditionRegion is not null)
{ {
// 如果存在条件区域容器 // 如果存在条件区域容器
conditionRegion.AddCondition(nodeControl); conditionRegion.AddCondition(nodeControl);
@@ -1365,14 +1366,17 @@ namespace Serein.WorkBench
} }
private void ChangeViewerObjOfNode(NodeControlBase nodeControl) private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
{ {
var node = nodeControl?.ViewModel?.Node; var node = nodeControl.ViewModel.Node;
//if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void)) //if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void))
if (node is not null && node?.MethodDetails?.ReturnType != typeof(void)) if (node is not null && node.MethodDetails?.ReturnType != typeof(void))
{ {
var key = node.Guid; var key = node.Guid;
var instance = node.GetFlowData(); var instance = node.GetFlowData();
ViewObjectViewer.LoadObjectInformation(key, instance); if(instance is not null)
ChangeViewerObj(key, instance); {
ViewObjectViewer.LoadObjectInformation(key, instance);
ChangeViewerObj(key, instance);
}
} }
} }
public void ChangeViewerObj(string key, object instance) public void ChangeViewerObj(string key, object instance)
@@ -1768,64 +1772,6 @@ namespace Serein.WorkBench
} }
e.Handled = true; // 防止事件传播影响其他控件 e.Handled = true; // 防止事件传播影响其他控件
return;
// 如果正在选取状态,再次点击画布时自动确定选取范围,否则进入选取状态
if (IsSelectControl)
{
IsSelectControl = false;
// 释放鼠标捕获
FlowChartCanvas.ReleaseMouseCapture();
// 隐藏选取矩形(如果需要保持选取状态显示,可以删除此行)
SelectionRectangle.Visibility = Visibility.Collapsed;
// 处理选取区域内的元素(例如,获取选取范围内的控件)
Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
Canvas.GetTop(SelectionRectangle),
SelectionRectangle.Width,
SelectionRectangle.Height);
// 在此处处理选取的逻辑
foreach (UIElement element in FlowChartCanvas.Children)
{
Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element),
element.RenderSize.Width, element.RenderSize.Height);
if (selectionArea.Contains(elementBounds))
{
// 选中元素,执行相应操作
if (element is NodeControlBase control)
{
selectNodeControls.Add(control);
}
}
}
SelectedNode();// 选择之后需要执行的操作
}
else
{
// 进入选取状态
IsSelectControl = true;
// 开始选取时,记录鼠标起始点
startSelectControolPoint = e.GetPosition(FlowChartCanvas);
// 初始化选取矩形的位置和大小
Canvas.SetLeft(SelectionRectangle, startSelectControolPoint.X);
Canvas.SetTop(SelectionRectangle, startSelectControolPoint.Y);
SelectionRectangle.Width = 0;
SelectionRectangle.Height = 0;
// 显示选取矩形
SelectionRectangle.Visibility = Visibility.Visible;
SelectionRectangle.ContextMenu ??= ConfiguerSelectionRectangle();
// 捕获鼠标以便在鼠标移动到Canvas外部时仍能处理事件
FlowChartCanvas.CaptureMouse();
}
e.Handled = true; // 防止事件传播影响其他控件
} }
/// <summary> /// <summary>
@@ -2323,13 +2269,13 @@ namespace Serein.WorkBench
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="element"></param> /// <param name="element"></param>
/// <returns></returns> /// <returns></returns>
private static T GetParentOfType<T>(DependencyObject element) where T : DependencyObject private static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
{ {
while (element != null) while (element != null)
{ {
if (element is T) if (element is T e)
{ {
return element as T; return e;
} }
element = VisualTreeHelper.GetParent(element); element = VisualTreeHelper.GetParent(element);
} }
@@ -2342,9 +2288,9 @@ namespace Serein.WorkBench
private void JudgmentFlipFlopNode(NodeControlBase nodeControl) private void JudgmentFlipFlopNode(NodeControlBase nodeControl)
{ {
if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器 if (nodeControl is FlipflopNodeControl flipflopControl
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 判断是否为触发器
{ {
var nodeModel = flipflopControl?.ViewModel?.Node;
int count = 0; int count = 0;
foreach (var ct in NodeStaticConfig.ConnectionTypes) foreach (var ct in NodeStaticConfig.ConnectionTypes)
{ {
@@ -2473,13 +2419,18 @@ namespace Serein.WorkBench
node.Position = new Position(positionRelativeToParent.X, positionRelativeToParent.Y); node.Position = new Position(positionRelativeToParent.X, positionRelativeToParent.Y);
} }
} }
var isPass = SaveContentToFile(out string savePath, out Action<string,string>? savaProjectFile); if (!SaveContentToFile(out string savePath, out Action<string, string>? savaProjectFile))
if(!isPass)
{ {
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
return; return;
} }
string librarySavePath = System.IO.Path.GetDirectoryName(savePath); string? librarySavePath = System.IO.Path.GetDirectoryName(savePath);
if (string.IsNullOrEmpty(librarySavePath))
{
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
return;
}
Console.WriteLine(savePath); Console.WriteLine(savePath);
for (int index = 0; index < projectData.Librarys.Length; index++) for (int index = 0; index < projectData.Librarys.Length; index++)
{ {
@@ -2578,17 +2529,17 @@ namespace Serein.WorkBench
/// <param name="e"></param> /// <param name="e"></param>
private void ButtonTestExpObj_Click(object sender, RoutedEventArgs e) private void ButtonTestExpObj_Click(object sender, RoutedEventArgs e)
{ {
string jsonString = //string jsonString =
""" //"""
{ //{
"Name": "张三", // "Name": "张三",
"Age": 24, // "Age": 24,
"Address": { // "Address": {
"City": "北京", // "City": "北京",
"PostalCode": "10000" // "PostalCode": "10000"
} // }
} //}
"""; //""";
var externalData = new Dictionary<string, object> var externalData = new Dictionary<string, object>
{ {
@@ -2615,12 +2566,12 @@ namespace Serein.WorkBench
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!"); Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
} }
ObjDynamicCreateHelper.PrintObjectProperties(result); ObjDynamicCreateHelper.PrintObjectProperties(result!);
Console.WriteLine( ); Console.WriteLine( );
var exp = "@set .Addresses[1].Street = qwq"; var exp = "@set .Addresses[1].Street = qwq";
var data = SerinExpressionEvaluator.Evaluate(exp, result, out bool isChange); var data = SerinExpressionEvaluator.Evaluate(exp, result!, out bool isChange);
exp = "@get .Addresses[1].Street"; exp = "@get .Addresses[1].Street";
data = SerinExpressionEvaluator.Evaluate(exp,result, out isChange); data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
Console.WriteLine($"{exp} => {data}"); Console.WriteLine($"{exp} => {data}");
} }
@@ -2681,22 +2632,29 @@ namespace Serein.WorkBench
canvas.Dispatcher.InvokeAsync(() => canvas.Dispatcher.InvokeAsync(() =>
{ {
if (connection is not null && connection.BezierPath is null) if (connection is null)
{
return;
}
if (connection.BezierPath is null)
{ {
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 }; connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
//Canvas.SetZIndex(connection.BezierPath, -1); //Canvas.SetZIndex(connection.BezierPath, -1);
canvas.Children.Add(connection.BezierPath); canvas.Children.Add(connection.BezierPath);
} }
if (connection is not null && connection.ArrowPath is null) if (connection.ArrowPath is null)
{ {
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 }; connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
//Canvas.SetZIndex(connection.ArrowPath, -1); //Canvas.SetZIndex(connection.ArrowPath, -1);
canvas.Children.Add(connection.ArrowPath); canvas.Children.Add(connection.ArrowPath);
} }
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath); BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
isUpdating = false; isUpdating = false;
}); });
} }
@@ -2746,10 +2704,10 @@ namespace Serein.WorkBench
public class Connection public class Connection
{ {
public ConnectionType Type { get; set; } public ConnectionType Type { get; set; }
public Canvas Canvas { get; set; }// 贝塞尔曲线所在画布 public Canvas? Canvas { get; set; }// 贝塞尔曲线所在画布
public System.Windows.Shapes.Path BezierPath { get; set; }// 贝塞尔曲线路径 public System.Windows.Shapes.Path? BezierPath { get; set; }// 贝塞尔曲线路径
public System.Windows.Shapes.Path ArrowPath { get; set; } // 箭头路径 public System.Windows.Shapes.Path? ArrowPath { get; set; } // 箭头路径
public required NodeControlBase Start { get; set; } // 起始 public required NodeControlBase Start { get; set; } // 起始
public required NodeControlBase End { get; set; } // 结束 public required NodeControlBase End { get; set; } // 结束
@@ -2757,8 +2715,11 @@ namespace Serein.WorkBench
public void RemoveFromCanvas() public void RemoveFromCanvas()
{ {
Canvas.Children.Remove(BezierPath); // 移除线 if(Canvas != null)
Canvas.Children.Remove(ArrowPath); // 移除线 {
Canvas.Children.Remove(BezierPath); // 移除线
Canvas.Children.Remove(ArrowPath); // 移除线
}
} }
/// <summary> /// <summary>
@@ -2766,6 +2727,10 @@ namespace Serein.WorkBench
/// </summary> /// </summary>
public void Refresh() public void Refresh()
{ {
if(Canvas is null || BezierPath is null || ArrowPath is null)
{
return;
}
BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath); BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath);
} }
} }

View File

@@ -12,7 +12,6 @@ namespace Serein.WorkBench.Node.View
/// </summary> /// </summary>
public partial class ConditionRegionControl : NodeControlBase public partial class ConditionRegionControl : NodeControlBase
{ {
private Point _dragStartPoint;
public ConditionRegionControl() : base() public ConditionRegionControl() : base()
{ {

View File

@@ -74,7 +74,7 @@ namespace Serein.WorkBench.Themes
{ConnectionType.IsError, []}, {ConnectionType.IsError, []},
} }
}; };
string itemName = rootNodeModel?.MethodDetails?.MethodTips; string? itemName = rootNodeModel.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName)) if (string.IsNullOrEmpty(itemName))
{ {
itemName = rootNodeModel.ControlType.ToString(); itemName = rootNodeModel.ControlType.ToString();
@@ -141,10 +141,10 @@ namespace Serein.WorkBench.Themes
RootNode = child, RootNode = child,
ChildNodes = child.SuccessorNodes, ChildNodes = child.SuccessorNodes,
}; };
string itemName = child?.MethodDetails?.MethodTips; string? itemName = child?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName)) if (string.IsNullOrEmpty(itemName))
{ {
itemName = child.ControlType.ToString(); itemName = child?.ControlType.ToString();
} }
TreeViewItem treeViewItem = new TreeViewItem TreeViewItem treeViewItem = new TreeViewItem
{ {
@@ -204,10 +204,10 @@ namespace Serein.WorkBench.Themes
ChildNodes = childNodeModel.SuccessorNodes, ChildNodes = childNodeModel.SuccessorNodes,
}; };
string itemName = childNodeModel?.MethodDetails?.MethodTips; string? itemName = childNodeModel?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName)) if (string.IsNullOrEmpty(itemName))
{ {
itemName = childNodeModel.ControlType.ToString(); itemName = childNodeModel?.ControlType.ToString();
} }
TreeViewItem treeViewItem = new TreeViewItem TreeViewItem treeViewItem = new TreeViewItem
{ {

View File

@@ -22,7 +22,6 @@ namespace Serein.WorkBench.Themes
/// </summary> /// </summary>
public partial class NodeTreeViewControl : UserControl public partial class NodeTreeViewControl : UserControl
{ {
private IFlowEnvironment FlowEnvironment { get; set; }
public NodeTreeViewControl() public NodeTreeViewControl()
{ {
InitializeComponent(); InitializeComponent();

View File

@@ -32,7 +32,7 @@ namespace Serein.WorkBench.Themes
/// <summary> /// <summary>
/// 属性名称 /// 属性名称
/// </summary> /// </summary>
public string Name { get; set; } public string? Name { get; set; }
/// <summary> /// <summary>
/// 属性类型 /// 属性类型
/// </summary> /// </summary>
@@ -40,15 +40,15 @@ namespace Serein.WorkBench.Themes
/// <summary> /// <summary>
/// 数据类型 /// 数据类型
/// </summary> /// </summary>
public Type DataType { get; set; } public Type? DataType { get; set; }
/// <summary> /// <summary>
/// 数据 /// 数据
/// </summary> /// </summary>
public object DataValue { get; set; } public object? DataValue { get; set; }
/// <summary> /// <summary>
/// 数据路径 /// 数据路径
/// </summary> /// </summary>
public string DataPath { get; set; } public string DataPath { get; set; } = string.Empty;
} }
@@ -80,24 +80,24 @@ namespace Serein.WorkBench.Themes
/// <summary> /// <summary>
/// 运行环境 /// 运行环境
/// </summary> /// </summary>
public IFlowEnvironment FlowEnvironment { get; set; } public IFlowEnvironment? FlowEnvironment { get; set; }
/// <summary> /// <summary>
/// 监视对象的键 /// 监视对象的键
/// </summary> /// </summary>
public string MonitorKey { get => monitorKey; } public string? MonitorKey { get => monitorKey; }
/// <summary> /// <summary>
/// 正在监视的对象 /// 正在监视的对象
/// </summary> /// </summary>
public object MonitorObj { get => monitorObj; } public object? MonitorObj { get => monitorObj; }
/// <summary> /// <summary>
/// 监视表达式 /// 监视表达式
/// </summary> /// </summary>
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); } public string? MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
private string monitorKey; private string? monitorKey;
private object monitorObj; private object? monitorObj;
// 用于存储当前展开的节点路径 // 用于存储当前展开的节点路径
private HashSet<string> expandedNodePaths = new HashSet<string>(); private HashSet<string> expandedNodePaths = new HashSet<string>();
@@ -133,7 +133,7 @@ namespace Serein.WorkBench.Themes
/// <param name="e"></param> /// <param name="e"></param>
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e) private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
{ {
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式 if (FlowEnvironment is not null && FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
{ {
if (string.IsNullOrEmpty(MonitorExpression)) if (string.IsNullOrEmpty(MonitorExpression))
{ {
@@ -146,7 +146,7 @@ namespace Serein.WorkBench.Themes
} }
} }
private TreeViewItem? LoadTree(object obj) private TreeViewItem? LoadTree(object? obj)
{ {
if (obj is null) return null; if (obj is null) return null;
var objectType = obj.GetType(); var objectType = obj.GetType();
@@ -178,7 +178,7 @@ namespace Serein.WorkBench.Themes
/// <summary> /// <summary>
/// 刷新对象属性树 /// 刷新对象属性树
/// </summary> /// </summary>
public void RefreshObjectTree(object obj) public void RefreshObjectTree(object? obj)
{ {
monitorObj = obj; monitorObj = obj;
var rootNode = LoadTree(obj); var rootNode = LoadTree(obj);
@@ -222,14 +222,20 @@ namespace Serein.WorkBench.Themes
{ {
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
{ {
if (flowDataDetails.ItemType == TreeItemType.Item || item.Items.Count == 0) if (flowDataDetails.ItemType != TreeItemType.Item && item.Items.Count != 0)
{ {
// 记录当前节点的路径 return;
var path = flowDataDetails.DataPath;
expandedNodePaths.Add(path);
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
} }
if(flowDataDetails.DataValue is null || flowDataDetails.DataType is null)
{
return;
}
// 记录当前节点的路径
var path = flowDataDetails.DataPath;
expandedNodePaths.Add(path);
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
} }
} }
} }
@@ -276,8 +282,8 @@ namespace Serein.WorkBench.Themes
continue; continue;
} }
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象 TreeViewItem? memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
if (memberNode != null) if (memberNode is not null)
{ {
treeViewNode.Items.Add(memberNode); // 添加到当前节点 treeViewNode.Items.Add(memberNode); // 添加到当前节点
@@ -305,7 +311,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="member"></param> /// <param name="member"></param>
/// <returns></returns> /// <returns></returns>
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member) private TreeViewItem? ConfigureTreeViewItem(object obj, MemberInfo member)
{ {
if (obj == null) if (obj == null)
{ {
@@ -353,7 +359,7 @@ namespace Serein.WorkBench.Themes
else else
{ {
TreeViewItem memberNode = new TreeViewItem { Header = member.Name }; TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
string propertyValue = GetPropertyValue(obj, property, out object value); string propertyValue = GetPropertyValue(obj, property, out object? value);
memberNode.Tag = new FlowDataDetails memberNode.Tag = new FlowDataDetails
{ {
ItemType = TreeItemType.Property, ItemType = TreeItemType.Property,
@@ -417,7 +423,7 @@ namespace Serein.WorkBench.Themes
else else
{ {
TreeViewItem memberNode = new TreeViewItem { Header = member.Name }; TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
string fieldValue = GetFieldValue(obj, field, out object value); string fieldValue = GetFieldValue(obj, field, out object? value);
memberNode.Tag = new FlowDataDetails memberNode.Tag = new FlowDataDetails
{ {
@@ -454,7 +460,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="property"></param> /// <param name="property"></param>
/// <returns></returns> /// <returns></returns>
private string GetPropertyValue(object obj, PropertyInfo property,out object value) private string GetPropertyValue(object obj, PropertyInfo property,out object? value)
{ {
try try
{ {
@@ -482,7 +488,7 @@ namespace Serein.WorkBench.Themes
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="field"></param> /// <param name="field"></param>
/// <returns></returns> /// <returns></returns>
private string GetFieldValue(object obj, FieldInfo field, out object value) private string GetFieldValue(object obj, FieldInfo field, out object? value)
{ {
try try
{ {

View File

@@ -227,10 +227,10 @@ namespace Serein.WorkBench.Themes
/// </summary> /// </summary>
/// <param name="node">目标节点</param> /// <param name="node">目标节点</param>
/// <returns>父节点</returns> /// <returns>父节点</returns>
private TreeViewItem GetParentTreeViewItem(TreeViewItem node) private TreeViewItem? GetParentTreeViewItem(TreeViewItem node)
{ {
DependencyObject parent = VisualTreeHelper.GetParent(node); DependencyObject parent = VisualTreeHelper.GetParent(node);
while (parent != null && !(parent is TreeViewItem)) while (parent != null && parent is not TreeViewItem)
{ {
parent = VisualTreeHelper.GetParent(parent); parent = VisualTreeHelper.GetParent(parent);
} }

View File

@@ -29,7 +29,7 @@ namespace Serein.WorkBench.Tool.Converters
return boolValue ? Visibility.Visible : Visibility.Collapsed; return boolValue ? Visibility.Visible : Visibility.Collapsed;
} }
public object ConvertBack(object value, Type targetType, public object? ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture) object parameter, CultureInfo culture)
{ {
return null; return null;