diff --git a/.windsurf/rules/projectrules.md b/.windsurf/rules/projectrules.md
new file mode 100644
index 0000000..75edeab
--- /dev/null
+++ b/.windsurf/rules/projectrules.md
@@ -0,0 +1,10 @@
+---
+trigger: always_on
+---
+## 当前是使用海康的VisionMaster 算法平台进行二次开发的程序
+
+## 你可以联网查询你需要的信息,当前的SDK下的VisionMaster算法平台SDK开发指南V4.3.1(.NET).chm 是开发文档,你可以参考使用
+
+## 使用VisionMaster 易于使用的开发界面和Winform 灵活的二次开发的方式组合使用,完成客户定制化的要求
+
+## 程序需要详细的注释
diff --git a/AwakenGPUToolLog.txt b/AwakenGPUToolLog.txt
new file mode 100644
index 0000000..227a285
--- /dev/null
+++ b/AwakenGPUToolLog.txt
@@ -0,0 +1,2 @@
+Awake Gpu start
+GPU OK.
diff --git a/HkVisionPro.App/App.config b/HkVisionPro.App/App.config
index 186a2d1..b1ac8fd 100644
--- a/HkVisionPro.App/App.config
+++ b/HkVisionPro.App/App.config
@@ -5,6 +5,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/HkVisionPro.App/HkVisionPro.App.csproj b/HkVisionPro.App/HkVisionPro.App.csproj
index 9ffc377..1c84c66 100644
--- a/HkVisionPro.App/HkVisionPro.App.csproj
+++ b/HkVisionPro.App/HkVisionPro.App.csproj
@@ -194,6 +194,10 @@
False
+
+
+ ..\packages\ReaLTaiizor.3.8.1.5\lib\net48\ReaLTaiizor.dll
+
False
@@ -281,6 +285,10 @@
False
+
+ ..\packages\Stateless.5.20.1\lib\net462\Stateless.dll
+
+
False
@@ -290,9 +298,6 @@
False
-
- False
-
False
@@ -571,6 +576,7 @@
+
@@ -600,6 +606,7 @@
Resources.resx
True
+
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -609,11 +616,12 @@
Settings.settings
True
+
+ PreserveNewest
+
-
-
-
+
\ No newline at end of file
diff --git a/HkVisionPro.App/MainForm.Designer.cs b/HkVisionPro.App/MainForm.Designer.cs
index 7c53ccb..08b86a2 100644
--- a/HkVisionPro.App/MainForm.Designer.cs
+++ b/HkVisionPro.App/MainForm.Designer.cs
@@ -30,16 +30,44 @@
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.btnLoadSolution = new System.Windows.Forms.Button();
- this.vmRenderControl1 = new VMControls.Winform.Release.VmRenderControl();
this.txtSolutionAddress = new System.Windows.Forms.TextBox();
this.btnSelctedSolution = new System.Windows.Forms.Button();
this.btnExecutSolution = new System.Windows.Forms.Button();
this.btnSaveSolution = new System.Windows.Forms.Button();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.cmbProcdure = new System.Windows.Forms.ComboBox();
+ this.label4 = new System.Windows.Forms.Label();
+ this.vmProcedureConfigControl1 = new VMControls.Winform.Release.VmProcedureConfigControl();
+ this.groupBox2 = new System.Windows.Forms.GroupBox();
+ this.btnProExecStop = new System.Windows.Forms.Button();
+ this.btnProExecAlway = new System.Windows.Forms.Button();
+ this.btnProExecOnce = new System.Windows.Forms.Button();
+ this.btnProDel = new System.Windows.Forms.Button();
+ this.btnProExpo = new System.Windows.Forms.Button();
+ this.btnProImport = new System.Windows.Forms.Button();
+ this.groupBox3 = new System.Windows.Forms.GroupBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.cmbModule = new System.Windows.Forms.ComboBox();
+ this.btnModuleRenderResult = new System.Windows.Forms.Button();
+ this.btnModuleExec = new System.Windows.Forms.Button();
+ this.btnModuleBindingPar = new System.Windows.Forms.Button();
+ this.listBox1 = new System.Windows.Forms.ListBox();
+ this.groupBox4 = new System.Windows.Forms.GroupBox();
+ this.pictureBox1 = new System.Windows.Forms.PictureBox();
+ this.groupBox6 = new System.Windows.Forms.GroupBox();
+ this.vmParamsConfigControl1 = new VMControls.Winform.Release.VmParamsConfigControl();
+ this.groupBox1.SuspendLayout();
+ this.groupBox2.SuspendLayout();
+ this.groupBox3.SuspendLayout();
+ this.groupBox4.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
+ this.groupBox6.SuspendLayout();
this.SuspendLayout();
//
// btnLoadSolution
//
- this.btnLoadSolution.Location = new System.Drawing.Point(784, 156);
+ this.btnLoadSolution.Location = new System.Drawing.Point(103, 93);
this.btnLoadSolution.Name = "btnLoadSolution";
this.btnLoadSolution.Size = new System.Drawing.Size(127, 38);
this.btnLoadSolution.TabIndex = 0;
@@ -47,28 +75,16 @@
this.btnLoadSolution.UseVisualStyleBackColor = true;
this.btnLoadSolution.Click += new System.EventHandler(this.btnLoadSolution_Click);
//
- // vmRenderControl1
- //
- this.vmRenderControl1.BackColor = System.Drawing.Color.Black;
- this.vmRenderControl1.CoordinateInfoVisible = true;
- this.vmRenderControl1.ImageSource = null;
- this.vmRenderControl1.Location = new System.Drawing.Point(1, 1);
- this.vmRenderControl1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
- this.vmRenderControl1.ModuleSource = null;
- this.vmRenderControl1.Name = "vmRenderControl1";
- this.vmRenderControl1.Size = new System.Drawing.Size(776, 742);
- this.vmRenderControl1.TabIndex = 1;
- //
// txtSolutionAddress
//
- this.txtSolutionAddress.Location = new System.Drawing.Point(784, 39);
+ this.txtSolutionAddress.Location = new System.Drawing.Point(103, 20);
this.txtSolutionAddress.Name = "txtSolutionAddress";
- this.txtSolutionAddress.Size = new System.Drawing.Size(386, 23);
+ this.txtSolutionAddress.Size = new System.Drawing.Size(393, 23);
this.txtSolutionAddress.TabIndex = 2;
//
// btnSelctedSolution
//
- this.btnSelctedSolution.Location = new System.Drawing.Point(1043, 68);
+ this.btnSelctedSolution.Location = new System.Drawing.Point(369, 49);
this.btnSelctedSolution.Name = "btnSelctedSolution";
this.btnSelctedSolution.Size = new System.Drawing.Size(127, 38);
this.btnSelctedSolution.TabIndex = 3;
@@ -78,7 +94,7 @@
//
// btnExecutSolution
//
- this.btnExecutSolution.Location = new System.Drawing.Point(917, 156);
+ this.btnExecutSolution.Location = new System.Drawing.Point(236, 93);
this.btnExecutSolution.Name = "btnExecutSolution";
this.btnExecutSolution.Size = new System.Drawing.Size(127, 38);
this.btnExecutSolution.TabIndex = 4;
@@ -88,7 +104,7 @@
//
// btnSaveSolution
//
- this.btnSaveSolution.Location = new System.Drawing.Point(1050, 156);
+ this.btnSaveSolution.Location = new System.Drawing.Point(369, 93);
this.btnSaveSolution.Name = "btnSaveSolution";
this.btnSaveSolution.Size = new System.Drawing.Size(127, 38);
this.btnSaveSolution.TabIndex = 5;
@@ -96,35 +112,316 @@
this.btnSaveSolution.UseVisualStyleBackColor = true;
this.btnSaveSolution.Click += new System.EventHandler(this.btnSaveSolution_Click);
//
+ // groupBox1
+ //
+ this.groupBox1.Controls.Add(this.label3);
+ this.groupBox1.Controls.Add(this.btnSelctedSolution);
+ this.groupBox1.Controls.Add(this.btnSaveSolution);
+ this.groupBox1.Controls.Add(this.btnExecutSolution);
+ this.groupBox1.Controls.Add(this.txtSolutionAddress);
+ this.groupBox1.Controls.Add(this.btnLoadSolution);
+ this.groupBox1.Location = new System.Drawing.Point(882, 4);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(505, 144);
+ this.groupBox1.TabIndex = 6;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "方案操作";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(30, 23);
+ this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(68, 17);
+ this.label3.TabIndex = 6;
+ this.label3.Text = "方案路径:";
+ //
+ // cmbProcdure
+ //
+ this.cmbProcdure.FormattingEnabled = true;
+ this.cmbProcdure.Location = new System.Drawing.Point(188, 21);
+ this.cmbProcdure.Margin = new System.Windows.Forms.Padding(2);
+ this.cmbProcdure.Name = "cmbProcdure";
+ this.cmbProcdure.Size = new System.Drawing.Size(307, 25);
+ this.cmbProcdure.TabIndex = 8;
+ this.cmbProcdure.DropDown += new System.EventHandler(this.cmbProcdure_DropDown);
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(103, 24);
+ this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(68, 17);
+ this.label4.TabIndex = 7;
+ this.label4.Text = "流程列表:";
+ //
+ // vmProcedureConfigControl1
+ //
+ this.vmProcedureConfigControl1.Dock = System.Windows.Forms.DockStyle.Left;
+ this.vmProcedureConfigControl1.Location = new System.Drawing.Point(20, 60);
+ this.vmProcedureConfigControl1.Margin = new System.Windows.Forms.Padding(2);
+ this.vmProcedureConfigControl1.Name = "vmProcedureConfigControl1";
+ this.vmProcedureConfigControl1.Size = new System.Drawing.Size(439, 781);
+ this.vmProcedureConfigControl1.TabIndex = 7;
+// TODO: “”的代码生成失败,原因是出现异常“无效的基元类型: System.IntPtr。请考虑使用 CodeObjectCreateExpression。”。
+ //
+ // groupBox2
+ //
+ this.groupBox2.Controls.Add(this.cmbProcdure);
+ this.groupBox2.Controls.Add(this.label4);
+ this.groupBox2.Controls.Add(this.btnProExecStop);
+ this.groupBox2.Controls.Add(this.btnProExecAlway);
+ this.groupBox2.Controls.Add(this.btnProExecOnce);
+ this.groupBox2.Controls.Add(this.btnProDel);
+ this.groupBox2.Controls.Add(this.btnProExpo);
+ this.groupBox2.Controls.Add(this.btnProImport);
+ this.groupBox2.Location = new System.Drawing.Point(882, 151);
+ this.groupBox2.Name = "groupBox2";
+ this.groupBox2.Size = new System.Drawing.Size(505, 145);
+ this.groupBox2.TabIndex = 7;
+ this.groupBox2.TabStop = false;
+ this.groupBox2.Text = "流程操作";
+ //
+ // btnProExecStop
+ //
+ this.btnProExecStop.Location = new System.Drawing.Point(372, 97);
+ this.btnProExecStop.Name = "btnProExecStop";
+ this.btnProExecStop.Size = new System.Drawing.Size(127, 38);
+ this.btnProExecStop.TabIndex = 8;
+ this.btnProExecStop.Text = "停止执行";
+ this.btnProExecStop.UseVisualStyleBackColor = true;
+ this.btnProExecStop.Click += new System.EventHandler(this.btnProExecStop_Click);
+ //
+ // btnProExecAlway
+ //
+ this.btnProExecAlway.Location = new System.Drawing.Point(239, 97);
+ this.btnProExecAlway.Name = "btnProExecAlway";
+ this.btnProExecAlway.Size = new System.Drawing.Size(127, 38);
+ this.btnProExecAlway.TabIndex = 7;
+ this.btnProExecAlway.Text = "连续执行";
+ this.btnProExecAlway.UseVisualStyleBackColor = true;
+ this.btnProExecAlway.Click += new System.EventHandler(this.btnProExecAlway_Click);
+ //
+ // btnProExecOnce
+ //
+ this.btnProExecOnce.Location = new System.Drawing.Point(106, 97);
+ this.btnProExecOnce.Name = "btnProExecOnce";
+ this.btnProExecOnce.Size = new System.Drawing.Size(127, 38);
+ this.btnProExecOnce.TabIndex = 6;
+ this.btnProExecOnce.Text = "执行一次";
+ this.btnProExecOnce.UseVisualStyleBackColor = true;
+ this.btnProExecOnce.Click += new System.EventHandler(this.btnProExecOnce_Click);
+ //
+ // btnProDel
+ //
+ this.btnProDel.Location = new System.Drawing.Point(372, 53);
+ this.btnProDel.Name = "btnProDel";
+ this.btnProDel.Size = new System.Drawing.Size(127, 38);
+ this.btnProDel.TabIndex = 5;
+ this.btnProDel.Text = "删除";
+ this.btnProDel.UseVisualStyleBackColor = true;
+ this.btnProDel.Click += new System.EventHandler(this.btnProDel_Click);
+ //
+ // btnProExpo
+ //
+ this.btnProExpo.Location = new System.Drawing.Point(239, 53);
+ this.btnProExpo.Name = "btnProExpo";
+ this.btnProExpo.Size = new System.Drawing.Size(127, 38);
+ this.btnProExpo.TabIndex = 4;
+ this.btnProExpo.Text = "导出";
+ this.btnProExpo.UseVisualStyleBackColor = true;
+ this.btnProExpo.Click += new System.EventHandler(this.btnProExpo_Click);
+ //
+ // btnProImport
+ //
+ this.btnProImport.Location = new System.Drawing.Point(106, 53);
+ this.btnProImport.Name = "btnProImport";
+ this.btnProImport.Size = new System.Drawing.Size(127, 38);
+ this.btnProImport.TabIndex = 0;
+ this.btnProImport.Text = "导入";
+ this.btnProImport.UseVisualStyleBackColor = true;
+ this.btnProImport.Click += new System.EventHandler(this.btnProImport_Click);
+ //
+ // groupBox3
+ //
+ this.groupBox3.Controls.Add(this.label2);
+ this.groupBox3.Controls.Add(this.cmbModule);
+ this.groupBox3.Controls.Add(this.btnModuleRenderResult);
+ this.groupBox3.Controls.Add(this.btnModuleExec);
+ this.groupBox3.Controls.Add(this.btnModuleBindingPar);
+ this.groupBox3.Location = new System.Drawing.Point(882, 302);
+ this.groupBox3.Name = "groupBox3";
+ this.groupBox3.Size = new System.Drawing.Size(505, 112);
+ this.groupBox3.TabIndex = 9;
+ this.groupBox3.TabStop = false;
+ this.groupBox3.Text = "模块操作";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(33, 22);
+ this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(68, 17);
+ this.label2.TabIndex = 9;
+ this.label2.Text = "模块列表:";
+ //
+ // cmbModule
+ //
+ this.cmbModule.FormattingEnabled = true;
+ this.cmbModule.Location = new System.Drawing.Point(106, 19);
+ this.cmbModule.Name = "cmbModule";
+ this.cmbModule.Size = new System.Drawing.Size(389, 25);
+ this.cmbModule.TabIndex = 9;
+ this.cmbModule.DropDown += new System.EventHandler(this.cmbModule_DropDown);
+ //
+ // btnModuleRenderResult
+ //
+ this.btnModuleRenderResult.Location = new System.Drawing.Point(372, 61);
+ this.btnModuleRenderResult.Name = "btnModuleRenderResult";
+ this.btnModuleRenderResult.Size = new System.Drawing.Size(127, 38);
+ this.btnModuleRenderResult.TabIndex = 5;
+ this.btnModuleRenderResult.Text = "渲染结果";
+ this.btnModuleRenderResult.UseVisualStyleBackColor = true;
+ this.btnModuleRenderResult.Click += new System.EventHandler(this.btnModuleRenderResult_Click);
+ //
+ // btnModuleExec
+ //
+ this.btnModuleExec.Location = new System.Drawing.Point(239, 61);
+ this.btnModuleExec.Name = "btnModuleExec";
+ this.btnModuleExec.Size = new System.Drawing.Size(127, 38);
+ this.btnModuleExec.TabIndex = 4;
+ this.btnModuleExec.Text = "执行模块";
+ this.btnModuleExec.UseVisualStyleBackColor = true;
+ this.btnModuleExec.Click += new System.EventHandler(this.btnModuleExec_Click);
+ //
+ // btnModuleBindingPar
+ //
+ this.btnModuleBindingPar.Location = new System.Drawing.Point(106, 61);
+ this.btnModuleBindingPar.Name = "btnModuleBindingPar";
+ this.btnModuleBindingPar.Size = new System.Drawing.Size(127, 38);
+ this.btnModuleBindingPar.TabIndex = 0;
+ this.btnModuleBindingPar.Text = "绑定参数";
+ this.btnModuleBindingPar.UseVisualStyleBackColor = true;
+ this.btnModuleBindingPar.Click += new System.EventHandler(this.btnModuleBindingPar_Click);
+ //
+ // listBox1
+ //
+ this.listBox1.FormattingEnabled = true;
+ this.listBox1.ItemHeight = 17;
+ this.listBox1.Location = new System.Drawing.Point(882, 422);
+ this.listBox1.Name = "listBox1";
+ this.listBox1.Size = new System.Drawing.Size(516, 429);
+ this.listBox1.TabIndex = 10;
+ //
+ // groupBox4
+ //
+ this.groupBox4.Controls.Add(this.pictureBox1);
+ this.groupBox4.Location = new System.Drawing.Point(463, 2);
+ this.groupBox4.Margin = new System.Windows.Forms.Padding(2);
+ this.groupBox4.Name = "groupBox4";
+ this.groupBox4.Padding = new System.Windows.Forms.Padding(2);
+ this.groupBox4.Size = new System.Drawing.Size(414, 412);
+ this.groupBox4.TabIndex = 11;
+ this.groupBox4.TabStop = false;
+ this.groupBox4.Text = "视图窗口";
+ //
+ // pictureBox1
+ //
+ this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.pictureBox1.Location = new System.Drawing.Point(2, 18);
+ this.pictureBox1.Name = "pictureBox1";
+ this.pictureBox1.Size = new System.Drawing.Size(410, 392);
+ this.pictureBox1.TabIndex = 0;
+ this.pictureBox1.TabStop = false;
+ //
+ // groupBox6
+ //
+ this.groupBox6.Controls.Add(this.vmParamsConfigControl1);
+ this.groupBox6.Location = new System.Drawing.Point(463, 422);
+ this.groupBox6.Margin = new System.Windows.Forms.Padding(2);
+ this.groupBox6.Name = "groupBox6";
+ this.groupBox6.Padding = new System.Windows.Forms.Padding(2);
+ this.groupBox6.Size = new System.Drawing.Size(409, 439);
+ this.groupBox6.TabIndex = 12;
+ this.groupBox6.TabStop = false;
+ this.groupBox6.Text = "参数配置";
+ //
+ // vmParamsConfigControl1
+ //
+ this.vmParamsConfigControl1.BackColor = System.Drawing.Color.White;
+ this.vmParamsConfigControl1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.vmParamsConfigControl1.Location = new System.Drawing.Point(2, 18);
+ this.vmParamsConfigControl1.ModuleSource = null;
+ this.vmParamsConfigControl1.Name = "vmParamsConfigControl1";
+ this.vmParamsConfigControl1.ParamsConfig = null;
+ this.vmParamsConfigControl1.Size = new System.Drawing.Size(405, 419);
+ this.vmParamsConfigControl1.TabIndex = 0;
+ //
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(1182, 744);
- this.Controls.Add(this.btnSaveSolution);
- this.Controls.Add(this.btnExecutSolution);
- this.Controls.Add(this.btnSelctedSolution);
- this.Controls.Add(this.txtSolutionAddress);
- this.Controls.Add(this.vmRenderControl1);
- this.Controls.Add(this.btnLoadSolution);
+ this.ClientSize = new System.Drawing.Size(1400, 861);
+ this.Controls.Add(this.groupBox6);
+ this.Controls.Add(this.groupBox4);
+ this.Controls.Add(this.listBox1);
+ this.Controls.Add(this.groupBox3);
+ this.Controls.Add(this.groupBox2);
+ this.Controls.Add(this.vmProcedureConfigControl1);
+ this.Controls.Add(this.groupBox1);
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "MainForm";
this.Text = "主窗体";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
+ this.Load += new System.EventHandler(this.MainForm_Load);
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox1.PerformLayout();
+ this.groupBox2.ResumeLayout(false);
+ this.groupBox2.PerformLayout();
+ this.groupBox3.ResumeLayout(false);
+ this.groupBox3.PerformLayout();
+ this.groupBox4.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
+ this.groupBox6.ResumeLayout(false);
this.ResumeLayout(false);
- this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnLoadSolution;
- private VMControls.Winform.Release.VmRenderControl vmRenderControl1;
private System.Windows.Forms.TextBox txtSolutionAddress;
private System.Windows.Forms.Button btnSelctedSolution;
private System.Windows.Forms.Button btnExecutSolution;
private System.Windows.Forms.Button btnSaveSolution;
+ private System.Windows.Forms.GroupBox groupBox1;
+ private VMControls.Winform.Release.VmProcedureConfigControl vmProcedureConfigControl1;
+ private System.Windows.Forms.GroupBox groupBox2;
+ private System.Windows.Forms.Button btnProDel;
+ private System.Windows.Forms.Button btnProExpo;
+ private System.Windows.Forms.Button btnProImport;
+ private System.Windows.Forms.Button btnProExecStop;
+ private System.Windows.Forms.Button btnProExecAlway;
+ private System.Windows.Forms.Button btnProExecOnce;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.GroupBox groupBox3;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.ComboBox cmbModule;
+ private System.Windows.Forms.Button btnModuleRenderResult;
+ private System.Windows.Forms.Button btnModuleExec;
+ private System.Windows.Forms.Button btnModuleBindingPar;
+ private System.Windows.Forms.ListBox listBox1;
+ private System.Windows.Forms.GroupBox groupBox4;
+ private System.Windows.Forms.GroupBox groupBox6;
+ private VMControls.Winform.Release.VmParamsConfigControl vmParamsConfigControl1;
+ private System.Windows.Forms.ComboBox cmbProcdure;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.PictureBox pictureBox1;
}
}
diff --git a/HkVisionPro.App/MainForm.cs b/HkVisionPro.App/MainForm.cs
index 8bf8dab..e865cb4 100644
--- a/HkVisionPro.App/MainForm.cs
+++ b/HkVisionPro.App/MainForm.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
@@ -7,32 +8,2113 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
+using System.Configuration;
+using ReaLTaiizor.Enum.Poison;
+using ReaLTaiizor.Forms;
+using ReaLTaiizor.Manager;
+using VM.PlatformSDKCS;
+using VM.Core;
+using IMVSBcrModuCs;
+using PointSetMODU_STDCs;
namespace HkVisionPro.App
{
- public partial class MainForm : Form
+ public partial class MainForm : PoisonForm
{
+ ///
+ /// VisionMaster 方案管理对象(负责加载、执行、保存方案)。
+ ///
+ private readonly SolutionManager _solutionManager;
+
+ ///
+ /// 当前已加载的方案文件完整路径。
+ ///
+ private string _currentSolutionPath = string.Empty;
+
+ ///
+ /// 当前待完成加载的方案路径(用于异步加载结束时提交状态)。
+ ///
+ private string _pendingLoadSolutionPath = string.Empty;
+
+ ///
+ /// 当前是否已成功加载方案。
+ ///
+ private bool _isSolutionLoaded;
+
+ ///
+ /// 当前是否正在进行加载/执行/保存过程(用于按钮防重入)。
+ ///
+ private bool _isBusy;
+
+ ///
+ /// 当前是否处于方案加载过程(SDK 可能异步加载,需显式防重入)。
+ ///
+ private bool _isSolutionLoading;
+
+ ///
+ /// 当前是否处于流程连续执行状态。
+ ///
+ private bool _isContinuousRunning;
+
+ ///
+ /// 当前连续执行的流程名称。
+ ///
+ private string _continuousProcedureName = string.Empty;
+
+ ///
+ /// 渲染互斥标记(0=空闲,1=渲染中),用于避免连续执行阶段并发读取结果。
+ ///
+ private int _renderingGuard;
+
+ ///
+ /// 当前选择的流程对象(用于流程执行与结果读取)。
+ ///
+ private VmProcedure _currentProcedure;
+
+ ///
+ /// 当前激活的条码模块工具对象(用于结果回调订阅与解绑)。
+ ///
+ private IMVSBcrModuTool _activeBcrTool;
+
+ ///
+ /// VM.Core 方案事件是否已注册。
+ ///
+ private bool _isVmSolutionEventRegistered;
+
+ ///
+ /// 关闭阶段资源是否已释放(用于防止 FormClosing 与 OnFormClosed 重复释放)。
+ ///
+ private bool _isClosingResourcesReleased;
+
+ ///
+ /// 流程字符串输出名称(支持配置,默认 out)。
+ ///
+ private readonly string _procedureOutputCodeName;
+
+ ///
+ /// 流程整数输出名称(支持配置,默认 out0)。
+ ///
+ private readonly string _procedureOutputNumberName;
+
+ ///
+ /// 渲染图像输出名称(支持配置,默认 Image)。
+ ///
+ private readonly string _renderImageOutputName;
+
+ ///
+ /// 渲染 ROI 输出名称(支持配置,默认 ROI)。
+ ///
+ private readonly string _renderRoiOutputName;
+
+ ///
+ /// 渲染文本输出名称(支持配置,默认 code)。
+ ///
+ private readonly string _renderCodeOutputName;
+
+ ///
+ /// VisionMaster: 方案正在加载中的错误码(IMVS_EC_SOLUTION_LOADING)。
+ ///
+ private const int VmErrorCodeSolutionLoading = -536870899;
+
+ ///
+ /// VisionMaster: 模块已处于连续执行中的错误码(IMVS_EC_MODULE_CONTINUE_EXECUTE)。
+ ///
+ private const int VmErrorCodeModuleContinueExecute = -536870127;
+
+ ///
+ /// ReaLTaiizor Poison 样式管理器(用于统一暗色主题风格元数据)。
+ ///
+ private readonly PoisonStyleManager _poisonStyleManager;
+
+ ///
+ /// 主背景颜色。
+ ///
+ private static readonly Color DarkThemeMainBackColor = Color.FromArgb(28, 28, 30);
+
+ ///
+ /// 分组容器背景颜色。
+ ///
+ private static readonly Color DarkThemePanelBackColor = Color.FromArgb(36, 36, 40);
+
+ ///
+ /// 输入类控件背景颜色。
+ ///
+ private static readonly Color DarkThemeInputBackColor = Color.FromArgb(45, 45, 48);
+
+ ///
+ /// 前景文字颜色。
+ ///
+ private static readonly Color DarkThemeForeColor = Color.FromArgb(230, 230, 230);
+
+ ///
+ /// 主题强调色(按钮边框、选中态)。
+ ///
+ private static readonly Color DarkThemeAccentColor = Color.FromArgb(0, 174, 219);
+
+ ///
+ /// 分组框边框颜色。
+ ///
+ private static readonly Color DarkThemeGroupBorderColor = Color.FromArgb(78, 78, 82);
+
+ ///
+ /// 分组框标题底色。
+ ///
+ private static readonly Color DarkThemeGroupTitleBackColor = Color.FromArgb(48, 48, 52);
+
+ ///
+ /// 列表选中项背景色。
+ ///
+ private static readonly Color DarkThemeSelectedBackColor = Color.FromArgb(0, 120, 170);
+
public MainForm()
{
InitializeComponent();
+ _poisonStyleManager = components == null ? new PoisonStyleManager() : new PoisonStyleManager(components);
+ _solutionManager = new SolutionManager();
+ _procedureOutputCodeName = GetAppSettingOrDefault("ProcedureOutputCodeName", "out");
+ _procedureOutputNumberName = GetAppSettingOrDefault("ProcedureOutputNumberName", "out0");
+ _renderImageOutputName = GetAppSettingOrDefault("RenderImageOutputName", "Image");
+ _renderRoiOutputName = GetAppSettingOrDefault("RenderRoiOutputName", "ROI");
+ _renderCodeOutputName = GetAppSettingOrDefault("RenderCodeOutputName", "code");
+ RegisterSolutionCallbacks();
+ RegisterVmSolutionCallbacks();
+
+ // 统一联动:流程切换时自动刷新模块列表。
+ cmbProcdure.SelectedIndexChanged += cmbProcdure_SelectedIndexChanged;
+
+ // 视图改为 pictureBox 展示时,统一设置渲染行为。
+ pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
+ pictureBox1.BackColor = Color.Black;
+
+ InitializeDarkTheme();
+
+ UpdateUiState(false);
+ txtSolutionAddress.Text = string.Empty;
}
+ ///
+ /// 初始化 ReaLTaiizor 暗色主题,并将原生 WinForms 控件统一成暗色风格。
+ ///
+ private void InitializeDarkTheme()
+ {
+ _poisonStyleManager.Owner = this;
+ _poisonStyleManager.Theme = ThemeStyle.Dark;
+ _poisonStyleManager.Style = ColorStyle.Teal;
+ StyleManager = _poisonStyleManager;
+ Theme = ThemeStyle.Dark;
+ Style = ColorStyle.Teal;
+ ApplyDarkThemeToNativeControls(this);
+ }
+
+ ///
+ /// 递归应用暗色主题到原生 WinForms 控件。
+ ///
+ /// 递归起始控件。
+ private void ApplyDarkThemeToNativeControls(Control rootControl)
+ {
+ if (rootControl == null)
+ {
+ return;
+ }
+
+ if (ReferenceEquals(rootControl, this))
+ {
+ BackColor = DarkThemeMainBackColor;
+ ForeColor = DarkThemeForeColor;
+ }
+
+ foreach (Control control in rootControl.Controls)
+ {
+ if (ReferenceEquals(control, vmProcedureConfigControl1) || ReferenceEquals(control, vmParamsConfigControl1))
+ {
+ continue;
+ }
+
+ if (control is GroupBox groupBox)
+ {
+ ApplyGroupBoxTheme(groupBox);
+ }
+ else if (control is Button button)
+ {
+ ApplyButtonTheme(button);
+ }
+ else if (control is TextBox textBox)
+ {
+ ApplyTextBoxTheme(textBox);
+ }
+ else if (control is ComboBox comboBox)
+ {
+ ApplyComboBoxTheme(comboBox);
+ }
+ else if (control is ListBox listBox)
+ {
+ ApplyListBoxTheme(listBox);
+ }
+ else if (control is Label label)
+ {
+ ApplyLabelTheme(label);
+ }
+ else if (control is PictureBox pictureBox)
+ {
+ ApplyPictureBoxTheme(pictureBox);
+ }
+ else
+ {
+ control.BackColor = DarkThemeMainBackColor;
+ control.ForeColor = DarkThemeForeColor;
+ }
+
+ if (control.HasChildren)
+ {
+ ApplyDarkThemeToNativeControls(control);
+ }
+ }
+ }
+
+ ///
+ /// 设置分组框控件暗色样式。
+ ///
+ /// 目标分组框。
+ private void ApplyGroupBoxTheme(GroupBox groupBox)
+ {
+ groupBox.BackColor = DarkThemePanelBackColor;
+ groupBox.ForeColor = DarkThemeForeColor;
+ groupBox.Paint -= ThemedGroupBox_Paint;
+ groupBox.Paint += ThemedGroupBox_Paint;
+ }
+
+ ///
+ /// 自定义绘制分组框标题与边框,统一暗色视觉层级。
+ ///
+ /// 事件发起对象。
+ /// 绘制事件参数。
+ private void ThemedGroupBox_Paint(object sender, PaintEventArgs e)
+ {
+ var groupBox = sender as GroupBox;
+ if (groupBox == null)
+ {
+ return;
+ }
+
+ e.Graphics.Clear(groupBox.BackColor);
+
+ var groupText = groupBox.Text ?? string.Empty;
+ var textSize = TextRenderer.MeasureText(groupText, groupBox.Font);
+ var textRect = new Rectangle(12, 0, textSize.Width + 8, textSize.Height);
+ var borderRect = new Rectangle(
+ 1,
+ Math.Max(1, textRect.Height / 2),
+ Math.Max(1, groupBox.Width - 2),
+ Math.Max(1, groupBox.Height - (textRect.Height / 2) - 2));
+
+ using (var borderPen = new Pen(DarkThemeGroupBorderColor))
+ {
+ e.Graphics.DrawRectangle(borderPen, borderRect);
+ }
+
+ using (var titleBackBrush = new SolidBrush(DarkThemeGroupTitleBackColor))
+ {
+ e.Graphics.FillRectangle(titleBackBrush, textRect);
+ }
+
+ TextRenderer.DrawText(
+ e.Graphics,
+ groupText,
+ groupBox.Font,
+ textRect,
+ DarkThemeForeColor,
+ TextFormatFlags.Left | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
+ }
+
+ ///
+ /// 设置按钮控件暗色样式。
+ ///
+ /// 目标按钮。
+ private void ApplyButtonTheme(Button button)
+ {
+ button.FlatStyle = FlatStyle.Flat;
+ button.FlatAppearance.BorderSize = 1;
+ button.FlatAppearance.BorderColor = DarkThemeAccentColor;
+ button.FlatAppearance.MouseOverBackColor = Color.FromArgb(52, 52, 56);
+ button.FlatAppearance.MouseDownBackColor = Color.FromArgb(62, 62, 66);
+ button.BackColor = DarkThemePanelBackColor;
+ button.ForeColor = DarkThemeForeColor;
+ button.UseVisualStyleBackColor = false;
+ button.Paint -= ThemedButton_Paint;
+ button.Paint += ThemedButton_Paint;
+ }
+
+ ///
+ /// 自定义绘制按钮文本,确保暗色主题下文字始终可读。
+ ///
+ /// 事件发起对象。
+ /// 绘制事件参数。
+ private void ThemedButton_Paint(object sender, PaintEventArgs e)
+ {
+ var button = sender as Button;
+ if (button == null)
+ {
+ return;
+ }
+
+ var textColor = button.Enabled ? DarkThemeForeColor : Color.FromArgb(170, 170, 170);
+ var textBounds = button.ClientRectangle;
+ TextRenderer.DrawText(
+ e.Graphics,
+ button.Text,
+ button.Font,
+ textBounds,
+ textColor,
+ TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis);
+ }
+
+ ///
+ /// 设置文本框控件暗色样式。
+ ///
+ /// 目标文本框。
+ private void ApplyTextBoxTheme(TextBox textBox)
+ {
+ textBox.BorderStyle = BorderStyle.FixedSingle;
+ textBox.BackColor = DarkThemeInputBackColor;
+ textBox.ForeColor = DarkThemeForeColor;
+ }
+
+ ///
+ /// 设置下拉框控件暗色样式,并接管绘制以统一列表区域颜色。
+ ///
+ /// 目标下拉框。
+ private void ApplyComboBoxTheme(ComboBox comboBox)
+ {
+ comboBox.FlatStyle = FlatStyle.Flat;
+ comboBox.BackColor = DarkThemeInputBackColor;
+ comboBox.ForeColor = DarkThemeForeColor;
+ comboBox.DrawMode = DrawMode.OwnerDrawFixed;
+ comboBox.DrawItem -= ThemedComboBox_DrawItem;
+ comboBox.DrawItem += ThemedComboBox_DrawItem;
+ }
+
+ ///
+ /// 设置列表框控件暗色样式,并接管绘制以统一选中高亮。
+ ///
+ /// 目标列表框。
+ private void ApplyListBoxTheme(ListBox listBox)
+ {
+ listBox.BorderStyle = BorderStyle.FixedSingle;
+ listBox.BackColor = DarkThemeInputBackColor;
+ listBox.ForeColor = DarkThemeForeColor;
+ listBox.Font = new Font("微软雅黑", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
+ listBox.ItemHeight = Math.Max(18, TextRenderer.MeasureText("日志", listBox.Font).Height + 2);
+ listBox.DrawMode = DrawMode.OwnerDrawFixed;
+ listBox.DrawItem -= ThemedListBox_DrawItem;
+ listBox.DrawItem += ThemedListBox_DrawItem;
+ }
+
+ ///
+ /// 设置标签控件暗色样式。
+ ///
+ /// 目标标签。
+ private void ApplyLabelTheme(Label label)
+ {
+ label.BackColor = Color.Transparent;
+ label.ForeColor = DarkThemeForeColor;
+ }
+
+ ///
+ /// 设置图片显示区域暗色样式。
+ ///
+ /// 目标图片框。
+ private void ApplyPictureBoxTheme(PictureBox pictureBox)
+ {
+ pictureBox.BackColor = Color.Black;
+ pictureBox.BorderStyle = BorderStyle.FixedSingle;
+ }
+
+ ///
+ /// 自定义绘制暗色下拉框项。
+ ///
+ /// 事件发起对象。
+ /// 绘制事件参数。
+ private void ThemedComboBox_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ e.DrawBackground();
+
+ if (e.Index < 0)
+ {
+ return;
+ }
+
+ var comboBox = sender as ComboBox;
+ var itemText = comboBox?.Items[e.Index]?.ToString() ?? string.Empty;
+ var isSelected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
+ var backColor = isSelected ? DarkThemeSelectedBackColor : DarkThemeInputBackColor;
+
+ using (var backBrush = new SolidBrush(backColor))
+ using (var textBrush = new SolidBrush(DarkThemeForeColor))
+ {
+ e.Graphics.FillRectangle(backBrush, e.Bounds);
+ e.Graphics.DrawString(itemText, e.Font, textBrush, e.Bounds);
+ }
+
+ e.DrawFocusRectangle();
+ }
+
+ ///
+ /// 自定义绘制暗色列表框项。
+ ///
+ /// 事件发起对象。
+ /// 绘制事件参数。
+ private void ThemedListBox_DrawItem(object sender, DrawItemEventArgs e)
+ {
+ if (e.Index < 0)
+ {
+ return;
+ }
+
+ var listBox = sender as ListBox;
+ var itemText = listBox?.Items[e.Index]?.ToString() ?? string.Empty;
+ var isSelected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
+ var backColor = isSelected ? DarkThemeSelectedBackColor : DarkThemeInputBackColor;
+ var textColor = listBox != null && listBox.Enabled ? DarkThemeForeColor : Color.FromArgb(170, 170, 170);
+ var textRect = new Rectangle(e.Bounds.X + 6, e.Bounds.Y + 1, Math.Max(1, e.Bounds.Width - 8), Math.Max(1, e.Bounds.Height - 2));
+
+ using (var backBrush = new SolidBrush(backColor))
+ using (var textBrush = new SolidBrush(textColor))
+ {
+ e.Graphics.FillRectangle(backBrush, e.Bounds);
+ e.Graphics.DrawString(itemText, e.Font, textBrush, textRect);
+ }
+
+ e.DrawFocusRectangle();
+ }
+
+ ///
+ /// 注册 VisionMaster 方案生命周期回调,用于关键节点日志输出。
+ ///
+ private void RegisterSolutionCallbacks()
+ {
+ _solutionManager.OnSolutionLoadBeginCallBack += OnSolutionLoadBegin;
+ _solutionManager.OnSolutionLoadProgressCallBack += OnSolutionLoadProgress;
+ _solutionManager.OnSolutionLoadEndCallBack += OnSolutionLoadEnd;
+
+ _solutionManager.OnSolutionSaveBeginCallBack += OnSolutionSaveBegin;
+ _solutionManager.OnSolutionSaveProgressCallBack += OnSolutionSaveProgress;
+ _solutionManager.OnSolutionSaveEndCallBack += OnSolutionSaveEnd;
+ }
+
+ ///
+ /// 注销 VisionMaster 回调,避免窗体关闭后的委托残留。
+ ///
+ private void UnregisterSolutionCallbacks()
+ {
+ _solutionManager.OnSolutionLoadBeginCallBack -= OnSolutionLoadBegin;
+ _solutionManager.OnSolutionLoadProgressCallBack -= OnSolutionLoadProgress;
+ _solutionManager.OnSolutionLoadEndCallBack -= OnSolutionLoadEnd;
+
+ _solutionManager.OnSolutionSaveBeginCallBack -= OnSolutionSaveBegin;
+ _solutionManager.OnSolutionSaveProgressCallBack -= OnSolutionSaveProgress;
+ _solutionManager.OnSolutionSaveEndCallBack -= OnSolutionSaveEnd;
+ }
+
+ ///
+ /// 注册 VM.Core 执行状态回调。
+ ///
+ private void RegisterVmSolutionCallbacks()
+ {
+ if (_isVmSolutionEventRegistered)
+ {
+ return;
+ }
+
+ VmSolution.OnWorkStatusEvent += VmSolution_OnWorkStatusEvent;
+ _isVmSolutionEventRegistered = true;
+ }
+
+ ///
+ /// 注销 VM.Core 执行状态回调。
+ ///
+ private void UnregisterVmSolutionCallbacks()
+ {
+ if (!_isVmSolutionEventRegistered)
+ {
+ return;
+ }
+
+ VmSolution.OnWorkStatusEvent -= VmSolution_OnWorkStatusEvent;
+ _isVmSolutionEventRegistered = false;
+ }
+
+ ///
+ /// 解绑当前流程结束回调,防止重复订阅和对象残留。
+ ///
+ private void UnbindProcedureWorkEndCallback()
+ {
+ if (_currentProcedure == null)
+ {
+ return;
+ }
+
+ try
+ {
+ _currentProcedure.OnWorkEndStatusCallBack -= Process_OnWorkEndStatusCallBack;
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"解绑流程结束回调异常:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 更新连续执行状态及按钮文案。
+ ///
+ /// 是否连续执行中。
+ /// 连续执行流程名称。
+ private void SetContinuousRunState(bool isRunning, string procedureName)
+ {
+ _isContinuousRunning = isRunning;
+ _continuousProcedureName = isRunning ? (procedureName ?? string.Empty) : string.Empty;
+ btnProExecAlway.Text = isRunning ? "停止连续" : "连续执行";
+ }
+
+ ///
+ /// 停止当前连续执行流程(若有),并重置连续执行状态。
+ ///
+ /// 停止原因(用于日志)。
+ private void StopContinuousRunIfNeeded(string reason)
+ {
+ if (!_isContinuousRunning)
+ {
+ return;
+ }
+
+ try
+ {
+ if (_currentProcedure != null)
+ {
+ _currentProcedure.ContinuousRunEnable = false;
+ }
+
+ if (!string.IsNullOrWhiteSpace(_continuousProcedureName))
+ {
+ WriteLog($"连续执行流程已停止:{_continuousProcedureName}({reason})");
+ }
+ else
+ {
+ WriteLog($"连续执行流程已停止({reason})。");
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"停止连续执行异常:{ex.Message}");
+ }
+ finally
+ {
+ SetContinuousRunState(false, string.Empty);
+ }
+ }
+
+ ///
+ /// 释放 VisionMaster 相关资源(幂等,允许重复调用)。
+ ///
+ private void ReleaseSdkResources()
+ {
+ if (_isClosingResourcesReleased)
+ {
+ return;
+ }
+
+ _isClosingResourcesReleased = true;
+
+ try
+ {
+ UnregisterSolutionCallbacks();
+ UnregisterVmSolutionCallbacks();
+
+ if (_activeBcrTool != null)
+ {
+ _activeBcrTool.ModuleResultCallBackArrived -= BcrModuleResultCallBackArrived;
+ _activeBcrTool = null;
+ }
+
+ StopContinuousRunIfNeeded("窗体关闭释放资源");
+ UnbindProcedureWorkEndCallback();
+ _currentProcedure = null;
+ vmParamsConfigControl1.ModuleSource = null;
+ ClearPictureBoxImage();
+
+ if (_isSolutionLoaded)
+ {
+ _solutionManager.CloseSolution();
+ _isSolutionLoaded = false;
+ }
+
+ _currentSolutionPath = string.Empty;
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"关闭窗体时释放资源异常:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 方案加载开始回调。
+ ///
+ private void OnSolutionLoadBegin(ImvsSdkDefine.IMVS_SOLUTION_LOAD_BEGEIN_INFO beginInfo)
+ {
+ _isSolutionLoading = true;
+ WriteLog("方案加载开始。");
+
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ BeginInvoke(new Action(() => UpdateUiState(_isBusy)));
+ }
+
+ ///
+ /// 方案加载进度回调。
+ ///
+ private void OnSolutionLoadProgress(ImvsSdkDefine.IMVS_SOLUTION_LOAD_PROCESS_INFO progressInfo)
+ {
+ WriteLog($"方案加载进度:{progressInfo.nProcess}%");
+ }
+
+ ///
+ /// 方案加载结束回调。
+ ///
+ private void OnSolutionLoadEnd(ImvsSdkDefine.IMVS_SOLUTION_LOAD_END_INFO endInfo)
+ {
+ _isSolutionLoading = false;
+ WriteLog($"方案加载结束,状态码:{endInfo.nStatus}");
+
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ if (endInfo.nStatus == 0)
+ {
+ _currentSolutionPath = _pendingLoadSolutionPath;
+ _isSolutionLoaded = true;
+ BindFirstModuleToRenderControl();
+ UpdateProcedureList();
+
+ WriteLog($"方案加载成功:{_currentSolutionPath}");
+ MessageBox.Show(this, "方案加载成功。", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ else
+ {
+ _isSolutionLoaded = false;
+ ResetProcedureAndModuleState();
+ MessageBox.Show(this, $"方案加载失败,状态码:{endInfo.nStatus}", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ catch (Exception ex)
+ {
+ _isSolutionLoaded = false;
+ ResetProcedureAndModuleState();
+ HandleException("加载方案回调处理", ex);
+ }
+ finally
+ {
+ _pendingLoadSolutionPath = string.Empty;
+ UpdateUiState(false);
+ }
+ }));
+ }
+
+ ///
+ /// 方案保存开始回调。
+ ///
+ private void OnSolutionSaveBegin(ImvsSdkDefine.IMVS_SOLUTION_SAVE_BEGEIN_INFO beginInfo)
+ {
+ WriteLog("方案保存开始。");
+ }
+
+ ///
+ /// 方案保存进度回调。
+ ///
+ private void OnSolutionSaveProgress(ImvsSdkDefine.IMVS_SOLUTION_SAVE_PROCESS_INFO progressInfo)
+ {
+ WriteLog($"方案保存进度:{progressInfo.nProcess}%");
+ }
+
+ ///
+ /// 方案保存结束回调。
+ ///
+ private void OnSolutionSaveEnd(ImvsSdkDefine.IMVS_SOLUTION_SAVE_END_INFO endInfo)
+ {
+ WriteLog($"方案保存结束,状态码:{endInfo.nStatus}");
+ }
+
+ ///
+ /// VM.Core 方案执行状态回调。
+ ///
+ /// 执行状态信息。
+ private void VmSolution_OnWorkStatusEvent(ImvsSdkDefine.IMVS_MODULE_WORK_STAUS workStatusInfo)
+ {
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ if (workStatusInfo.nWorkStatus != 0)
+ {
+ return;
+ }
+
+ if (_currentProcedure == null)
+ {
+ return;
+ }
+
+ // 连续执行阶段仅使用流程结束回调做渲染,避免与流程回调并发读取同一份结果。
+ if (_isContinuousRunning)
+ {
+ return;
+ }
+
+ RenderProcedureResultToPictureBox(_currentProcedure, "方案执行状态回调");
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"方案执行回调异常:{ex.Message}");
+ }
+ }));
+ }
+
+ ///
+ /// 流程执行结束回调:读取输出并渲染到 pictureBox。
+ ///
+ private void Process_OnWorkEndStatusCallBack(object sender, EventArgs e)
+ {
+ try
+ {
+ var procedure = sender as VmProcedure;
+ if (procedure == null)
+ {
+ return;
+ }
+
+ RenderProcedureResultToPictureBox(procedure, "流程结束回调");
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"流程执行回调异常:{ex.Message}");
+ }
+ }
+
+ ///
+ /// 将流程结果(图像、ROI、识别文本)渲染到 pictureBox。
+ ///
+ /// 流程对象。
+ /// 触发源描述(用于日志)。
+ private void RenderProcedureResultToPictureBox(VmProcedure procedure, string triggerSource)
+ {
+ if (procedure == null)
+ {
+ return;
+ }
+
+ if (System.Threading.Interlocked.Exchange(ref _renderingGuard, 1) == 1)
+ {
+ return;
+ }
+
+ try
+ {
+ // 字符串读取仅尝试字符串候选名,避免对 int 输出调用 GetOutputString 引发 SDK 内部异常。
+ var code = GetProcedureOutputString(procedure, _renderCodeOutputName, _procedureOutputCodeName, "out0", "code", "Code");
+ // 整型读取仅尝试整型候选名。
+ var codeNum = GetProcedureOutputInt(procedure, _procedureOutputNumberName, "out");
+ var bitmap = GetProcedureOutputBitmap(procedure, _renderImageOutputName, "ImageData", "Image", "image");
+ if (bitmap == null)
+ {
+ WriteLog($"{triggerSource}未读取到可渲染图像,输出名候选:{_renderImageOutputName}/ImageData/Image/image");
+ return;
+ }
+
+ try
+ {
+ var roiList = GetProcedureOutputBoxList(procedure, _renderRoiOutputName, "InputROI", "rect", "outline", "ROI", "roi");
+ var renderBitmap = DrawROIOnBitmap(bitmap, roiList, Color.Cyan, 2, code);
+ SetPictureBoxImage(renderBitmap);
+ WriteLog($"{triggerSource}渲染完成:Code={code},CodeNumber={codeNum},ROI数={roiList.Count}");
+ }
+ finally
+ {
+ bitmap.Dispose();
+ }
+ }
+ finally
+ {
+ System.Threading.Interlocked.Exchange(ref _renderingGuard, 0);
+ }
+ }
+
+ ///
+ /// 从流程输出中读取图像并转换为 Bitmap。
+ ///
+ /// 流程对象。
+ /// 候选输出名列表。
+ /// Bitmap 对象;读取失败返回 null。
+ private Bitmap GetProcedureOutputBitmap(VmProcedure procedure, params string[] outputNames)
+ {
+ foreach (var outputName in outputNames)
+ {
+ if (string.IsNullOrWhiteSpace(outputName))
+ {
+ continue;
+ }
+
+ try
+ {
+ var imageData = procedure.ModuResult.GetOutputImageV2(outputName);
+ if (imageData == null)
+ {
+ continue;
+ }
+
+ var bitmap = imageData.ToBitmap();
+ if (bitmap != null)
+ {
+ return bitmap;
+ }
+ }
+ catch
+ {
+ // 候选输出名逐个容错尝试,避免单个输出不存在导致主流程中断。
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// 从流程输出中读取 ROI 列表。
+ ///
+ /// 流程对象。
+ /// 候选输出名列表。
+ /// ROI 列表;读取失败返回空列表。
+ private List GetProcedureOutputBoxList(VmProcedure procedure, params string[] outputNames)
+ {
+ foreach (var outputName in outputNames)
+ {
+ if (string.IsNullOrWhiteSpace(outputName))
+ {
+ continue;
+ }
+
+ try
+ {
+ var boxList = procedure.ModuResult.GetOutputBoxArray(outputName);
+ if (boxList != null)
+ {
+ return boxList;
+ }
+ }
+ catch
+ {
+ // 候选输出名逐个容错尝试,避免单个输出不存在导致主流程中断。
+ }
+ }
+
+ return new List();
+ }
+
+ ///
+ /// 在图像上绘制 ROI 轮廓和识别文本。
+ ///
+ /// 源图像。
+ /// ROI 列表。
+ /// ROI 颜色。
+ /// 线宽。
+ /// 识别文本。
+ /// 绘制后的新 Bitmap。
+ private Bitmap DrawROIOnBitmap(Bitmap sourceBitmap, List boxList, Color color, int thickness, string code)
+ {
+ if (sourceBitmap == null)
+ {
+ return null;
+ }
+
+ var bitmap = new Bitmap(sourceBitmap);
+ using (var graphics = Graphics.FromImage(bitmap))
+ using (var pen = new Pen(color, thickness))
+ {
+ if (boxList != null && boxList.Count > 0)
+ {
+ foreach (var rect in boxList)
+ {
+ var cx = rect.CenterPoint.X;
+ var cy = rect.CenterPoint.Y;
+ var width = rect.BoxWidth;
+ var height = rect.BoxHeight;
+ var angle = rect.Angle;
+
+ var points = new System.Drawing.PointF[4];
+ points[0] = new System.Drawing.PointF(-width / 2f, -height / 2f);
+ points[1] = new System.Drawing.PointF(width / 2f, -height / 2f);
+ points[2] = new System.Drawing.PointF(width / 2f, height / 2f);
+ points[3] = new System.Drawing.PointF(-width / 2f, height / 2f);
+
+ var rad = angle * Math.PI / 180.0;
+ for (var i = 0; i < points.Length; i++)
+ {
+ var x = points[i].X;
+ var y = points[i].Y;
+ points[i].X = (float)(x * Math.Cos(rad) - y * Math.Sin(rad) + cx);
+ points[i].Y = (float)(x * Math.Sin(rad) + y * Math.Cos(rad) + cy);
+ }
+
+ graphics.DrawPolygon(pen, points);
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(code))
+ {
+ using (var font = new Font("Arial", 16, FontStyle.Bold))
+ using (var textBrush = new SolidBrush(Color.Orange))
+ using (var backgroundBrush = new SolidBrush(Color.FromArgb(160, 0, 0, 0)))
+ {
+ var textSize = graphics.MeasureString(code, font);
+ var x = Math.Max(0, bitmap.Width - textSize.Width - 12f);
+ const float y = 10f;
+
+ graphics.FillRectangle(backgroundBrush, x - 4f, y - 2f, textSize.Width + 8f, textSize.Height + 4f);
+ graphics.DrawString(code, font, textBrush, x, y);
+ }
+ }
+ }
+
+ return bitmap;
+ }
+
+ ///
+ /// 安全更新 pictureBox 图像并释放旧图,避免内存泄漏。
+ ///
+ /// 新图像。
+ private void SetPictureBoxImage(Bitmap bitmap)
+ {
+ if (bitmap == null)
+ {
+ return;
+ }
+
+ if (!IsHandleCreated || IsDisposed)
+ {
+ bitmap.Dispose();
+ return;
+ }
+
+ void UpdateAction()
+ {
+ var oldImage = pictureBox1.Image;
+ pictureBox1.Image = bitmap;
+ oldImage?.Dispose();
+ }
+
+ if (pictureBox1.InvokeRequired)
+ {
+ pictureBox1.BeginInvoke(new Action(UpdateAction));
+ }
+ else
+ {
+ UpdateAction();
+ }
+ }
+
+ ///
+ /// 清空 pictureBox 图像并释放旧图。
+ ///
+ private void ClearPictureBoxImage()
+ {
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ void ClearAction()
+ {
+ var oldImage = pictureBox1.Image;
+ pictureBox1.Image = null;
+ oldImage?.Dispose();
+ }
+
+ if (pictureBox1.InvokeRequired)
+ {
+ pictureBox1.BeginInvoke(new Action(ClearAction));
+ }
+ else
+ {
+ ClearAction();
+ }
+ }
+
+ ///
+ /// 条码模块执行结果回调。
+ ///
+ private void BcrModuleResultCallBackArrived(object sender, EventArgs e)
+ {
+ if (!(sender is IMVSBcrModuTool tool))
+ {
+ return;
+ }
+
+ var codeStr = string.Empty;
+ try
+ {
+ if (tool.ModuResult?.CodeStr != null && tool.ModuResult.CodeStr.Count > 0)
+ {
+ codeStr = tool.ModuResult.CodeStr[0];
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteLog($"读取条码结果异常:{ex.Message}");
+ }
+
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ BeginInvoke(new Action(() =>
+ {
+ WriteLog($"条码识别结果:{codeStr}");
+ }));
+ }
+
+ ///
+ /// 统一日志输出(当前工程无独立日志控件,先写控制台)。
+ ///
+ /// 日志内容。
+ private void WriteLog(string message)
+ {
+ var consoleLogText = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
+ var listBoxLogText = $"[{DateTime.Now:HH:mm:ss.fff}] {message}";
+ Console.WriteLine(consoleLogText);
+
+ if (!IsHandleCreated || IsDisposed)
+ {
+ return;
+ }
+
+ if (listBox1.InvokeRequired)
+ {
+ listBox1.BeginInvoke(new Action(() =>
+ {
+ listBox1.Items.Add(listBoxLogText);
+ listBox1.TopIndex = listBox1.Items.Count - 1;
+ }));
+ }
+ else
+ {
+ listBox1.Items.Add(listBoxLogText);
+ listBox1.TopIndex = listBox1.Items.Count - 1;
+ }
+ }
+
+ ///
+ /// 更新按钮启用状态,防止流程重入和非法操作。
+ ///
+ /// 当前是否忙碌。
+ private void UpdateUiState(bool busy)
+ {
+ _isBusy = busy;
+
+ var canLoadNewSolution = !busy && !_isSolutionLoading;
+ var canOperateLoadedSolution = !busy && _isSolutionLoaded && !_isSolutionLoading;
+
+ btnSelctedSolution.Enabled = !busy && !_isSolutionLoading;
+ btnLoadSolution.Enabled = canLoadNewSolution;
+ btnExecutSolution.Enabled = canOperateLoadedSolution;
+ btnSaveSolution.Enabled = canOperateLoadedSolution;
+
+ btnProImport.Enabled = canOperateLoadedSolution;
+ btnProExpo.Enabled = canOperateLoadedSolution;
+ btnProDel.Enabled = canOperateLoadedSolution;
+ btnProExecOnce.Enabled = canOperateLoadedSolution;
+ btnProExecAlway.Enabled = canOperateLoadedSolution;
+ btnProExecStop.Enabled = canOperateLoadedSolution && _isContinuousRunning;
+ btnModuleBindingPar.Enabled = canOperateLoadedSolution;
+ btnModuleExec.Enabled = canOperateLoadedSolution;
+ btnModuleRenderResult.Enabled = canOperateLoadedSolution;
+ cmbProcdure.Enabled = canOperateLoadedSolution;
+ cmbModule.Enabled = canOperateLoadedSolution;
+
+ // 保持连续执行按钮文案与状态一致。
+ btnProExecAlway.Text = _isContinuousRunning ? "停止连续" : "连续执行";
+ }
+
+ ///
+ /// 校验并返回文本框中的方案路径。
+ ///
+ /// 合法的完整路径。
+ private string GetValidatedSolutionPath()
+ {
+ var path = txtSolutionAddress.Text?.Trim() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ throw new InvalidOperationException("请先选择方案文件。\n支持格式:*.sol");
+ }
+
+ if (!File.Exists(path))
+ {
+ throw new FileNotFoundException("方案文件不存在,请重新选择。", path);
+ }
+
+ return path;
+ }
+
+ ///
+ /// 根据已加载方案,尝试把首个模块绑定到渲染控件以显示结果。
+ ///
+ private void BindFirstModuleToRenderControl()
+ {
+ // 界面已切换为 pictureBox 渲染,加载新方案时先清空上次显示图像。
+ ClearPictureBoxImage();
+ }
+
+ ///
+ /// 统一异常处理:记录日志并弹出错误提示。
+ ///
+ /// 弹窗标题。
+ /// 异常对象。
+ private void HandleException(string caption, Exception ex)
+ {
+ var vmException = VmExceptionTool.GetVmException(ex);
+ var errorMessage = vmException == null
+ ? ex.Message
+ : $"{vmException.errorMessage} (Code={vmException.errorCode})";
+
+ WriteLog($"{caption}失败:{errorMessage}");
+ MessageBox.Show(this, errorMessage, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+
+ ///
+ /// 校验当前是否已加载方案。
+ ///
+ private bool EnsureSolutionLoaded(string operationName)
+ {
+ if (_isSolutionLoaded)
+ {
+ return true;
+ }
+
+ MessageBox.Show(this, $"请先加载方案后再{operationName}。", operationName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return false;
+ }
+
+ ///
+ /// 判断异常是否为“方案正在加载中”错误。
+ ///
+ /// 异常对象。
+ /// 若是加载中错误则返回 true。
+ private static bool IsSolutionLoadingVmException(Exception ex)
+ {
+ if (ex == null)
+ {
+ return false;
+ }
+
+ var vmException = VmExceptionTool.GetVmException(ex);
+ return vmException != null && vmException.errorCode == VmErrorCodeSolutionLoading;
+ }
+
+ ///
+ /// 判断异常是否为“模块已处于连续执行”错误。
+ ///
+ /// 异常对象。
+ /// 若是连续执行中错误则返回 true。
+ private static bool IsModuleContinueExecuteVmException(Exception ex)
+ {
+ if (ex == null)
+ {
+ return false;
+ }
+
+ var vmException = VmExceptionTool.GetVmException(ex);
+ return vmException != null && vmException.errorCode == VmErrorCodeModuleContinueExecute;
+ }
+
+ ///
+ /// 调用 SDK 加载方案,并针对“方案正在加载中”做有限重试。
+ ///
+ /// 方案路径。
+ /// 最大重试次数。
+ /// 每次重试间隔(毫秒)。
+ private void LoadSolutionWithRetry(string solutionPath, int retryCount, int retryDelayMs)
+ {
+ Exception lastException = null;
+
+ for (var attempt = 0; attempt <= retryCount; attempt++)
+ {
+ try
+ {
+ _solutionManager.LoadSolution(solutionPath, string.Empty);
+ return;
+ }
+ catch (Exception ex)
+ {
+ if (!IsSolutionLoadingVmException(ex) || attempt >= retryCount)
+ {
+ throw;
+ }
+
+ lastException = ex;
+ WriteLog($"方案仍在加载中,准备重试:第{attempt + 1}/{retryCount}次,间隔{retryDelayMs}ms。");
+ System.Threading.Thread.Sleep(retryDelayMs);
+ }
+ }
+
+ if (lastException != null)
+ {
+ throw lastException;
+ }
+ }
+
+ ///
+ /// 从 AppSettings 读取配置值,读取失败或为空时返回默认值。
+ ///
+ /// 配置键名。
+ /// 默认值。
+ /// 配置值或默认值。
+ private static string GetAppSettingOrDefault(string key, string defaultValue)
+ {
+ try
+ {
+ var value = ConfigurationManager.AppSettings[key];
+ return string.IsNullOrWhiteSpace(value) ? defaultValue : value.Trim();
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ ///
+ /// 尝试获取当前选中的流程对象。
+ ///
+ private bool TryGetSelectedProcedure(out VmProcedure procedure)
+ {
+ procedure = null;
+ var selectedProcedureName = cmbProcdure.Text?.Trim() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(selectedProcedureName))
+ {
+ MessageBox.Show(this, "请先在流程列表中选择目标流程。", "流程操作", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return false;
+ }
+
+ procedure = VmSolution.Instance[selectedProcedureName] as VmProcedure;
+ if (procedure == null)
+ {
+ MessageBox.Show(this, $"未找到流程:{selectedProcedureName}。请刷新流程列表后重试。", "流程操作", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 尝试获取当前选中的模块对象。
+ ///
+ private bool TryGetSelectedModule(out VmModule module)
+ {
+ module = null;
+ var selectedProcedureName = cmbProcdure.Text?.Trim() ?? string.Empty;
+ var selectedModuleName = cmbModule.Text?.Trim() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(selectedProcedureName) || string.IsNullOrWhiteSpace(selectedModuleName))
+ {
+ MessageBox.Show(this, "请先选择流程和模块。", "模块操作", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return false;
+ }
+
+ module = VmSolution.Instance[$"{selectedProcedureName}.{selectedModuleName}"] as VmModule;
+ if (module == null)
+ {
+ MessageBox.Show(this, $"未找到模块:{selectedProcedureName}.{selectedModuleName}。", "模块操作", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 刷新流程下拉列表。
+ ///
+ private void UpdateProcedureList()
+ {
+ var previous = cmbProcdure.Text?.Trim() ?? string.Empty;
+ cmbProcdure.Items.Clear();
+
+ var processInfoList = VmSolution.Instance.GetAllProcedureList();
+ if (processInfoList.nNum <= 0 || processInfoList.astProcessInfo == null)
+ {
+ WriteLog("方案中未检测到流程。");
+ cmbModule.Items.Clear();
+ return;
+ }
+
+ for (var i = 0; i < processInfoList.nNum; i++)
+ {
+ cmbProcdure.Items.Add(processInfoList.astProcessInfo[i].strProcessName);
+ }
+
+ if (!string.IsNullOrWhiteSpace(previous) && cmbProcdure.Items.Contains(previous))
+ {
+ cmbProcdure.SelectedItem = previous;
+ }
+ else
+ {
+ cmbProcdure.SelectedIndex = 0;
+ }
+ }
+
+ ///
+ /// 刷新当前流程对应模块列表。
+ ///
+ private void UpdateModuleList()
+ {
+ cmbModule.Items.Clear();
+
+ if (!TryGetSelectedProcedure(out var procedure))
+ {
+ return;
+ }
+
+ var moduleInfoList = procedure.GetAllModuleList();
+ if (moduleInfoList.nNum <= 0 || moduleInfoList.astModuleInfo == null)
+ {
+ WriteLog($"流程[{cmbProcdure.Text}]中未检测到模块。");
+ return;
+ }
+
+ for (var i = 0; i < moduleInfoList.nNum; i++)
+ {
+ cmbModule.Items.Add(moduleInfoList.astModuleInfo[i].strDisplayName);
+ }
+
+ cmbModule.SelectedIndex = 0;
+ }
+
+ ///
+ /// 清理流程与模块相关状态。
+ ///
+ private void ResetProcedureAndModuleState()
+ {
+ StopContinuousRunIfNeeded("重置流程与模块状态");
+ UnbindProcedureWorkEndCallback();
+ _currentProcedure = null;
+
+ if (_activeBcrTool != null)
+ {
+ _activeBcrTool.ModuleResultCallBackArrived -= BcrModuleResultCallBackArrived;
+ _activeBcrTool = null;
+ }
+
+ cmbProcdure.Items.Clear();
+ cmbModule.Items.Clear();
+ vmParamsConfigControl1.ModuleSource = null;
+ ClearPictureBoxImage();
+ }
+
+ ///
+ /// 读取流程字符串输出(多候选容错读取)。
+ ///
+ private string GetProcedureOutputString(VmProcedure procedure, params string[] outputNames)
+ {
+ if (procedure == null || outputNames == null || outputNames.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ foreach (var outputName in outputNames)
+ {
+ if (string.IsNullOrWhiteSpace(outputName))
+ {
+ continue;
+ }
+
+ try
+ {
+ var output = procedure.ModuResult.GetOutputString(outputName);
+ if (output.astStringVal != null && output.astStringVal.Length > 0)
+ {
+ return output.astStringVal[0].strValue;
+ }
+ }
+ catch
+ {
+ // 忽略输出不存在等异常,保持流程主链路稳定。
+ }
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// 读取流程整型输出(多候选容错读取)。
+ ///
+ private string GetProcedureOutputInt(VmProcedure procedure, params string[] outputNames)
+ {
+ if (procedure == null || outputNames == null || outputNames.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ foreach (var outputName in outputNames)
+ {
+ if (string.IsNullOrWhiteSpace(outputName))
+ {
+ continue;
+ }
+
+ try
+ {
+ var output = procedure.ModuResult.GetOutputInt(outputName);
+ if (output.pIntVal != null && output.pIntVal.Length > 0)
+ {
+ return output.pIntVal[0].ToString();
+ }
+ }
+ catch
+ {
+ // 忽略输出不存在等异常,保持流程主链路稳定。
+ }
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// 选择方案按钮事件:打开文件选择框并回填路径。
+ ///
private void btnSelctedSolution_Click(object sender, EventArgs e)
{
+ using (var dialog = new OpenFileDialog())
+ {
+ dialog.Title = "请选择 VisionMaster 方案文件";
+ dialog.Filter = "VisionMaster方案文件 (*.sol)|*.sol|所有文件 (*.*)|*.*";
+ dialog.CheckFileExists = true;
+ dialog.Multiselect = false;
+ if (dialog.ShowDialog(this) != DialogResult.OK)
+ {
+ return;
+ }
+
+ txtSolutionAddress.Text = dialog.FileName;
+ WriteLog($"已选择方案文件:{dialog.FileName}");
+ }
}
+ ///
+ /// 加载方案按钮事件:校验路径并调用 SolutionManager.LoadSolution。
+ ///
private void btnLoadSolution_Click(object sender, EventArgs e)
{
+ if (_isBusy || _isSolutionLoading)
+ {
+ if (_isSolutionLoading)
+ {
+ MessageBox.Show(this, "方案正在加载中,请等待加载完成后再操作。", "加载方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ return;
+ }
+ var keepBusyUntilLoadEndCallback = false;
+
+ try
+ {
+ UpdateUiState(true);
+ var solutionPath = GetValidatedSolutionPath();
+
+ // 设置服务就绪,避免部分环境首次调用方案接口时报服务未就绪异常。
+ _solutionManager.SetServerReadyEvent();
+ _isSolutionLoading = true;
+ _pendingLoadSolutionPath = solutionPath;
+
+ // 若已有方案,先关闭,避免方案切换场景下状态残留。
+ if (_isSolutionLoaded)
+ {
+ _solutionManager.CloseSolution();
+ _isSolutionLoaded = false;
+ ResetProcedureAndModuleState();
+ WriteLog("已关闭旧方案。\n开始加载新方案。");
+ }
+
+ LoadSolutionWithRetry(solutionPath, retryCount: 12, retryDelayMs: 200);
+ keepBusyUntilLoadEndCallback = true;
+ WriteLog($"方案加载请求已提交:{solutionPath}");
+ }
+ catch (Exception ex)
+ {
+ _isSolutionLoading = false;
+ _isSolutionLoaded = false;
+ _pendingLoadSolutionPath = string.Empty;
+ ResetProcedureAndModuleState();
+ HandleException("加载方案", ex);
+ }
+ finally
+ {
+ if (!keepBusyUntilLoadEndCallback)
+ {
+ UpdateUiState(false);
+ }
+ }
}
+ ///
+ /// 执行方案按钮事件:调用 SolutionManager.ExecuteOnce 执行一次流程。
+ ///
private void btnExecutSolution_Click(object sender, EventArgs e)
{
+ if (_isBusy)
+ {
+ return;
+ }
+ if (!_isSolutionLoaded)
+ {
+ MessageBox.Show(this, "请先加载方案后再执行。", "执行方案", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+ _solutionManager.ExecuteOnce();
+ WriteLog("方案执行完成(ExecuteOnce)。");
+ MessageBox.Show(this, "方案执行完成。", "执行方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ HandleException("执行方案", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
}
+ ///
+ /// 保存方案按钮事件:默认覆盖保存到当前方案路径。
+ ///
private void btnSaveSolution_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!_isSolutionLoaded)
+ {
+ MessageBox.Show(this, "请先加载方案后再保存。", "保存方案", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+
+ var savePath = string.IsNullOrWhiteSpace(_currentSolutionPath)
+ ? GetValidatedSolutionPath()
+ : _currentSolutionPath;
+
+ _solutionManager.SaveSolution(savePath, string.Empty, IntPtr.Zero, 0);
+ WriteLog($"方案保存成功:{savePath}");
+ MessageBox.Show(this, "方案保存成功。", "保存方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ HandleException("保存方案", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ ///
+ /// 关闭窗体时释放 SDK 相关资源。
+ ///
+ /// 事件参数。
+ protected override void OnFormClosed(FormClosedEventArgs e)
+ {
+ try
+ {
+ ReleaseSdkResources();
+ }
+ finally
+ {
+ base.OnFormClosed(e);
+ }
+ }
+
+ private void btnProImport_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("导入流程"))
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+
+ string procedurePath;
+ using (var dialog = new OpenFileDialog())
+ {
+ dialog.Title = "请选择流程文件";
+ dialog.Filter = "流程文件 (*.prc)|*.prc|所有文件 (*.*)|*.*";
+ dialog.CheckFileExists = true;
+ dialog.Multiselect = false;
+
+ if (dialog.ShowDialog(this) != DialogResult.OK)
+ {
+ WriteLog("用户取消导入流程。\n");
+ return;
+ }
+
+ procedurePath = dialog.FileName;
+ }
+
+ VmProcedure.Load(procedurePath);
+ UpdateProcedureList();
+ WriteLog($"流程导入成功:{procedurePath}");
+ MessageBox.Show(this, "流程导入成功。", "导入流程", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ HandleException("导入流程", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ private void btnProExpo_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("导出流程"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedProcedure(out var procedure))
+ {
+ return;
+ }
+
+ using (var dialog = new SaveFileDialog())
+ {
+ dialog.Title = "请选择流程导出路径";
+ dialog.Filter = "流程文件 (*.prc)|*.prc|所有文件 (*.*)|*.*";
+ dialog.FileName = $"{cmbProcdure.Text}.prc";
+
+ if (dialog.ShowDialog(this) != DialogResult.OK)
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+ procedure.SaveAs(dialog.FileName);
+ WriteLog($"流程导出成功:{dialog.FileName}");
+ MessageBox.Show(this, "流程导出成功。", "导出流程", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ HandleException("导出流程", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+ }
+
+ private void btnProDel_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("删除流程"))
+ {
+ return;
+ }
+
+ var selectedProcedureName = cmbProcdure.Text?.Trim() ?? string.Empty;
+ if (string.IsNullOrWhiteSpace(selectedProcedureName))
+ {
+ MessageBox.Show(this, "请先选择需要删除的流程。", "删除流程", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+
+ if (MessageBox.Show(this, $"确认删除流程:{selectedProcedureName} 吗?", "删除流程", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+ VmSolution.Instance.DeleteOneProcedure(selectedProcedureName);
+ UnbindProcedureWorkEndCallback();
+ _currentProcedure = null;
+ vmParamsConfigControl1.ModuleSource = null;
+ ClearPictureBoxImage();
+ UpdateProcedureList();
+ cmbModule.Items.Clear();
+ WriteLog($"删除流程成功:{selectedProcedureName}");
+ }
+ catch (Exception ex)
+ {
+ HandleException("删除流程", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ private void btnProExecOnce_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("执行流程"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedProcedure(out var procedure))
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+ StopContinuousRunIfNeeded("执行单次流程");
+ UnbindProcedureWorkEndCallback();
+ _currentProcedure = procedure;
+ _currentProcedure.OnWorkEndStatusCallBack -= Process_OnWorkEndStatusCallBack;
+ _currentProcedure.OnWorkEndStatusCallBack += Process_OnWorkEndStatusCallBack;
+ _currentProcedure.Run();
+ WriteLog($"执行指定流程一次成功:{cmbProcdure.Text}");
+ }
+ catch (Exception ex)
+ {
+ HandleException("执行指定流程一次", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ private void btnProExecAlway_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("连续执行流程"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedProcedure(out var procedure))
+ {
+ return;
+ }
+
+ var selectedProcedureName = cmbProcdure.Text?.Trim() ?? string.Empty;
+
+ try
+ {
+ UpdateUiState(true);
+
+ if (_isContinuousRunning)
+ {
+ // 当前按钮处于“停止连续”语义:点击后先停止;若选中流程已变更,则继续启动新流程连续执行。
+ var runningProcedureName = _continuousProcedureName;
+ StopContinuousRunIfNeeded("用户点击连续执行按钮");
+
+ if (string.Equals(runningProcedureName, selectedProcedureName, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ }
+
+ procedure.ContinuousRunEnable = true;
+ UnbindProcedureWorkEndCallback();
+ _currentProcedure = procedure;
+ _currentProcedure.OnWorkEndStatusCallBack -= Process_OnWorkEndStatusCallBack;
+ _currentProcedure.OnWorkEndStatusCallBack += Process_OnWorkEndStatusCallBack;
+ _currentProcedure.Run();
+ SetContinuousRunState(true, selectedProcedureName);
+ WriteLog($"连续执行流程已开启:{cmbProcdure.Text}");
+ }
+ catch (Exception ex)
+ {
+ if (IsModuleContinueExecuteVmException(ex))
+ {
+ SetContinuousRunState(true, selectedProcedureName);
+ WriteLog($"流程[{selectedProcedureName}]已处于连续执行状态,已同步运行状态。\n若需停止请点击“停止执行”。");
+ return;
+ }
+
+ HandleException("连续执行流程", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ private void btnProExecStop_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!_isSolutionLoaded)
+ {
+ return;
+ }
+
+ if (!_isContinuousRunning)
+ {
+ WriteLog("当前未处于连续执行状态,无需停止。");
+ return;
+ }
+
+ StopContinuousRunIfNeeded("用户点击停止按钮");
+ UpdateUiState(false);
+ }
+
+ private void btnModuleBindingPar_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("绑定模块参数"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedModule(out var module))
+ {
+ return;
+ }
+
+ try
+ {
+ vmParamsConfigControl1.ModuleSource = module;
+ WriteLog($"绑定模块参数成功:{cmbProcdure.Text}.{cmbModule.Text}");
+ }
+ catch (Exception ex)
+ {
+ HandleException("绑定模块参数", ex);
+ }
+ }
+
+ private void btnModuleExec_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("执行模块"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedModule(out var module))
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateUiState(true);
+
+ module.Run();
+ if (TryGetSelectedProcedure(out var procedure))
+ {
+ _currentProcedure = procedure;
+ RenderProcedureResultToPictureBox(procedure, "模块执行");
+ }
+
+ if (_activeBcrTool != null)
+ {
+ _activeBcrTool.ModuleResultCallBackArrived -= BcrModuleResultCallBackArrived;
+ _activeBcrTool = null;
+ }
+
+ var bcrTool = module as IMVSBcrModuTool;
+ if (bcrTool != null)
+ {
+ _activeBcrTool = bcrTool;
+ _activeBcrTool.EnableResultCallback();
+ _activeBcrTool.ModuleResultCallBackArrived -= BcrModuleResultCallBackArrived;
+ _activeBcrTool.ModuleResultCallBackArrived += BcrModuleResultCallBackArrived;
+ WriteLog($"模块[{cmbModule.Text}]执行完成,已启用条码结果回调。");
+ }
+ else
+ {
+ WriteLog($"模块[{cmbModule.Text}]执行完成(非条码模块)。");
+ }
+ }
+ catch (Exception ex)
+ {
+ HandleException("执行模块", ex);
+ }
+ finally
+ {
+ UpdateUiState(false);
+ }
+ }
+
+ private void btnModuleRenderResult_Click(object sender, EventArgs e)
+ {
+ if (_isBusy)
+ {
+ return;
+ }
+
+ if (!EnsureSolutionLoaded("渲染模块结果"))
+ {
+ return;
+ }
+
+ if (!TryGetSelectedModule(out var module))
+ {
+ return;
+ }
+
+ try
+ {
+ if (TryGetSelectedProcedure(out var procedure))
+ {
+ _currentProcedure = procedure;
+ RenderProcedureResultToPictureBox(procedure, "模块结果渲染");
+ }
+
+ WriteLog($"模块结果刷新完成:{cmbProcdure.Text}.{cmbModule.Text}");
+ }
+ catch (Exception ex)
+ {
+ HandleException("渲染模块结果", ex);
+ }
+ }
+
+ ///
+ /// 流程切换联动:自动刷新模块列表,并清空旧图像显示。
+ ///
+ private void cmbProcdure_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (_isBusy || !_isSolutionLoaded)
+ {
+ return;
+ }
+
+ try
+ {
+ if (_isContinuousRunning && !string.Equals(_continuousProcedureName, cmbProcdure.Text?.Trim() ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ {
+ StopContinuousRunIfNeeded("切换流程");
+ }
+
+ UpdateModuleList();
+ vmParamsConfigControl1.ModuleSource = null;
+ ClearPictureBoxImage();
+ WriteLog($"流程切换完成:{cmbProcdure.Text}");
+ }
+ catch (Exception ex)
+ {
+ HandleException("切换流程", ex);
+ }
+ }
+
+ private void cmbModule_DropDown(object sender, EventArgs e)
+ {
+ if (_isBusy || !_isSolutionLoaded)
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateModuleList();
+ WriteLog($"流程[{cmbProcdure.Text}]模块列表刷新完成。");
+ }
+ catch (Exception ex)
+ {
+ HandleException("刷新模块列表", ex);
+ }
+ }
+
+ private void cmbProcdure_DropDown(object sender, EventArgs e)
+ {
+ if (_isBusy || !_isSolutionLoaded)
+ {
+ return;
+ }
+
+ try
+ {
+ UpdateProcedureList();
+ WriteLog("流程列表刷新完成。");
+ }
+ catch (Exception ex)
+ {
+ HandleException("刷新流程列表", ex);
+ }
+ }
+
+ ///
+ /// 窗体关闭前释放 SDK 相关资源。
+ ///
+ /// 事件源。
+ /// 关闭事件参数。
+ private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ ReleaseSdkResources();
+ }
+
+ private void MainForm_Load(object sender, EventArgs e)
{
}
diff --git a/HkVisionPro.App/SDK/VisionMaster算法平台SDK开发指南V4.3.1(.NET).chm b/HkVisionPro.App/SDK/VisionMaster算法平台SDK开发指南V4.3.1(.NET).chm
new file mode 100644
index 0000000..b2822c2
Binary files /dev/null and b/HkVisionPro.App/SDK/VisionMaster算法平台SDK开发指南V4.3.1(.NET).chm differ
diff --git a/HkVisionPro.App/packages.config b/HkVisionPro.App/packages.config
new file mode 100644
index 0000000..51af7b7
--- /dev/null
+++ b/HkVisionPro.App/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/HkVisionPro.slnx b/HkVisionPro.slnx
index 64ae7bf..3452c12 100644
--- a/HkVisionPro.slnx
+++ b/HkVisionPro.slnx
@@ -1,3 +1,3 @@
-
+