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; } } } }