| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- using PropertyGridLib.Attributes;
- using PropertyGridLib.Controls;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using TeamAAS.FlowEditor.Plugins;
- using TeamAAS.FlowEngine.FormulaData;
- namespace Plugins.Vm.Models
- {
- /// <summary>
- /// VM(海康 VisionMaster)流程/工具块节点模型。镜像 VppToolBlockModel 的结构,
- /// 但不直接持有 SDK 类型:方案/流程句柄用 object 承载并标记 [NonSerialized](运行态,不入库)。
- /// </summary>
- [Serializable]
- public class VmToolBlockModel : BasePluginModel
- {
- [Category("I.参数")]
- [DisplayName("1.VM方案路径")]
- [Description("海康 VisionMaster 方案(.sol)文件路径;运行前由适配层加载")]
- public string SolutionPath { get; set; } = "";
- [Category("I.参数")]
- [DisplayName("2.流程名")]
- [Description("方案内要执行的流程/工具块名称")]
- public string ProcedureName { get; set; } = "";
- [Category("I.参数")]
- [DisplayName("3.输入参数映射")]
- [Description("将主流程前序节点的输出映射到 VM 流程的输入终端。Key 必须与 VM 内输入名一致。")]
- public List<VmInputMapping> InputMappings { get; set; } = new List<VmInputMapping>();
- /// <summary>
- /// 输出终端名列表:VM 的输出终端在接入 SDK 前无法自动读取,先由用户在此声明,
- /// 使 <see cref="VmToolBlockPlugin.DeclareOutputs"/> 在跑之前就能声明输出、被下游绑定树索引。
- /// 接入 SDK 后可改为运行时自动学习并回填。
- /// </summary>
- [Category("I.参数")]
- [DisplayName("4.输出终端名")]
- [Description("声明 VM 流程的输出终端名(每行一个);用于运行前即可被下游绑定")]
- public List<string> OutputNames { get; set; } = new List<string>();
- /// <summary>上次运行学习到的“输出图层”数量,供 DeclareOutputs 在跑之前声明 输出图层N。</summary>
- [Browsable(false)]
- public int LastLayerCount { get; set; } = 0;
- /// <summary>已加载句柄对应的方案路径(变化时自动重载)。</summary>
- [Browsable(false)]
- public string LoadedSolutionPath { get; set; }
- /// <summary>已加载句柄对应的流程名(变化时自动重载)。</summary>
- [Browsable(false)]
- public string LoadedProcedureName { get; set; }
- // ── 高级编辑(属性面板按钮) ──
- // PropertyGridLib v1.1.2 [Button]:属性面板渲染按钮,点击反射调用 OpenAdvancedEditor。
- // 宿主可注入 AdvancedEditorLauncher 指到原生 VM 编辑入口;未注入时兜底用系统关联
- // 调起 VisionMaster 打开方案(原生编辑体验一致)。
- [NonSerialized]
- public Action AdvancedEditorLauncher;
- [Category("II.高级编辑")]
- [DisplayName("1.打开 VM 工具编辑")]
- [Description("打开海康 VisionMaster 原生界面编辑该工具块(与双击节点等效)")]
- [Button("打开 VM 工具编辑", nameof(OpenAdvancedEditor))]
- public object AdvancedEditButton { get; set; }
- public void OpenAdvancedEditor()
- {
- var launch = AdvancedEditorLauncher;
- if (launch != null) { launch(); return; }
- // 兜底:调起 VisionMaster 打开方案(经安全封装:扩展名白名单 + 参数列表启动)
- VmSolutionLauncher.OpenSolution(SolutionPath, null);
- }
- /// <summary>VM 方案句柄(运行态,不序列化)。</summary>
- [NonSerialized]
- public object Solution;
- /// <summary>VM 流程/工具块句柄(运行态,不序列化)。</summary>
- [NonSerialized]
- public object Procedure;
- }
- [Serializable]
- public class VmInputMapping
- {
- public VmInputMapping()
- {
- Source = new FormulaBound<object>();
- }
- [Category("输入映射")]
- [DisplayName("输入名")]
- [Description("必须与 VM 流程里定义的输入终端名完全一致")]
- public string InputName { get; set; } = "";
- [Category("输入映射")]
- [DisplayName("来源值")]
- [Description("从主流程前序节点的输出结果中选择")]
- [FormulaEditor(typeof(InputSourceDataProvider))]
- public FormulaBound<object> Source { get; set; }
- public override string ToString() => $"{InputName} ← {Source?.Formula ?? ""}";
- }
- /// <summary>
- /// VisionMaster 方案安全启动封装。
- /// 安全约束(防命令注入/任意执行):
- /// 1. 扩展名白名单——仅允许 .sol(VisionMaster 方案),配置成 .bat/.exe 等一律拒绝;
- /// 2. 配置了 VM 客户端路径时用「参数列表」方式启动(UseShellExecute=false),
- /// 方案路径作为单个带引号参数传入,不拼接任何 shell 命令;
- /// 3. 未配置客户端时走系统文件关联打开 .sol(仍受白名单限制)。
- /// </summary>
- public static class VmSolutionLauncher
- {
- public static bool OpenSolution(string solutionPath, string vmClientPath)
- {
- if (string.IsNullOrWhiteSpace(solutionPath) || !System.IO.File.Exists(solutionPath))
- {
- TeamAAS.AppLogger.Warning($"VM 方案文件不存在,无法打开编辑: {solutionPath}", "Vm");
- return false;
- }
- if (!string.Equals(System.IO.Path.GetExtension(solutionPath), ".sol", StringComparison.OrdinalIgnoreCase))
- {
- TeamAAS.AppLogger.Warning($"仅支持打开 .sol 方案文件,已拒绝: {solutionPath}", "Vm");
- return false;
- }
- try
- {
- if (!string.IsNullOrWhiteSpace(vmClientPath) && System.IO.File.Exists(vmClientPath))
- {
- System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
- {
- FileName = vmClientPath,
- Arguments = "\"" + solutionPath + "\"",
- UseShellExecute = false,
- });
- return true;
- }
- System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
- {
- FileName = solutionPath,
- Verb = "open",
- UseShellExecute = true,
- });
- return true;
- }
- catch (Exception ex)
- {
- TeamAAS.AppLogger.Error("调起 VisionMaster 失败", ex, "Vm");
- return false;
- }
- }
- }
- }
|