using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using Newtonsoft.Json;
using TeamAAS.FlowEditor.Execution;
using TeamAAS.FlowEditor.Plugins;
namespace TeamAAS.FlowEditor.Models
{
[Serializable]
///
/// 流程节点基类 - 所有节点类型的公共属性
///
public class FlowNode : TeamAAS.BindableBase
{
#region 标识属性
[Category("II.杂项")]
[DisplayName("1.节点ID")]
[ReadOnly(true)]
[Browsable(true)]
public string NodeId { get; set; } = System.Guid.NewGuid().ToString("N");
private string _nodeName = "新节点";
[Category("I.节点参数")]
[DisplayName("1.节点名称")]
[Description("设置流程名称")]
[Browsable(true)]
///
/// 节点唯一ID(用于连接线关联,自动生成)
///
public string NodeName
{
get => _nodeName;
set
{
if (SetProperty(ref _nodeName, value))
{
if (PluginModel?.GetModel != null)
PluginModel.GetModel.NodeName = value;
}
}
}
[Category("II.杂项")]
[DisplayName("2.工具ID")]
[ReadOnly(true)]
[Browsable(true)]
///
/// 关联的插件ID(兼容旧代码) 不变
///
public string PluginId { get; set; }
///
/// 所属流程绑定的结果注册表(运行态=Main,编辑/调试态=Debug)。
/// 由 FlowGraph/InitializeNode 注入,并同步到 PluginModel.Registry。
///
[JsonIgnore]
[ReadOnly(true)]
[Browsable(false)]
public ResultRegistry Registry
{
get => _registry;
set
{
_registry = value;
if (PluginModel != null)
PluginModel.Registry = value;
}
}
[NonSerialized]
private ResultRegistry _registry;
[Category("II.杂项")]
[DisplayName("3.工具名称")]
[Description("设置工具名称")]
[Browsable(true)]
[ReadOnly(true)]
///
/// 工具名称
///
public string ToolName
{
get => PluginModel?.GetModel?.ToolName;
set
{
if (PluginModel?.GetModel != null)
PluginModel.GetModel.ToolName = value;
}
}
private string _flowId;
[Category("II.杂项")]
[DisplayName("4.流程ID")]
[ReadOnly(true)]
[Browsable(true)]
///
/// 所属流程ID(用于结果注册表和公式解析)
///
public string FlowId
{
get => _flowId;
set
{
if (_flowId != value)
{
_flowId = value;
if (PluginModel?.GetModel != null)
PluginModel.GetModel.FlowId = value;
}
}
}
private string _flowName;
[Category("II.杂项")]
[DisplayName("5.流程名称")]
[ReadOnly(true)]
[Browsable(true)]
///
/// 所属流程名称(用于UI展示和公式解析)
///
public string FlowName
{
get => _flowName;
set
{
if (_flowName != value)
{
_flowName = value;
if (PluginModel?.GetModel != null)
PluginModel.GetModel.FlowName = value;
}
}
}
/// 节点是否启用(委托到 PluginModel.IsEnable,禁用后不执行+画布变灰)
[Category("I.节点参数")]
[DisplayName("2.启用工具")]
[Description("是否启用工具")]
[Browsable(true)]
public bool IsEnabled
{
get => PluginModel?.IsEnable ?? true;
set
{
if (PluginModel != null && PluginModel.IsEnable != value)
{
PluginModel.IsEnable = value;
RaisePropertyChanged(nameof(IsEnabled));
}
}
}
[Browsable(false)]
///
/// 节点分类(用于UI展示不同形状和颜色)
///
public NodeCategory Category { get; set; } = NodeCategory.Normal;
private IFlowNodePlugin _PluginModel { get; set; }
[Browsable(false)]
///
/// 插件模型数据(序列化存储,类型为 BasePluginModel 派生类)
///
public IFlowNodePlugin PluginModel
{
get => _PluginModel;
set
{
if (value != null && _PluginModel != value)
{
_PluginModel = value;
//_PluginModel.InitRun();
// 同步 FlowNode 当前属性到模型
_PluginModel.Registry = _registry;
_PluginModel.GetModel.NodeName = _nodeName;
if (_flowName != null) _PluginModel.GetModel.FlowName = _flowName;
if (_flowId != null) _PluginModel.GetModel.FlowId = _flowId;
SetResults(FlowId, _PluginModel.LastResults);
}
}
}
[Category("I.节点参数")]
[DisplayName("3.跳过警告")]
[Description("是否跳过返回值为 Warning 的返回任务")]
[Browsable(true)]
public bool IsSkipWarning
{
get; set;
} = false;
private int _preDelayMs = 0;
[Category("I.节点参数")]
[DisplayName("4.运行前休眠(ms)")]
[Description("节点执行前等待的毫秒数。默认0=不等待,最小0ms。")]
[Browsable(true)]
public int PreDelayMs
{
get => _preDelayMs;
set => _preDelayMs = value <= 0 ? 0 : value;
}
private int _postDelayMs = 0;
[Category("I.节点参数")]
[DisplayName("5.运行后休眠(ms)")]
[Description("节点执行完成后等待的毫秒数,再启动后继节点。默认0=不等待,最小0ms。")]
[Browsable(true)]
public int PostDelayMs
{
get => _postDelayMs;
set => _postDelayMs = value <= 0 ? 0 : value;
}
private int _timeoutMs = 0;
[Category("I.节点参数")]
[DisplayName("运行超时(ms)")]
[Description("节点执行超时看门狗:超过该毫秒数仍未返回则判定为超时失败并继续后续流程(防止硬件卡死拖垮整条流程)。默认0=不限时。注:阻塞式硬件调用无法被强制中断,超时仅保证流程不再无限等待。")]
[Browsable(true)]
public int TimeoutMs
{
get => _timeoutMs;
set => _timeoutMs = value <= 0 ? 0 : value;
}
///
/// 任务插件「日志存储详细度」(委托到 PluginModel.GetModel.StoreVerbosity,实际存储与序列化在模型侧)。
/// 粗略=只写 L1/L2 到公共日志;详细=额外把 L3/L4 写到独立路径。
///
[Category("I.节点参数")]
[DisplayName("6.日志存储")]
[Description("日志存储详细度:粗略=只写公共日志(L1/L2);详细=额外把 L3/L4 写到 流程\\任务 独立日志")]
[Browsable(true)]
public TeamAAS.Logging.LogVerbosity StoreVerbosity
{
get => PluginModel?.GetModel?.StoreVerbosity ?? TeamAAS.Logging.LogVerbosity.粗略;
set
{
if (PluginModel?.GetModel != null && PluginModel.GetModel.StoreVerbosity != value)
{
PluginModel.GetModel.StoreVerbosity = value;
RaisePropertyChanged(nameof(StoreVerbosity));
}
}
}
///
/// 任务插件「日志显示详细度」(委托到 PluginModel.GetModel.DisplayVerbosity,实际存储在模型侧)。
/// 粗略=实时日志只显 L1/L2;详细=额外显 L3/L4(与是否落盘无关)。
///
[Category("I.节点参数")]
[DisplayName("7.日志显示")]
[Description("日志显示详细度:粗略=实时日志只显 L1/L2;详细=额外显 L3/L4(独立于存储)")]
[Browsable(true)]
public TeamAAS.Logging.LogVerbosity DisplayVerbosity
{
get => PluginModel?.GetModel?.DisplayVerbosity ?? TeamAAS.Logging.LogVerbosity.粗略;
set
{
if (PluginModel?.GetModel != null && PluginModel.GetModel.DisplayVerbosity != value)
{
PluginModel.GetModel.DisplayVerbosity = value;
RaisePropertyChanged(nameof(DisplayVerbosity));
}
}
}
#endregion
#region 布局属性
[Browsable(false)]
public double X
{
get => _x;
set => SetProperty(ref _x, value);
}
private double _x = 100;
[Browsable(false)]
public double Y
{
get => _y;
set => SetProperty(ref _y, value);
}
private double _y = 100;
#endregion
#region 运行属性
[Browsable(false)]
[JsonIgnore]
public NodeRunStatus Status
{
get => _status;
set => SetProperty(ref _status, value);
}
[NonSerialized]
private NodeRunStatus _status = NodeRunStatus.NotStarted;
[Browsable(false)]
[JsonIgnore]
public int CostTime
{
get => _costTime;
set => SetProperty(ref _costTime, value);
}
[NonSerialized]
private int _costTime;
#endregion
#region 异常分支触发状态(运行时,不序列化)
[NonSerialized] private int _exceptionTriggers;
[NonSerialized] private int _exceptionRunFlag;
[Browsable(false)]
[JsonIgnore]
public int ExceptionTriggers => _exceptionTriggers;
/// 原子 +1 触发计数,返回新值。用于异常分支节点排队触发。
public int IncrementExceptionTrigger() => Interlocked.Increment(ref _exceptionTriggers);
/// 原子 -1 触发计数,返回新值。每次实际执行完一次后调用。
public int DecrementExceptionTrigger() => Interlocked.Decrement(ref _exceptionTriggers);
/// 尝试获取运行权:CAS 把 _exceptionRunFlag 从 0 改为 1。成功=true 表示获得运行权。
public bool TryAcquireExceptionRun() =>
Interlocked.CompareExchange(ref _exceptionRunFlag, 1, 0) == 0;
/// 释放运行权:把 _exceptionRunFlag 重置为 0。
public void ReleaseExceptionRun() => Interlocked.Exchange(ref _exceptionRunFlag, 0);
/// 重置异常触发状态(每次流程开始 ResetAllNodes 时调用)。
public void ResetExceptionState()
{
Interlocked.Exchange(ref _exceptionTriggers, 0);
Interlocked.Exchange(ref _exceptionRunFlag, 0);
}
#endregion
#region 执行结果
[NonSerialized]
private ObservableCollection _resultItems = new ObservableCollection();
[Browsable(false)]
[JsonIgnore]
public ObservableCollection ResultItems
{
get => _resultItems;
set => SetProperty(ref _resultItems, value);
}
///
/// 执行历史记录(每次执行追加一条)
///
[NonSerialized]
private ObservableCollection _executionHistory = new ObservableCollection();
[Browsable(false)]
[JsonIgnore]
public ObservableCollection ExecutionHistory
{
get => _executionHistory;
set => SetProperty(ref _executionHistory, value);
}
///
/// 当前选中的历史记录(UI展示用)
///
[NonSerialized]
private ExecutionHistoryEntry _selectedHistoryEntry;
[Browsable(false)]
[JsonIgnore]
public ExecutionHistoryEntry SelectedHistoryEntry
{
get => _selectedHistoryEntry;
set => SetProperty(ref _selectedHistoryEntry, value);
}
[Browsable(false)]
///
/// 历史记录最大保留条数(默认50,可外部设置)
///
public int MaxHistoryCount { get; set; } = 50;
///
/// 节点最新执行结果原始字典(供公式引用,不参与序列化)
///
[Browsable(false)]
[JsonIgnore]
public Dictionary RawResults { get; set; }
private static bool IsBasicType(object value)
{
if (value == null) return true;
var t = value.GetType();
return t.IsPrimitive || t == typeof(string) || t == typeof(decimal) ||
t == typeof(DateTime) || t == typeof(TimeSpan) || t == typeof(Guid) || t.IsEnum;
}
///
/// 设置执行结果(UI线程调用)
///
public void SetResults(string GraphId, Dictionary results)
{
if (_resultItems == null)
_resultItems = new ObservableCollection();
_resultItems?.Clear();
if (results != null)
{
foreach (var kv in results)
{
// 非基础类型只存 ToString,避免图像/CogRecord 等大对象挂在 UI 和历史快照里
// RawResults 保留原始引用供节点间公式解析使用
bool converted = kv.Value != null && !IsBasicType(kv.Value);
_resultItems?.Add(new ResultItem
{
Key = kv.Key,
Value = converted ? kv.Value.ToString() : kv.Value,
// 转换后 TypeName 显式记录原始实际类型,避免显示成 String
TypeName = converted ? ResultItem.DescribeType(kv.Value.GetType()) : null
});
}
}
RaisePropertyChanged(nameof(ResultItems));
RaisePropertyChanged(nameof(ExecutionHistory));
RaisePropertyChanged(nameof(SelectedHistoryEntry));
// 添加到历史记录
var snapshot = new ObservableCollection();
foreach (var item in _resultItems)
snapshot.Add(new ResultItem { Key = item.Key, Value = item.Value, TypeName = item.TypeName });
var entry = new ExecutionHistoryEntry(DateTime.Now, snapshot);
if (_executionHistory == null)
_executionHistory = new ObservableCollection();
Application.Current?.Dispatcher.Invoke(() => _executionHistory.Insert(0, entry));
// 超过最大条数自动清理最旧的(末尾)
while (_executionHistory.Count > MaxHistoryCount)
Application.Current?.Dispatcher.Invoke(() => _executionHistory.RemoveAt(_executionHistory.Count - 1));
SelectedHistoryEntry = entry; // 自动选最新
RawResults = results;
}
#endregion
#region UI辅助属性
[Browsable(false)]
///
/// 节点图标(Material Design 图标 Kind 名,如 "Camera";由 MahApps IconPacks 渲染)
///
public string IconGeometry { get; set; } = "CircleOutline";
[Browsable(false)]
[JsonIgnore]
public bool IsSelected
{
get => _isSelected;
set
{
if (SetProperty(ref _isSelected, value))
RaisePropertyChanged(nameof(BorderColor));
}
}
[NonSerialized]
private bool _isSelected;
[Browsable(false)]
[JsonIgnore]
///
/// 边框颜色(选中时高亮)
///
public string BorderColor => IsSelected ? "#007ACC" : "#3F3F46";
///
/// 节点宽度(可被实际渲染尺寸覆盖)
///
[NonSerialized]
private double _nodeWidth = 0;
[Browsable(false)]
public double NodeWidth
{
get
{
if (_nodeWidth > 0) return _nodeWidth;
switch (Category)
{
case NodeCategory.Decision: return 180;
case NodeCategory.ForLoop: return 180;
case NodeCategory.Group: return 200;
case NodeCategory.End: return 140;
case NodeCategory.ExceptionBranch: return 160;
default: return 160;
}
}
set => _nodeWidth = value;
}
///
/// 节点高度(可被实际渲染尺寸覆盖)
///
[NonSerialized]
private double _nodeHeight = 0;
[Browsable(false)]
public double NodeHeight
{
get
{
if (_nodeHeight > 0) return _nodeHeight;
switch (Category)
{
case NodeCategory.Decision: return 66;
case NodeCategory.ForLoop: return 66;
case NodeCategory.Group: return 66;
case NodeCategory.End: return 52;
case NodeCategory.ExceptionBranch: return 66;
default: return 66;
}
}
set => _nodeHeight = value;
}
[Browsable(false)]
[JsonIgnore]
///
/// 是否有第二个输出端口(判断节点)
///
public System.Windows.Visibility HasSecondOutput
{
get
{
return Category == NodeCategory.Decision
? System.Windows.Visibility.Visible
: System.Windows.Visibility.Collapsed;
}
}
[Browsable(false)]
[JsonIgnore]
///
/// 分类对应的标题色(十六进制)
///
public string CategoryColor
{
get
{
switch (Category)
{
case NodeCategory.Decision: return "#C2771A";
case NodeCategory.ToolBlock: return "#7B1FA2";
case NodeCategory.ForLoop: return "#00897B";
case NodeCategory.Group: return "#E65100";
case NodeCategory.End: return "#D32F2F";
case NodeCategory.ExceptionBranch: return "#B71C1C";
default: return "#007ACC";
}
}
}
[Browsable(false)]
[JsonIgnore]
///
/// 状态文本(属性面板用)
///
public string StatusText
{
get
{
switch (Status)
{
case NodeRunStatus.NotStarted: return "未运行";
case NodeRunStatus.Running: return "运行中...";
case NodeRunStatus.Success: return "成功";
case NodeRunStatus.Failed: return "失败";
case NodeRunStatus.Skipped: return "跳过";
default: return "";
}
}
}
[Browsable(false)]
[JsonIgnore]
///
/// 耗时显示文本(节点上显示)
///
public string CostTimeText
{
get
{
if (CostTime == 0) return "0ms";
if (CostTime < 1000) return CostTime + "ms";
return (CostTime / 1000.0).ToString("F1") + "s";
}
}
[Browsable(false)]
[JsonIgnore]
///
/// 状态指示色(十六进制)
///
public string StatusColor
{
get
{
switch (Status)
{
case NodeRunStatus.NotStarted: return "#888888";
case NodeRunStatus.Running: return "#FF9800";
case NodeRunStatus.Success: return "#4CAF50";
case NodeRunStatus.Failed: return "#F44336";
case NodeRunStatus.Skipped: return "#666666";
default: return "#888888";
}
}
}
public void NotifyStatusChanged()
{
RaisePropertyChanged(nameof(StatusText));
RaisePropertyChanged(nameof(StatusColor));
RaisePropertyChanged(nameof(CostTimeText));
//RaisePropertyChanged(nameof(PropertySummary));
RaisePropertyChanged(nameof(ResultItems));
}
#endregion
public FlowNode()
{
}
///
/// 初始化ID链接。
/// 导入 .aas / 打开产品流程 / 复制粘贴等所有加载路径最终都会经过这里,
/// 是节点"重挂接"的唯一入口;插件初始化(InitPlugin + 注册默认输出)也在这里统一补跑。
///
///
///
public void InitializeNode(string flowId, string flowName, ResultRegistry registry = null)
{
FlowId = flowId;
FlowName = flowName;
NodeId = System.Guid.NewGuid().ToString("N");
// 绑定所属流程的结果注册表(setter 会同步到 PluginModel.Registry)
Registry = registry ?? _registry ?? ResultRegistry.Debug;
var model = PluginModel?.GetModel;
if (model != null)
{
model.NodeId = NodeId;
model.FlowId = FlowId;
model.FlowName = FlowName;
model.NodeName = NodeName;
}
// Group/ForLoop 子流程:递归初始化子节点并重映射子图连线
if ((Category == NodeCategory.Group || Category == NodeCategory.ForLoop) && model != null)
{
// 反射查找 FlowGraph 类型的属性,遍历子节点初始化ID和Name
var graphProp = model.GetType().GetProperties()
.FirstOrDefault(p => p.PropertyType == typeof(FlowGraph));
if (graphProp != null)
{
var subGraph = graphProp.GetValue(model) as FlowGraph;
if (subGraph != null)
{
subGraph.GraphId = NodeId;
subGraph.GraphName = NodeName;
subGraph.Registry = Registry; // 子流程与父流程共用同一注册表
if (subGraph.Nodes != null)
{
var childIdMap = new Dictionary();
foreach (var subNode in subGraph.Nodes)
{
string oldSubNodeId = subNode.NodeId;
subNode.InitializeNode(NodeId, NodeName, Registry);
if (!string.IsNullOrEmpty(oldSubNodeId))
{
childIdMap[oldSubNodeId] = subNode.NodeId;
}
}
// 重映射子图连接线的 SourceNodeId/TargetNodeId
if (subGraph.Connections != null && childIdMap.Count > 0)
{
foreach (var conn in subGraph.Connections)
{
if (childIdMap.TryGetValue(conn.SourceNodeId, out var newSrcId))
conn.SourceNodeId = newSrcId;
if (childIdMap.TryGetValue(conn.TargetNodeId, out var newTgtId))
conn.TargetNodeId = newTgtId;
}
}
}
}
}
}
ExecutionHistory?.Clear();
// 恢复运行态集合([NonSerialized] 字段经 BinaryFormatter 反序列化后为 null)
if (_resultItems == null)
_resultItems = new ObservableCollection();
if (_executionHistory == null)
_executionHistory = new ObservableCollection();
// 关键修复:反序列化直接恢复私有字段 _PluginModel,不会经过 PluginModel 的 setter,
// 因此这里统一补跑插件初始化(InitPlugin + 将默认输出注册到 ResultRegistry),
// 保证导入/打开方案/粘贴后的节点与编辑器新建节点行为一致。
if (PluginModel != null)
{
PluginModel.InitRun();
// 与新建节点一致:默认输出同步到 UI 结果列表(仅在集合尚未建立时执行,避免重复历史记录)
if (_resultItems.Count == 0 && _executionHistory.Count == 0)
SetResults(FlowId, PluginModel.LastResults);
}
}
}
[Serializable]
///
/// 执行历史记录项
///
public class ExecutionHistoryEntry : TeamAAS.BindableBase
{
public DateTime Timestamp { get; }
public string TimestampText => Timestamp.ToString("HH:mm:ss");
public string FullTimestampText => Timestamp.ToString("HH:mm:ss.fff");
private ObservableCollection _results;
public ObservableCollection Results
{
get => _results;
set => SetProperty(ref _results, value);
}
public ExecutionHistoryEntry(DateTime timestamp, ObservableCollection results)
{
Timestamp = timestamp;
_results = results;
}
}
[Serializable]
///
/// 执行结果项(UI展示用)
///
public class ResultItem
{
public string Key { get; set; }
public object Value { get; set; }
///
/// 显式类型名:大对象被转成字符串展示时,记录转换前的实际类型;
/// 未显式设置时按 Value 运行时类型推断。
///
private string _typeName;
public string TypeName
{
get => _typeName ?? (Value == null ? "null" : DescribeType(Value.GetType()));
set => _typeName = value;
}
/// 类型名格式化(泛型显示为 List<T> 形式)
internal static string DescribeType(Type t)
{
if (t.IsGenericType)
{
var args = string.Join(", ", t.GetGenericArguments().Select(a => a.Name));
return t.Name.Split('`')[0] + "<" + args + ">";
}
return t.Name;
}
/// 简单类型的字符串展示
public string DisplayValue
{
get
{
if (Value == null) return "";
if (Value is System.Collections.IList list && !(Value is string))
return list.Count + " 项";
if (IsComplex) return "";
return Value?.ToString() ?? "";
}
}
/// 是否为复杂对象(需展开子属性)
public bool IsComplex => Value != null && !IsSimpleType(Value.GetType());
private static bool IsSimpleType(Type type)
{
return type.IsPrimitive
|| type.IsEnum
|| type == typeof(string)
|| type == typeof(decimal)
|| type == typeof(DateTime);
}
private ObservableCollection _subItems;
public ObservableCollection SubItems
{
get
{
if (_subItems == null && IsComplex)
{
_subItems = new ObservableCollection();
// 列表/数组:按索引展开元素
if (Value is System.Collections.IList list && !(Value is string))
{
for (int i = 0; i < list.Count; i++)
{
_subItems.Add(new ResultItem { Key = "[" + i + "]", Value = list[i] });
}
}
else
{
// 普通对象:反射公开属性
foreach (var prop in Value.GetType().GetProperties(
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
try
{
var val = prop.GetValue(Value);
_subItems.Add(new ResultItem { Key = prop.Name, Value = val });
}
catch { }
}
}
}
return _subItems ?? new ObservableCollection();
}
}
}
}