优化了SereinEnv.WriteLine(Exception, InfoClass)输出,可以通过更改InfoClass控制是否输出异常堆栈信息。

优化了批量加载节点时,脚本节点类型分析异常的问题。
This commit is contained in:
fengjiayi
2025-07-17 22:46:40 +08:00
parent 550122208d
commit 88de5a21f5
13 changed files with 113 additions and 36 deletions

View File

@@ -248,7 +248,7 @@ namespace Serein.Library.Web
catch (Exception ex1) catch (Exception ex1)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex1.ToString()); SereinEnv.WriteLine(ex1);
} }
} }

View File

@@ -135,7 +135,7 @@ namespace Serein.Library.Web
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
} }
} }

View File

@@ -132,7 +132,16 @@ namespace Serein.Library
/// <param name="class"></param> /// <param name="class"></param>
public static void WriteLine(Exception ex, InfoClass @class = InfoClass.General) public static void WriteLine(Exception ex, InfoClass @class = InfoClass.General)
{ {
SereinEnv.environment.WriteLine(InfoType.ERROR, ex.ToString(), @class); if(@class == InfoClass.Trivial)
{
SereinEnv.environment.WriteLine(InfoType.ERROR, ex.ToString(), @class);
}
else
{
SereinEnv.environment.WriteLine(InfoType.ERROR, ex.Message, @class);
}
} }

View File

@@ -393,8 +393,8 @@ namespace Serein.NodeFlow.Env
public async Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos) public async Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
{ {
#region NodeInfo创建NodeModel #region NodeInfo创建NodeModel
// 流程接口节点最后才创建
// 加载节点与画布Model进行绑定
async Task AddNodeAsync(NodeInfo nodeInfo, IFlowNode nodeModel) async Task AddNodeAsync(NodeInfo nodeInfo, IFlowNode nodeModel)
{ {
if (!TryGetCanvasModel(nodeInfo.CanvasGuid, out var canvasModel)) if (!TryGetCanvasModel(nodeInfo.CanvasGuid, out var canvasModel))
@@ -403,6 +403,8 @@ namespace Serein.NodeFlow.Env
} }
else else
{ {
// 节点与画布互相绑定 // 节点与画布互相绑定
// 需要在UI线程上进行添加否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常 // 需要在UI线程上进行添加否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常
nodeModel.CanvasDetails = canvasModel; nodeModel.CanvasDetails = canvasModel;
@@ -431,7 +433,6 @@ namespace Serein.NodeFlow.Env
} }
} }
@@ -469,6 +470,53 @@ namespace Serein.NodeFlow.Env
} }
#endregion #endregion
#region
HashSet<string> nodeIds = new HashSet<string>();
void ReloadScript(SingleScriptNode scriptNode)
{
if (nodeIds.Contains(scriptNode.Guid))
{
nodeIds.Add(scriptNode.Guid);
return;
}
var pds = scriptNode.MethodDetails?.ParameterDetailss;
if (pds is null || pds.Length == 0)
{
nodeIds.Add(scriptNode.Guid);
return;
}
foreach (var pd in pds)
{
//if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) continue;
var argSourceNodeGuid = pd.ArgDataSourceNodeGuid;
if (!string.IsNullOrWhiteSpace(argSourceNodeGuid)
&& flowModelService.TryGetNodeModel(argSourceNodeGuid, out var flowNode) && flowNode is SingleScriptNode argSourceNode)
{
ReloadScript(argSourceNode);
}
}
scriptNode.ReloadScript(); // 如果是流程接口节点,则需要重新加载脚本
nodeIds.Add(scriptNode.Guid);
}
var scriptNodes = nodeInfos.Where(info => info.Type == nameof(NodeControlType.Script))
.Select(info => flowModelService.TryGetNodeModel(info.Guid, out var node) ? node : null)
.OfType<SingleScriptNode>()
.ToList();
foreach (SingleScriptNode scriptNode in scriptNodes)
{
ReloadScript(scriptNode);
}
#endregion
#region #region
List<NodeInfo> needPlaceNodeInfos = []; List<NodeInfo> needPlaceNodeInfos = [];

View File

