using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using TeamAAS.FlowEditor.Execution;
using TeamAAS.FlowEditor.Models;
using TeamAAS.FlowEditor.Plugins;
using TeamAAS.FlowEngine.Execution;
using TeamAAS.FlowEngine.Interfaces;
using TeamAAS;
namespace TeamAAS.FlowEngine
{
///
/// 流程节点插件注册中心 + 流程 Tab 统一容器。懒汉单例:PluginLoader.Instance。
/// 启动时从 Plugins 目录扫描带有 PluginAttribute 的类型,
/// 建立「显示名称 → 插件类型」的索引;流程编辑器和执行器通过该索引创建节点实例;
/// 同时通过 统一管理所有打开的流程,支撑组合模块的跨流程查询。
/// 管理类按架构规则保留在根命名空间 TeamAAS.FlowEngine。
///
public class PluginLoader : IPluginLoader
{
#region 懒汉单例
private static readonly Lazy _instance =
new Lazy(() => new PluginLoader(), isThreadSafe: true);
///
/// 懒汉单例入口。首次访问时初始化,线程安全。
///
public static PluginLoader Instance => _instance.Value;
#endregion
private readonly List _descriptors = new List();
private readonly Dictionary _byName = new Dictionary(StringComparer.OrdinalIgnoreCase);
private bool _loaded = false;
private readonly object _scanLock = new object();
///
/// 组合模块编辑时,记录当前子流程对应的组合模块 PluginModel;
/// 公式编辑器据此区分「编辑子流程节点属性」和「编辑组合模块输出」场景。
///
public FlowGraph SetGroupPluginModel { get; set; }
///
/// 组合模块编辑时,记录外层组合模块的 Model;
/// InputSourceDataProvider 用它查询主流程中组合模块的前置节点结果。
///
public BasePluginModel ParentGroupModel { get; set; }
///
/// 当前在 PropertyGrid 中选中的节点(公式编辑器用它判断上下文)。
///
public FlowNode SelectFlow { get; set; }
#region 流程统一管理
///
/// 当前打开的所有流程 Tab(编辑器/执行器共用,可绑定到 UI)。
///
public ObservableCollection FlowTabs { get; set; } = new ObservableCollection();
///
/// 通过名称获取对应的 FlowTabItem(不存在返回 null)。
///
public FlowTabItem GetFlowTab(string flowName)
{
if (string.IsNullOrEmpty(flowName)) return null;
return FlowTabs.FirstOrDefault(t => t.Name == flowName);
}
///
/// 通过流程名称获取一个新的 FlowExecutor 执行器(基于 FlowGraph 构造)。
/// 流程或图不存在返回 null。
///
public FlowExecutor GetFlowExecutor(string flowName)
{
var tab = GetFlowTab(flowName);
if (tab?.Graph == null) return null;
return new FlowExecutor(tab.Graph);
}
///
/// 获取所有已打开流程的名称列表(用于下拉框、日志输出等)。
///
public List GetFlowNames()
{
return FlowTabs.Select(t => t.Name).ToList();
}
#endregion
///
/// 所有已发现插件的描述(只读快照,顺序与扫描顺序一致)。
///
public IReadOnlyList Descriptors => _descriptors;
///
/// 当前插件目录是否已加载过(防止重复扫描)。
///
public bool IsLoaded => _loaded;
///
/// 从指定目录加载插件:仅把文件名形如 Plugins.*.dll 的程序集反射用作插件;
/// 目录下的其它 DLL 属于运行环境/依赖,交给 CLR 与 AssemblyResolve 按需加载,这里不主动加载、也不清理。
/// 加载或扫描某个插件出错时,通过 写入主页实时日志提示。
/// 只在首次调用时执行( 为 true 时直接返回)。
///
/// 插件目录路径(通常是 Runtime\Plugins)
public void LoadFrom(string pluginsPath)
{
if (_loaded) return;
lock (_scanLock)
{
if (_loaded) return;
_loaded = true;
if (Directory.Exists(pluginsPath))
{
// 递归扫描子目录(视觉平台分包:HalconPlugins/VmPlugins/VppPlugins 等),
// 同名 DLL 只装载一次(防止多目录重复部署导致插件类型双注册)
var seenAssemblies = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var dll in Directory.GetFiles(pluginsPath, "*.dll", SearchOption.AllDirectories))
{
var fileName = Path.GetFileName(dll);
// 只把 Plugins.* 命名的程序集当插件反射;其它 DLL 是环境/依赖,会被别处按需加载,这里不管
if (!fileName.StartsWith("Plugins.", StringComparison.OrdinalIgnoreCase))
continue;
if (!seenAssemblies.Add(fileName))
{
AppLogger.Warning($"插件 {fileName} 在多个子目录中重复出现,跳过: {dll}", "插件加载");
continue;
}
try
{
var asm = Assembly.LoadFrom(dll);
if (!ScanAssembly(asm))
AppLogger.Warning($"插件 {fileName} 未发现任何插件类型(检查 [Plugin] 标注或依赖是否齐全)", "插件加载");
}
catch (Exception ex)
{
// 插件加载/扫描失败 → 主页实时日志提示(含异常详情)
AppLogger.Error($"插件加载失败 {fileName}: {ex.Message}", ex, "插件加载");
System.Diagnostics.Debug.WriteLine($"[PluginLoader] {dll}: {ex}");
}
}
}
else
{
AppLogger.Warning($"插件目录不存在,已跳过插件扫描: {pluginsPath}", "插件加载");
}
// 兜底:扫描当前 AppDomain 内已加载的非动态程序集(如被主程序直接引用的插件)
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try { if (!asm.IsDynamic) ScanAssembly(asm); } catch { }
}
AppLogger.Info($"插件加载完成,共 {_descriptors.Count} 个流程节点插件", "插件加载");
}
}
///
/// 在一个程序集内扫描所有带有 [PluginAttribute] 且实现 IFlowNodePlugin
/// 的非抽象类型,写入描述列表与名称索引。
///
/// 程序集中是否至少发现一个插件类型
private bool ScanAssembly(Assembly asm)
{
bool found = false;
Type[] types;
try { types = asm.GetTypes(); }
catch (ReflectionTypeLoadException ex)
{
// 部分类型加载失败时,仍扫描已成功加载的类型
types = ex.Types?.Where(t => t != null).ToArray() ?? Type.EmptyTypes;
}
catch { return false; }
foreach (var type in types)
{
try
{
var attr = type.GetCustomAttribute();
if (attr != null && typeof(IFlowNodePlugin).IsAssignableFrom(type) && !type.IsAbstract)
{
found = true;
if (!_descriptors.Any(d => d.PluginType == type))
{
var desc = new PluginDescriptor { PluginType = type, Attribute = attr };
_descriptors.Add(desc);
_byName[attr.DisplayName] = desc;
}
}
}
catch { }
}
return found;
}
///
/// 按显示名称创建插件实例(不带模型初始化)。找不到或实例化失败返回 null。
///
public IFlowNodePlugin CreateInstance(string displayName)
{
if (string.IsNullOrEmpty(displayName)) return null;
if (_byName.TryGetValue(displayName, out var desc))
{
try { return Activator.CreateInstance(desc.PluginType) as IFlowNodePlugin; }
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[PluginLoader] Create {displayName}: {ex.Message}");
return null;
}
}
return null;
}
///
/// 按显示名称获取插件描述信息(含 PluginType、Attribute 元数据)。找不到返回 null。
///
public PluginDescriptor GetDescriptor(string displayName)
{
if (string.IsNullOrEmpty(displayName)) return null;
_byName.TryGetValue(displayName, out var desc);
return desc;
}
///
/// 获取所有已注册插件的 UI 展示信息(工具箱列表绑定用)。
///
public List GetAllPluginInfos()
{
return _descriptors.Select(d => new NodePluginInfo
{
PluginId = GuidGenerator.GenerateGuidFromValue(d.DisplayName).ToString(),
DisplayName = d.DisplayName,
Category = d.NodeShape,
IconGeometry = d.IconGeometry,
Description = d.Attribute.Description,
Group = d.Category,
IsSubFlowNode = d.IsSubFlowNode,
VisionCategory = d.VisionCategory
}).ToList();
}
///
/// 获取单个插件的 UI 展示信息。找不到返回 null。
///
public NodePluginInfo GetPluginInfo(string displayName)
{
var desc = GetDescriptor(displayName);
if (desc == null) return null;
return new NodePluginInfo
{
PluginId = desc.DisplayName,
DisplayName = desc.DisplayName,
Category = desc.NodeShape,
IconGeometry = desc.IconGeometry,
Description = desc.Attribute.Description,
Group = desc.Category
};
}
///
/// 查找指定节点的首个后继节点插件实例(按连线先后顺序,只返回第一个)。
/// 用于条件跳转、组合模块子流程等只取一条后继的场景。
///
/// 流程名称(匹配 FlowTabItem.Name)
/// 起点节点 NodeId
public IFlowNodePlugin GetPluginTool(string flowName, string nodeId)
{
var graph = FlowTabs.FirstOrDefault(x => x.Graph.GraphName == flowName)?.Graph;
var outgoing = graph?.Connections
.Where(c => c.SourceNodeId == nodeId)
.ToList();
foreach (var conn in outgoing)
{
var target = graph?.GetNode(conn.TargetNodeId);
if (target != null) return target.PluginModel;
}
return default(IFlowNodePlugin);
}
///
/// 通过 FlowId + NodeId 获取所有后继节点列表。
/// 自动在 FlowTabs 顶层图以及所有组合模块 SubGraph 中递归查找;
/// 找不到目标图时返回空列表。
///
/// 目标图 GraphId(主流程或组合模块子流程皆可)
/// 起点节点 NodeId
public List IDGetNextPlugins(string flowId, string nodeId)
{
var flows = new List();
try
{
// 1) 先查顶层 FlowTabs(主流程场景)
var graph = FlowTabs.FirstOrDefault(x => x.Graph.GraphId == flowId)?.Graph;
// 2) 没找到 → 在所有组合模块的子流程里查(子流程场景,用反射避免 FlowEngine 依赖具体插件类型)
if (graph == null) graph = FindSubGraph(FlowTabs, flowId);
if (graph == null) return flows;
var outgoing = graph.Connections
.Where(c => c.SourceNodeId == nodeId)
.ToList();
foreach (var conn in outgoing)
{
var target = graph.GetNode(conn.TargetNodeId);
if (target != null) flows.Add(target);
}
return flows;
}
catch (Exception) { return flows; }
}
///
/// 递归在所有 Tab 的节点 PluginModel.SubGraph 里查找 GraphId 匹配的子流程。
/// 用反射访问 SubGraph 属性,避免 FlowEngine 直接依赖具体插件类型(如 GroupPluginModel)。
///
private static FlowGraph FindSubGraph(ObservableCollection tabs, string subFlowId)
{
if (tabs == null || string.IsNullOrEmpty(subFlowId)) return null;
foreach (var tab in tabs)
{
if (tab?.Graph?.Nodes == null) continue;
var found = FindSubGraphInGraph(tab.Graph, subFlowId);
if (found != null) return found;
}
return null;
}
///
/// 在单个图内递归查找 SubGraph(支持任意嵌套组合模块)。
///
private static FlowGraph FindSubGraphInGraph(FlowGraph graph, string subFlowId)
{
if (graph?.Nodes == null) return null;
foreach (var node in graph.Nodes)
{
// node.PluginModel 是 IFlowNodePlugin(插件实例),SubGraph 属性在它的 GetModel(BasePluginModel 派生)上
var model = node.PluginModel?.GetModel;
var subGraph = TryGetSubGraph(model);
if (subGraph != null)
{
if (subGraph.GraphId == subFlowId) return subGraph;
var nested = FindSubGraphInGraph(subGraph, subFlowId);
if (nested != null) return nested;
}
}
return null;
}
///
/// 反射读 PluginModel 的 SubGraph 属性(兼容 GroupPluginModel 及其他组合模块插件)。
/// 属性不存在或为 null 返回 null。
///
private static FlowGraph TryGetSubGraph(object pluginModel)
{
if (pluginModel == null) return null;
try
{
var prop = pluginModel.GetType().GetProperty("SubGraph");
return prop?.GetValue(pluginModel) as FlowGraph;
}
catch { return null; }
}
}
}