using PropertyGridLib.Controls; using System; using System.Collections.Generic; using System.Threading; using TeamAAS.FlowEditor.Execution; using TeamAAS.FlowEditor.Models; namespace TeamAAS.FlowEditor.Plugins { /// /// 插件抽象基类 - 参照 VisionKit 的 BaseAbstractPlugin<TModel> /// 泛型 TModel 自动实例化,子类只需实现 InitPlugin 和 PluginRun /// [Serializable] public abstract class BasePlugin : IFlowNodePlugin where TModel : BasePluginModel, new() { public TModel Model { get; protected set; } public BasePluginModel GetModel { get => Model; set => Model = (TModel)value; } /// /// 所属流程绑定的结果注册表(运行态=Main,编辑/调试态=Debug)。 /// 由 FlowNode 在初始化/换图时注入;未注入时回退到 Debug 注册表。 /// [field: NonSerialized] public ResultRegistry Registry { get; set; } /// 实际使用的注册表(保证非 null) protected ResultRegistry RegistryOrDebug => Registry ?? ResultRegistry.Debug; [NonSerialized] private TeamAAS.Global.IPluginContext _context; /// /// 插件运行上下文:获取平台服务(相机/机器人/通讯/供料/运动)的正规渠道。 /// 未注入时回落到全局 ServiceRegistry.Default,永不为 null; /// 插件经 Context.GetService 取服务,无需直接摸 XxxManager.Instance。 /// 宿主将来可提供自定义实现(如远程代理)注入覆盖。 /// public TeamAAS.Global.IPluginContext Context { get => _context ?? TeamAAS.Global.ServiceRegistry.Default; set => _context = value; } /// 按类型获取平台服务(工厂方法):相机/机器人/通讯/供料/运动管理器等;未注册返回 null。 protected T Service() => Context.GetService(); public int CostTime { get; protected set; } public NodeRunStatus IsSuccess { get; set; } = NodeRunStatus.NotStarted; public bool IsEnable { get; set; } = true; public Dictionary LastResults { get; private set; } /// 分支节点:返回下一个要执行的节点名列表(null=非分支节点) public List BranchTargets { get; protected set; } protected BasePlugin() { Model = new TModel(); } /// /// 释放插件持有的资源。 /// 子类有相机句柄/连接流/大对象等需释放的资源时,重写并调用 base.Dispose()。 /// public virtual void Dispose() { try { LastResults?.Clear(); LastResults = null; BranchTargets?.Clear(); BranchTargets = null; Model = null; } catch { } } public virtual bool InitPlugin() { return true; } /// /// 声明插件输出字段。静态插件重写此方法声明固定输出;动态插件可从 Model 动态生成。 /// public virtual List DeclareOutputs() => new List(); public abstract NodeRunStatus PluginRun(CancellationToken token, out Dictionary results); public bool InitRun() { Dictionary results = new Dictionary(); try { bool ret = InitPlugin(); foreach (var field in DeclareOutputs()) { results[field.Key] = field.ValueType != null && field.ValueType.IsValueType ? Activator.CreateInstance(field.ValueType) : null; } results["状态"] = NodeRunStatus.NotStarted; results["结果"] = false; results["耗时"] = 0; results["模型"] = this; if (!ret) results["Error"] = GetModel.FlowName + "插件初始化失败"; RegistryOrDebug.SetResult(GetModel.FlowId, GetModel.NodeId, GetModel.NodeName, results); LastResults = results; return ret; } catch (System.Exception ex) { results["Error"] = GetModel.FlowName + ex.Message; RegistryOrDebug.SetResult(GetModel.FlowId, GetModel.NodeId, GetModel.NodeName, results); LastResults = results; System.Diagnostics.Debug.WriteLine($"[InitRun] {GetModel.NodeName}: {ex}"); return false; } } public NodeRunStatus Run(CancellationToken token) { IsSuccess = NodeRunStatus.Running; NodeRunStatus status; try { Dictionary results; status = PluginRun(token, out results); LastResults = results; IsSuccess = status; } catch (System.Exception) { IsSuccess = NodeRunStatus.Failed; status = NodeRunStatus.Failed; } return status; } /// /// 通过公式获取节点执行结果值 /// /// /// /// public T GetValue(FormulaBound formula) { if (formula == null) return default(T); object _result = new object(); _result = RegistryOrDebug.ResolveFormula(GetModel.FlowId, formula.Formula); if (_result == null || _result?.ToString() == "") return formula.Value; else if (typeof(T).IsInterface) { if (_result is T tResult) return tResult; else return default(T); } else return (T)System.Convert.ChangeType(_result, typeof(T)); } public object GetResolve(FormulaBound formula) { if (formula == null) return default(object); object _result = new object(); _result = RegistryOrDebug.ResolveFormula(GetModel.FlowId, formula.Formula); if (_result == null || _result?.ToString() == "") return formula.Value; else return _result; } /// /// 保存一条插件日志(像 GetValue 一样直接调用)。 /// level 是详细度等级(1低/2中/3高/4终):超过本插件设置的"日志等级"就跳过对应通道(存储/显示各一道,详见下行); /// 两道独立闸门:level ≤ 节点「存储详细度」才写文件(1/2 公共日志,3/4 流程/任务 独立路径);level ≤ 节点「显示详细度」才进实时日志控件(可存而不显、显而不存)。category 是类别(Info/Warning/Error),只作标记不影响存/显。 /// 例:Log(1, "开始执行"); Log(3, "通讯超时", TeamAAS.LogLevel.Warning); /// public void Log(int level, string message, TeamAAS.LogLevel category = TeamAAS.LogLevel.Info) => TeamAAS.Logging.PluginLogger.Log( Model.FlowName, Model.NodeName, Model.StoreVerbosity, Model.DisplayVerbosity, level, message, category); } }