@@ -481,7 +481,7 @@ namespace Serein.NodeFlow.Env
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex}"); SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex.Message}");
} }
} }
@@ -501,7 +501,7 @@ namespace Serein.NodeFlow.Env
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex}"); SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex.Message}");
} }
} }

View File

@@ -80,23 +80,17 @@ namespace Serein.NodeFlow.Model
/// </summary> /// </summary>
public override void OnCreating() public override void OnCreating()
{ {
/* MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi));
if (method != null)
{
ScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口
}*/
var md = MethodDetails; var md = MethodDetails;
var pd = md.ParameterDetailss ??= new ParameterDetails[1]; var pd = md.ParameterDetailss ??= new ParameterDetails[1];
md.ParamsArgIndex = 0; md.ParamsArgIndex = 0;
pd[0] = new ParameterDetails pd[0] = new ParameterDetails
{ {
Index = 0, Index = 0,
Name = "object", Name = "string",
IsExplicitData = true, IsExplicitData = true,
DataValue = string.Empty, DataValue = string.Empty,
DataType = typeof(object), DataType = typeof(string),
ExplicitType = typeof(object), ExplicitType = typeof(string),
ArgDataSourceNodeGuid = string.Empty, ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
@@ -105,7 +99,7 @@ namespace Serein.NodeFlow.Model
IsParams = true, IsParams = true,
//Description = "脚本节点入参" //Description = "脚本节点入参"
}; };
md.ReturnType = typeof(object); // 默认返回 object md.ReturnType = typeof(void); // 默认返回
} }
@@ -136,7 +130,7 @@ namespace Serein.NodeFlow.Model
this.MethodDetails.ParameterDetailss[i].Name = nodeInfo.ParameterData[i].ArgName; this.MethodDetails.ParameterDetailss[i].Name = nodeInfo.ParameterData[i].ArgName;
} }
ReloadScript();// 加载时重新解析 //ReloadScript();// 加载时重新解析
IsScriptChanged = false; // 重置脚本改变标志 IsScriptChanged = false; // 重置脚本改变标志
} }
@@ -144,7 +138,7 @@ namespace Serein.NodeFlow.Model
/// <summary> /// <summary>
/// 重新加载脚本代码 /// 重新加载脚本代码
/// </summary> /// </summary>
public void ReloadScript() public bool ReloadScript()
{ {
try try
{ {
@@ -158,16 +152,30 @@ namespace Serein.NodeFlow.Model
varNames.Add(pd.Name); varNames.Add(pd.Name);
} }
Dictionary<string, Type> dict = MethodDetails.ParameterDetailss.ToDictionary(pd => pd.Name, pd => pd.DataType); // 准备预定义类型
var returnType = sereinScript.ParserScript(dict, Script); // 开始解析获取程序主节点 var argTypes = MethodDetails.ParameterDetailss
.Select(pd =>
{
if (Env.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var node) &&
node.MethodDetails?.ReturnType is not null)
{
pd.DataType = node.MethodDetails.ReturnType;
return (pd.Name, node.MethodDetails.ReturnType);
}
return default;
})
.Where(x => x != default)
.ToDictionary(x => x.Name, x => x.ReturnType); // 准备预定义类型
var returnType = sereinScript.ParserScript(Script, argTypes); // 开始解析获取程序主节点
MethodDetails.ReturnType = returnType; MethodDetails.ReturnType = returnType;
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
return false; // 解析失败
} }
} }
@@ -202,7 +210,7 @@ namespace Serein.NodeFlow.Model
lock (@params) { lock (@params) {
if (IsScriptChanged) if (IsScriptChanged)
{ {
ReloadScript();// 每次都重新解析 ReloadScript();// 执行时检查是否需要重新解析
IsScriptChanged = false; IsScriptChanged = false;
context.Env.WriteLine(InfoType.INFO, $"[{Guid}]脚本解析完成"); context.Env.WriteLine(InfoType.INFO, $"[{Guid}]脚本解析完成");
} }

View File

@@ -378,7 +378,7 @@ namespace Serein.NodeFlow.Model.Operation
if (FromNode.MethodDetails.ReturnType == typeof(void)) if (FromNode.MethodDetails.ReturnType == typeof(void))
{ {
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收 void 返回值"); SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收 void 返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
return false; return false;
} }
@@ -388,7 +388,7 @@ namespace Serein.NodeFlow.Model.Operation
if (false && string.IsNullOrWhiteSpace(toNodeArgSourceGuid) && flowModelService.ContainsNodeModel(toNodeArgSourceGuid)) if (false && string.IsNullOrWhiteSpace(toNodeArgSourceGuid) && flowModelService.ContainsNodeModel(toNodeArgSourceGuid))
{ {
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收多个节点返回值"); SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收多个节点返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
return false; return false;
} }

View File

@@ -93,7 +93,7 @@ namespace Serein.NodeFlow.Services
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, $"尝试卸载程序集[{assemblyName}]发生错误:{ex}"); SereinEnv.WriteLine(InfoType.ERROR, $"尝试卸载程序集[{assemblyName}]发生错误:{ex.Message}");
return false; return false;
} }

View File

@@ -21,12 +21,18 @@ namespace Serein.Script
private ProgramNode? programNode; private ProgramNode? programNode;
public Type ParserScript(Dictionary<string, Type> argTypes, string script) /// <summary>
/// 解析脚本
/// </summary>
/// <param name="script">脚本</param>
/// <param name="argTypes">挂载的变量</param>
/// <returns></returns>
public Type ParserScript(string script, Dictionary<string, Type>? argTypes = null)
{ {
SereinScriptParser parser = new SereinScriptParser(); SereinScriptParser parser = new SereinScriptParser();
var programNode = parser.Parse(script); var programNode = parser.Parse(script);
TypeAnalysis.NodeSymbolInfos.Clear(); // 清空符号表 TypeAnalysis.NodeSymbolInfos.Clear(); // 清空符号表
TypeAnalysis.LoadSymbol(argTypes); // 提前加载脚本节点定义的符号 if(argTypes is not null) TypeAnalysis.LoadSymbol(argTypes); // 提前加载脚本节点定义的符号
TypeAnalysis.Analysis(programNode); // 分析节点类型 TypeAnalysis.Analysis(programNode); // 分析节点类型
var returnType = TypeAnalysis.NodeSymbolInfos[programNode]; // 获取返回类型 var returnType = TypeAnalysis.NodeSymbolInfos[programNode]; // 获取返回类型
this.programNode = programNode; this.programNode = programNode;

View File

@@ -623,7 +623,13 @@ namespace Serein.Script
NextToken(); // 消耗 ")" NextToken(); // 消耗 ")"
break; break;
} }
else if (peekToken.Type == TokenType.BraceLeft)
{
NextToken(); // 消耗 类型名称
break;
}
} }
TypeNode typeNode = new TypeNode(typeName); TypeNode typeNode = new TypeNode(typeName);
typeNode.SetTokenInfo(typeToken); typeNode.SetTokenInfo(typeToken);
ObjectInstantiationNode objectInstantiationNode = new ObjectInstantiationNode(typeNode, ctorArguments); ObjectInstantiationNode objectInstantiationNode = new ObjectInstantiationNode(typeNode, ctorArguments);

View File

@@ -37,13 +37,13 @@ namespace Serein.Workbench.Node.ViewModel
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
} }
}); });
CommandLoadScript = new RelayCommand( o => CommandLoadScript = new RelayCommand( o =>
{ {
NodeModel.ReloadScript(); NodeModel.ReloadScript(); // 工作台重新加载脚本
}); });
} }

View File

@@ -40,7 +40,7 @@ namespace Serein.Workbench.ViewModels
} }
catch (Exception ex) catch (Exception ex)
{ {
flowEnvironment.WriteLine(Library.InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
return; return;
} }
} }

View File

@@ -804,7 +804,7 @@ namespace Serein.Workbench.Views
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
} }
} }
@@ -1578,7 +1578,7 @@ namespace Serein.Workbench.Views
} }
catch (Exception ex) catch (Exception ex)
{ {
SereinEnv.WriteLine(InfoType.ERROR, ex.ToString()); SereinEnv.WriteLine(ex);
} }
} }
#endregion #endregion