C3032
2025-12-23 7c2a646ce89a06d0bfbb16f993d4ae3734f1de9c
更新相机功能和界面布局
已修改4个文件
已添加1个文件
1432 ■■■■■ 文件已修改
LB_SmartVision/Forms/Pages/CameraPage/CameraForm.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/ProcessRun/ProcessContext.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/2DCameraForm.Designer.cs 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/CameraConfig.cs 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/HRCamera.cs 1136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CameraPage/CameraForm.cs
@@ -43,7 +43,7 @@
            this.Text = camera.SN;
            //设置品牌
            uiComboBoxBrand.Text = camera.Brand.ToString();
            onlinePictureBox = new UserPictureBox(uiGroupBoxImage);
            this.uiGroupBoxImage.Controls.Clear();
LB_SmartVision/ProcessRun/ProcessContext.cs
@@ -539,10 +539,6 @@
        [Node("相机取图", "取像工具", "Basic", "相机取图")]
        public void ç›¸æœºå–图(FlowNode node) { RunNodeAsync(node); }
        [Node("Halcon2D斑点工具", "Haclon2D工具", "Basic", "Halcon2D斑点工具")]
        public void Halcon2D斑点工具(FlowNode node) { RunNodeAsync(node); }
        #endregion
LB_VisionProcesses/Cameras/2DCameraForm.Designer.cs
@@ -104,18 +104,20 @@
            // 
            cmbBrand.Dock = DockStyle.Fill;
            cmbBrand.FormattingEnabled = true;
            cmbBrand.Location = new Point(53, 3);
            cmbBrand.Location = new Point(68, 4);
            cmbBrand.Margin = new Padding(4);
            cmbBrand.Name = "cmbBrand";
            cmbBrand.Size = new Size(296, 25);
            cmbBrand.Size = new Size(381, 28);
            cmbBrand.TabIndex = 0;
            // 
            // cmbSN
            // 
            cmbSN.Dock = DockStyle.Fill;
            cmbSN.FormattingEnabled = true;
            cmbSN.Location = new Point(53, 3);
            cmbSN.Location = new Point(68, 4);
            cmbSN.Margin = new Padding(4);
            cmbSN.Name = "cmbSN";
            cmbSN.Size = new Size(296, 25);
            cmbSN.Size = new Size(381, 28);
            cmbSN.TabIndex = 1;
            cmbSN.SelectedIndexChanged += cmbSN_SelectedIndexChanged;
            cmbSN.MouseDown += cmbSN_MouseDown;
@@ -124,9 +126,10 @@
            // 
            label1.AutoSize = true;
            label1.Dock = DockStyle.Fill;
            label1.Location = new Point(3, 0);
            label1.Location = new Point(4, 0);
            label1.Margin = new Padding(4, 0, 4, 0);
            label1.Name = "label1";
            label1.Size = new Size(44, 34);
            label1.Size = new Size(56, 39);
            label1.TabIndex = 2;
            label1.Text = "品牌";
            label1.TextAlign = ContentAlignment.MiddleCenter;
@@ -135,9 +138,10 @@
            // 
            label2.AutoSize = true;
            label2.Dock = DockStyle.Fill;
            label2.Location = new Point(3, 0);
            label2.Location = new Point(4, 0);
            label2.Margin = new Padding(4, 0, 4, 0);
            label2.Name = "label2";
            label2.Size = new Size(44, 34);
            label2.Size = new Size(56, 39);
            label2.TabIndex = 3;
            label2.Text = "SN";
            label2.TextAlign = ContentAlignment.MiddleCenter;
@@ -146,9 +150,10 @@
            // 
            btnStartGrab.Dock = DockStyle.Fill;
            btnStartGrab.ForeColor = SystemColors.ControlText;
            btnStartGrab.Location = new Point(91, 3);
            btnStartGrab.Location = new Point(117, 4);
            btnStartGrab.Margin = new Padding(4);
            btnStartGrab.Name = "btnStartGrab";
            btnStartGrab.Size = new Size(82, 28);
            btnStartGrab.Size = new Size(105, 31);
            btnStartGrab.TabIndex = 5;
            btnStartGrab.Text = "连续采集";
            btnStartGrab.UseVisualStyleBackColor = true;
@@ -158,9 +163,10 @@
            // 
            btnCloseGrab.Dock = DockStyle.Fill;
            btnCloseGrab.ForeColor = SystemColors.Desktop;
            btnCloseGrab.Location = new Point(179, 3);
            btnCloseGrab.Location = new Point(230, 4);
            btnCloseGrab.Margin = new Padding(4);
            btnCloseGrab.Name = "btnCloseGrab";
            btnCloseGrab.Size = new Size(82, 28);
            btnCloseGrab.Size = new Size(105, 31);
            btnCloseGrab.TabIndex = 6;
            btnCloseGrab.Text = "关闭采集";
            btnCloseGrab.UseVisualStyleBackColor = true;
@@ -170,9 +176,10 @@
            // 
            btnGrabOnce.Dock = DockStyle.Fill;
            btnGrabOnce.ForeColor = SystemColors.ControlText;
            btnGrabOnce.Location = new Point(3, 3);
            btnGrabOnce.Location = new Point(4, 4);
            btnGrabOnce.Margin = new Padding(4);
            btnGrabOnce.Name = "btnGrabOnce";
            btnGrabOnce.Size = new Size(82, 28);
            btnGrabOnce.Size = new Size(105, 31);
            btnGrabOnce.TabIndex = 7;
            btnGrabOnce.Text = "单张采集";
            btnGrabOnce.UseVisualStyleBackColor = true;
@@ -182,9 +189,10 @@
            // 
            btnOpen.Dock = DockStyle.Fill;
            btnOpen.ForeColor = SystemColors.ControlText;
            btnOpen.Location = new Point(3, 3);
            btnOpen.Location = new Point(4, 4);
            btnOpen.Margin = new Padding(4);
            btnOpen.Name = "btnOpen";
            btnOpen.Size = new Size(82, 28);
            btnOpen.Size = new Size(105, 31);
            btnOpen.TabIndex = 8;
            btnOpen.Text = "打开";
            btnOpen.UseVisualStyleBackColor = true;
@@ -194,9 +202,10 @@
            // 
            btnClose.Dock = DockStyle.Fill;
            btnClose.ForeColor = SystemColors.ControlText;
            btnClose.Location = new Point(91, 3);
            btnClose.Location = new Point(117, 4);
            btnClose.Margin = new Padding(4);
            btnClose.Name = "btnClose";
            btnClose.Size = new Size(82, 28);
            btnClose.Size = new Size(105, 31);
            btnClose.TabIndex = 9;
            btnClose.Text = "关闭";
            btnClose.UseVisualStyleBackColor = true;
@@ -206,9 +215,10 @@
            // 
            label5.AutoSize = true;
            label5.Dock = DockStyle.Fill;
            label5.Location = new Point(3, 0);
            label5.Location = new Point(4, 0);
            label5.Margin = new Padding(4, 0, 4, 0);
            label5.Name = "label5";
            label5.Size = new Size(114, 34);
            label5.Size = new Size(146, 39);
            label5.TabIndex = 15;
            label5.Text = "取图数量:";
            label5.TextAlign = ContentAlignment.MiddleCenter;
@@ -217,9 +227,10 @@
            // 
            lblPicCount.AutoSize = true;
            lblPicCount.Dock = DockStyle.Fill;
            lblPicCount.Location = new Point(123, 0);
            lblPicCount.Location = new Point(158, 0);
            lblPicCount.Margin = new Padding(4, 0, 4, 0);
            lblPicCount.Name = "lblPicCount";
            lblPicCount.Size = new Size(226, 34);
            lblPicCount.Size = new Size(291, 39);
            lblPicCount.TabIndex = 16;
            lblPicCount.Text = "0";
            lblPicCount.TextAlign = ContentAlignment.MiddleLeft;
@@ -227,18 +238,20 @@
            // txtGain
            // 
            txtGain.Dock = DockStyle.Fill;
            txtGain.Location = new Point(53, 3);
            txtGain.Location = new Point(68, 4);
            txtGain.Margin = new Padding(4);
            txtGain.Name = "txtGain";
            txtGain.Size = new Size(296, 23);
            txtGain.Size = new Size(381, 27);
            txtGain.TabIndex = 21;
            txtGain.TextChanged += txtGain_TextChanged;
            // 
            // txtExp
            // 
            txtExp.Dock = DockStyle.Fill;
            txtExp.Location = new Point(53, 3);
            txtExp.Location = new Point(68, 4);
            txtExp.Margin = new Padding(4);
            txtExp.Name = "txtExp";
            txtExp.Size = new Size(296, 23);
            txtExp.Size = new Size(381, 27);
            txtExp.TabIndex = 20;
            txtExp.TextChanged += txtExp_TextChanged;
            // 
@@ -246,9 +259,10 @@
            // 
            label4.AutoSize = true;
            label4.Dock = DockStyle.Fill;
            label4.Location = new Point(3, 0);
            label4.Location = new Point(4, 0);
            label4.Margin = new Padding(4, 0, 4, 0);
            label4.Name = "label4";
            label4.Size = new Size(44, 34);
            label4.Size = new Size(56, 39);
            label4.TabIndex = 19;
            label4.Text = "增益";
            label4.TextAlign = ContentAlignment.MiddleCenter;
@@ -257,9 +271,10 @@
            // 
            label3.AutoSize = true;
            label3.Dock = DockStyle.Fill;
            label3.Location = new Point(3, 0);
            label3.Location = new Point(4, 0);
            label3.Margin = new Padding(4, 0, 4, 0);
            label3.Name = "label3";
            label3.Size = new Size(44, 34);
            label3.Size = new Size(56, 39);
            label3.TabIndex = 18;
            label3.Text = "曝光";
            label3.TextAlign = ContentAlignment.MiddleCenter;
@@ -271,8 +286,10 @@
            groupBox1.Dock = DockStyle.Fill;
            groupBox1.ForeColor = SystemColors.Control;
            groupBox1.Location = new Point(0, 0);
            groupBox1.Margin = new Padding(4);
            groupBox1.Name = "groupBox1";
            groupBox1.Size = new Size(487, 591);
            groupBox1.Padding = new Padding(4);
            groupBox1.Size = new Size(626, 695);
            groupBox1.TabIndex = 17;
            groupBox1.TabStop = false;
            groupBox1.Text = "实时采集";
@@ -280,18 +297,20 @@
            // panel_Picture
            // 
            panel_Picture.Dock = DockStyle.Fill;
            panel_Picture.Location = new Point(3, 19);
            panel_Picture.Location = new Point(4, 24);
            panel_Picture.Margin = new Padding(4);
            panel_Picture.Name = "panel_Picture";
            panel_Picture.Size = new Size(481, 569);
            panel_Picture.Size = new Size(618, 667);
            panel_Picture.TabIndex = 0;
            // 
            // radioButtonSoft
            // 
            radioButtonSoft.AutoSize = true;
            radioButtonSoft.Dock = DockStyle.Fill;
            radioButtonSoft.Location = new Point(3, 123);
            radioButtonSoft.Location = new Point(4, 145);
            radioButtonSoft.Margin = new Padding(4);
            radioButtonSoft.Name = "radioButtonSoft";
            radioButtonSoft.Size = new Size(352, 34);
            radioButtonSoft.Size = new Size(453, 39);
            radioButtonSoft.TabIndex = 23;
            radioButtonSoft.TabStop = true;
            radioButtonSoft.Text = "软触发";
@@ -302,9 +321,10 @@
            // 
            radioButtonHard.AutoSize = true;
            radioButtonHard.Dock = DockStyle.Fill;
            radioButtonHard.Location = new Point(3, 203);
            radioButtonHard.Location = new Point(4, 239);
            radioButtonHard.Margin = new Padding(4);
            radioButtonHard.Name = "radioButtonHard";
            radioButtonHard.Size = new Size(352, 34);
            radioButtonHard.Size = new Size(453, 39);
            radioButtonHard.TabIndex = 24;
            radioButtonHard.TabStop = true;
            radioButtonHard.Text = "硬触发";
@@ -313,7 +333,8 @@
            // splitContainer1
            // 
            splitContainer1.Dock = DockStyle.Fill;
            splitContainer1.Location = new Point(5, 5);
            splitContainer1.Location = new Point(6, 6);
            splitContainer1.Margin = new Padding(4);
            splitContainer1.Name = "splitContainer1";
            // 
            // splitContainer1.Panel1
@@ -323,8 +344,9 @@
            // splitContainer1.Panel2
            // 
            splitContainer1.Panel2.Controls.Add(groupBox1);
            splitContainer1.Size = new Size(855, 591);
            splitContainer1.SplitterDistance = 364;
            splitContainer1.Size = new Size(1100, 695);
            splitContainer1.SplitterDistance = 469;
            splitContainer1.SplitterWidth = 5;
            splitContainer1.TabIndex = 19;
            // 
            // groupBox2
@@ -334,9 +356,11 @@
            groupBox2.Dock = DockStyle.Fill;
            groupBox2.ForeColor = SystemColors.Control;
            groupBox2.Location = new Point(0, 0);
            groupBox2.MinimumSize = new Size(261, 61);
            groupBox2.Margin = new Padding(4);
            groupBox2.MinimumSize = new Size(336, 72);
            groupBox2.Name = "groupBox2";
            groupBox2.Size = new Size(364, 591);
            groupBox2.Padding = new Padding(4);
            groupBox2.Size = new Size(469, 695);
            groupBox2.TabIndex = 0;
            groupBox2.TabStop = false;
            groupBox2.Text = "相机参数";
@@ -360,7 +384,8 @@
            tableLayoutPanel2.Controls.Add(tableLayoutPanel9, 0, 10);
            tableLayoutPanel2.Controls.Add(tableLayoutPanel10, 0, 9);
            tableLayoutPanel2.Dock = DockStyle.Fill;
            tableLayoutPanel2.Location = new Point(3, 19);
            tableLayoutPanel2.Location = new Point(4, 24);
            tableLayoutPanel2.Margin = new Padding(4);
            tableLayoutPanel2.Name = "tableLayoutPanel2";
            tableLayoutPanel2.RowCount = 14;
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 7.14301062F));
@@ -377,38 +402,40 @@
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 7.14301062F));
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 7.143011F));
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 7.14086771F));
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F));
            tableLayoutPanel2.Size = new Size(358, 569);
            tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Absolute, 24F));
            tableLayoutPanel2.Size = new Size(461, 667);
            tableLayoutPanel2.TabIndex = 25;
            // 
            // tableLayoutPanel3
            // 
            tableLayoutPanel3.ColumnCount = 2;
            tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
            tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 64F));
            tableLayoutPanel3.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel3.Controls.Add(label1, 0, 0);
            tableLayoutPanel3.Controls.Add(cmbBrand, 1, 0);
            tableLayoutPanel3.Dock = DockStyle.Fill;
            tableLayoutPanel3.Location = new Point(3, 3);
            tableLayoutPanel3.Location = new Point(4, 4);
            tableLayoutPanel3.Margin = new Padding(4);
            tableLayoutPanel3.Name = "tableLayoutPanel3";
            tableLayoutPanel3.RowCount = 1;
            tableLayoutPanel3.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel3.Size = new Size(352, 34);
            tableLayoutPanel3.Size = new Size(453, 39);
            tableLayoutPanel3.TabIndex = 0;
            // 
            // tableLayoutPanel4
            // 
            tableLayoutPanel4.ColumnCount = 2;
            tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
            tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 64F));
            tableLayoutPanel4.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel4.Controls.Add(label2, 0, 0);
            tableLayoutPanel4.Controls.Add(cmbSN, 1, 0);
            tableLayoutPanel4.Dock = DockStyle.Fill;
            tableLayoutPanel4.Location = new Point(3, 43);
            tableLayoutPanel4.Location = new Point(4, 51);
            tableLayoutPanel4.Margin = new Padding(4);
            tableLayoutPanel4.Name = "tableLayoutPanel4";
            tableLayoutPanel4.RowCount = 1;
            tableLayoutPanel4.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel4.Size = new Size(352, 34);
            tableLayoutPanel4.Size = new Size(453, 39);
            tableLayoutPanel4.TabIndex = 1;
            // 
            // tableLayoutPanel5
@@ -422,11 +449,12 @@
            tableLayoutPanel5.Controls.Add(btnClose, 1, 0);
            tableLayoutPanel5.Controls.Add(btnEdit, 3, 0);
            tableLayoutPanel5.Dock = DockStyle.Fill;
            tableLayoutPanel5.Location = new Point(3, 83);
            tableLayoutPanel5.Location = new Point(4, 98);
            tableLayoutPanel5.Margin = new Padding(4);
            tableLayoutPanel5.Name = "tableLayoutPanel5";
            tableLayoutPanel5.RowCount = 1;
            tableLayoutPanel5.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel5.Size = new Size(352, 34);
            tableLayoutPanel5.Size = new Size(453, 39);
            tableLayoutPanel5.TabIndex = 2;
            // 
            // btnEdit
@@ -434,9 +462,10 @@
            btnEdit.Dock = DockStyle.Fill;
            btnEdit.Enabled = false;
            btnEdit.ForeColor = SystemColors.ControlText;
            btnEdit.Location = new Point(267, 3);
            btnEdit.Location = new Point(343, 4);
            btnEdit.Margin = new Padding(4);
            btnEdit.Name = "btnEdit";
            btnEdit.Size = new Size(82, 28);
            btnEdit.Size = new Size(106, 31);
            btnEdit.TabIndex = 10;
            btnEdit.Text = "编辑";
            btnEdit.UseVisualStyleBackColor = true;
@@ -454,21 +483,22 @@
            tableLayoutPanel6.Controls.Add(btnCloseGrab, 2, 0);
            tableLayoutPanel6.Controls.Add(btnSaveImage, 3, 0);
            tableLayoutPanel6.Dock = DockStyle.Fill;
            tableLayoutPanel6.Location = new Point(3, 163);
            tableLayoutPanel6.Location = new Point(4, 192);
            tableLayoutPanel6.Margin = new Padding(4);
            tableLayoutPanel6.Name = "tableLayoutPanel6";
            tableLayoutPanel6.RowCount = 1;
            tableLayoutPanel6.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel6.Size = new Size(352, 34);
            tableLayoutPanel6.Size = new Size(453, 39);
            tableLayoutPanel6.TabIndex = 24;
            // 
            // btnSaveImage
            // 
            btnSaveImage.Dock = DockStyle.Fill;
            btnSaveImage.ForeColor = SystemColors.Desktop;
            btnSaveImage.Location = new Point(266, 2);
            btnSaveImage.Margin = new Padding(2);
            btnSaveImage.Location = new Point(342, 2);
            btnSaveImage.Margin = new Padding(3, 2, 3, 2);
            btnSaveImage.Name = "btnSaveImage";
            btnSaveImage.Size = new Size(84, 30);
            btnSaveImage.Size = new Size(108, 35);
            btnSaveImage.TabIndex = 8;
            btnSaveImage.Text = "保存图片";
            btnSaveImage.UseVisualStyleBackColor = true;
@@ -477,31 +507,33 @@
            // tableLayoutPanel7
            // 
            tableLayoutPanel7.ColumnCount = 2;
            tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
            tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 64F));
            tableLayoutPanel7.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel7.Controls.Add(label3, 0, 0);
            tableLayoutPanel7.Controls.Add(txtExp, 1, 0);
            tableLayoutPanel7.Dock = DockStyle.Fill;
            tableLayoutPanel7.Location = new Point(3, 283);
            tableLayoutPanel7.Location = new Point(4, 333);
            tableLayoutPanel7.Margin = new Padding(4);
            tableLayoutPanel7.Name = "tableLayoutPanel7";
            tableLayoutPanel7.RowCount = 1;
            tableLayoutPanel7.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
            tableLayoutPanel7.Size = new Size(352, 34);
            tableLayoutPanel7.Size = new Size(453, 39);
            tableLayoutPanel7.TabIndex = 25;
            // 
            // tableLayoutPanel8
            // 
            tableLayoutPanel8.ColumnCount = 2;
            tableLayoutPanel8.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
            tableLayoutPanel8.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 64F));
            tableLayoutPanel8.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel8.Controls.Add(label4, 0, 0);
            tableLayoutPanel8.Controls.Add(txtGain, 1, 0);
            tableLayoutPanel8.Dock = DockStyle.Fill;
            tableLayoutPanel8.Location = new Point(3, 323);
            tableLayoutPanel8.Location = new Point(4, 380);
            tableLayoutPanel8.Margin = new Padding(4);
            tableLayoutPanel8.Name = "tableLayoutPanel8";
            tableLayoutPanel8.RowCount = 1;
            tableLayoutPanel8.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel8.Size = new Size(352, 34);
            tableLayoutPanel8.Size = new Size(453, 39);
            tableLayoutPanel8.TabIndex = 26;
            // 
            // tableLayoutPanel1
@@ -514,19 +546,21 @@
            tableLayoutPanel1.Controls.Add(ckbUpParams, 1, 0);
            tableLayoutPanel1.Controls.Add(ckbRegrab, 2, 0);
            tableLayoutPanel1.Dock = DockStyle.Fill;
            tableLayoutPanel1.Location = new Point(3, 243);
            tableLayoutPanel1.Location = new Point(4, 286);
            tableLayoutPanel1.Margin = new Padding(4);
            tableLayoutPanel1.Name = "tableLayoutPanel1";
            tableLayoutPanel1.RowCount = 1;
            tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel1.Size = new Size(352, 34);
            tableLayoutPanel1.Size = new Size(453, 39);
            tableLayoutPanel1.TabIndex = 29;
            // 
            // btnStartHard
            // 
            btnStartHard.Dock = DockStyle.Fill;
            btnStartHard.Location = new Point(3, 3);
            btnStartHard.Location = new Point(4, 4);
            btnStartHard.Margin = new Padding(4);
            btnStartHard.Name = "btnStartHard";
            btnStartHard.Size = new Size(64, 28);
            btnStartHard.Size = new Size(82, 31);
            btnStartHard.TabIndex = 22;
            btnStartHard.Text = "等待触发";
            btnStartHard.UseVisualStyleBackColor = true;
@@ -536,9 +570,10 @@
            // 
            ckbUpParams.AutoSize = true;
            ckbUpParams.Dock = DockStyle.Fill;
            ckbUpParams.Location = new Point(73, 3);
            ckbUpParams.Location = new Point(94, 4);
            ckbUpParams.Margin = new Padding(4);
            ckbUpParams.Name = "ckbUpParams";
            ckbUpParams.Size = new Size(134, 28);
            ckbUpParams.Size = new Size(173, 31);
            ckbUpParams.TabIndex = 23;
            ckbUpParams.Text = "是否每次写入参数";
            ckbUpParams.UseVisualStyleBackColor = true;
@@ -547,9 +582,10 @@
            // 
            ckbRegrab.AutoSize = true;
            ckbRegrab.Dock = DockStyle.Fill;
            ckbRegrab.Location = new Point(213, 3);
            ckbRegrab.Location = new Point(275, 4);
            ckbRegrab.Margin = new Padding(4);
            ckbRegrab.Name = "ckbRegrab";
            ckbRegrab.Size = new Size(136, 28);
            ckbRegrab.Size = new Size(174, 31);
            ckbRegrab.TabIndex = 24;
            ckbRegrab.Text = "是否失败重新取图";
            ckbRegrab.UseVisualStyleBackColor = true;
@@ -557,25 +593,27 @@
            // tableLayoutPanel12
            // 
            tableLayoutPanel12.ColumnCount = 2;
            tableLayoutPanel12.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 120F));
            tableLayoutPanel12.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 154F));
            tableLayoutPanel12.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel12.Controls.Add(lblCapTime, 1, 0);
            tableLayoutPanel12.Controls.Add(label7, 0, 0);
            tableLayoutPanel12.Dock = DockStyle.Fill;
            tableLayoutPanel12.Location = new Point(3, 523);
            tableLayoutPanel12.Location = new Point(4, 615);
            tableLayoutPanel12.Margin = new Padding(4);
            tableLayoutPanel12.Name = "tableLayoutPanel12";
            tableLayoutPanel12.RowCount = 1;
            tableLayoutPanel12.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel12.Size = new Size(352, 43);
            tableLayoutPanel12.Size = new Size(453, 48);
            tableLayoutPanel12.TabIndex = 31;
            // 
            // lblCapTime
            // 
            lblCapTime.AutoSize = true;
            lblCapTime.Dock = DockStyle.Fill;
            lblCapTime.Location = new Point(123, 0);
            lblCapTime.Location = new Point(158, 0);
            lblCapTime.Margin = new Padding(4, 0, 4, 0);
            lblCapTime.Name = "lblCapTime";
            lblCapTime.Size = new Size(226, 43);
            lblCapTime.Size = new Size(291, 48);
            lblCapTime.TabIndex = 16;
            lblCapTime.Text = "0ms";
            lblCapTime.TextAlign = ContentAlignment.MiddleLeft;
@@ -584,9 +622,10 @@
            // 
            label7.AutoSize = true;
            label7.Dock = DockStyle.Fill;
            label7.Location = new Point(3, 0);
            label7.Location = new Point(4, 0);
            label7.Margin = new Padding(4, 0, 4, 0);
            label7.Name = "label7";
            label7.Size = new Size(114, 43);
            label7.Size = new Size(146, 48);
            label7.TabIndex = 15;
            label7.Text = "取图时间:";
            label7.TextAlign = ContentAlignment.MiddleCenter;
@@ -594,16 +633,17 @@
            // tableLayoutPanel11
            // 
            tableLayoutPanel11.ColumnCount = 2;
            tableLayoutPanel11.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 120F));
            tableLayoutPanel11.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 154F));
            tableLayoutPanel11.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel11.Controls.Add(label5, 0, 0);
            tableLayoutPanel11.Controls.Add(lblPicCount, 1, 0);
            tableLayoutPanel11.Dock = DockStyle.Fill;
            tableLayoutPanel11.Location = new Point(3, 483);
            tableLayoutPanel11.Location = new Point(4, 568);
            tableLayoutPanel11.Margin = new Padding(4);
            tableLayoutPanel11.Name = "tableLayoutPanel11";
            tableLayoutPanel11.RowCount = 1;
            tableLayoutPanel11.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel11.Size = new Size(352, 34);
            tableLayoutPanel11.Size = new Size(453, 39);
            tableLayoutPanel11.TabIndex = 30;
            // 
            // cmbImagesPath
@@ -612,9 +652,10 @@
            cmbImagesPath.Dock = DockStyle.Fill;
            cmbImagesPath.ForeColor = SystemColors.ControlText;
            cmbImagesPath.FormattingEnabled = true;
            cmbImagesPath.Location = new Point(3, 443);
            cmbImagesPath.Location = new Point(4, 521);
            cmbImagesPath.Margin = new Padding(4);
            cmbImagesPath.Name = "cmbImagesPath";
            cmbImagesPath.Size = new Size(352, 25);
            cmbImagesPath.Size = new Size(453, 28);
            cmbImagesPath.TabIndex = 0;
            cmbImagesPath.SelectedIndexChanged += cmbImagesPath_SelectedIndexChanged;
            cmbImagesPath.MouseHover += cmbImagesPath_MouseHover;
@@ -629,20 +670,22 @@
            tableLayoutPanel9.Controls.Add(btnAddImages, 1, 0);
            tableLayoutPanel9.Controls.Add(btnLocalGrab, 2, 0);
            tableLayoutPanel9.Dock = DockStyle.Fill;
            tableLayoutPanel9.Location = new Point(3, 403);
            tableLayoutPanel9.Location = new Point(4, 474);
            tableLayoutPanel9.Margin = new Padding(4);
            tableLayoutPanel9.Name = "tableLayoutPanel9";
            tableLayoutPanel9.RowCount = 1;
            tableLayoutPanel9.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel9.Size = new Size(352, 34);
            tableLayoutPanel9.Size = new Size(453, 39);
            tableLayoutPanel9.TabIndex = 27;
            // 
            // ckbLocalTest
            // 
            ckbLocalTest.AutoSize = true;
            ckbLocalTest.Dock = DockStyle.Fill;
            ckbLocalTest.Location = new Point(3, 3);
            ckbLocalTest.Location = new Point(4, 4);
            ckbLocalTest.Margin = new Padding(4);
            ckbLocalTest.Name = "ckbLocalTest";
            ckbLocalTest.Size = new Size(111, 28);
            ckbLocalTest.Size = new Size(142, 31);
            ckbLocalTest.TabIndex = 0;
            ckbLocalTest.Text = "本地取图";
            ckbLocalTest.UseVisualStyleBackColor = true;
@@ -651,9 +694,10 @@
            // 
            btnAddImages.Dock = DockStyle.Fill;
            btnAddImages.ForeColor = SystemColors.Desktop;
            btnAddImages.Location = new Point(120, 3);
            btnAddImages.Location = new Point(154, 4);
            btnAddImages.Margin = new Padding(4);
            btnAddImages.Name = "btnAddImages";
            btnAddImages.Size = new Size(111, 28);
            btnAddImages.Size = new Size(142, 31);
            btnAddImages.TabIndex = 1;
            btnAddImages.Text = "添加图片";
            btnAddImages.UseVisualStyleBackColor = true;
@@ -663,9 +707,10 @@
            // 
            btnLocalGrab.Dock = DockStyle.Fill;
            btnLocalGrab.ForeColor = SystemColors.Desktop;
            btnLocalGrab.Location = new Point(237, 3);
            btnLocalGrab.Location = new Point(304, 4);
            btnLocalGrab.Margin = new Padding(4);
            btnLocalGrab.Name = "btnLocalGrab";
            btnLocalGrab.Size = new Size(112, 28);
            btnLocalGrab.Size = new Size(145, 31);
            btnLocalGrab.TabIndex = 2;
            btnLocalGrab.Text = "本地取图";
            btnLocalGrab.UseVisualStyleBackColor = true;
@@ -674,25 +719,27 @@
            // tableLayoutPanel10
            // 
            tableLayoutPanel10.ColumnCount = 2;
            tableLayoutPanel10.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 50F));
            tableLayoutPanel10.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 64F));
            tableLayoutPanel10.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
            tableLayoutPanel10.Controls.Add(label6, 0, 0);
            tableLayoutPanel10.Controls.Add(txtTimeout, 1, 0);
            tableLayoutPanel10.Dock = DockStyle.Fill;
            tableLayoutPanel10.Location = new Point(3, 363);
            tableLayoutPanel10.Location = new Point(4, 427);
            tableLayoutPanel10.Margin = new Padding(4);
            tableLayoutPanel10.Name = "tableLayoutPanel10";
            tableLayoutPanel10.RowCount = 1;
            tableLayoutPanel10.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tableLayoutPanel10.Size = new Size(352, 34);
            tableLayoutPanel10.Size = new Size(453, 39);
            tableLayoutPanel10.TabIndex = 32;
            // 
            // label6
            // 
            label6.AutoSize = true;
            label6.Dock = DockStyle.Fill;
            label6.Location = new Point(3, 0);
            label6.Location = new Point(4, 0);
            label6.Margin = new Padding(4, 0, 4, 0);
            label6.Name = "label6";
            label6.Size = new Size(44, 34);
            label6.Size = new Size(56, 39);
            label6.TabIndex = 0;
            label6.Text = "超时";
            label6.TextAlign = ContentAlignment.MiddleCenter;
@@ -700,9 +747,10 @@
            // txtTimeout
            // 
            txtTimeout.Dock = DockStyle.Fill;
            txtTimeout.Location = new Point(53, 3);
            txtTimeout.Location = new Point(68, 4);
            txtTimeout.Margin = new Padding(4);
            txtTimeout.Name = "txtTimeout";
            txtTimeout.Size = new Size(296, 23);
            txtTimeout.Size = new Size(381, 27);
            txtTimeout.TabIndex = 1;
            // 
            // theme_2DCameraForm
@@ -715,11 +763,12 @@
            theme_2DCameraForm.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
            theme_2DCameraForm.Image = (Image)resources.GetObject("theme_2DCameraForm.Image");
            theme_2DCameraForm.Location = new Point(0, 0);
            theme_2DCameraForm.Margin = new Padding(4);
            theme_2DCameraForm.Name = "theme_2DCameraForm";
            theme_2DCameraForm.Padding = new Padding(10, 70, 10, 9);
            theme_2DCameraForm.Padding = new Padding(13, 82, 13, 11);
            theme_2DCameraForm.RoundCorners = true;
            theme_2DCameraForm.Sizable = true;
            theme_2DCameraForm.Size = new Size(885, 680);
            theme_2DCameraForm.Size = new Size(1138, 800);
            theme_2DCameraForm.SmartBounds = true;
            theme_2DCameraForm.StartPosition = FormStartPosition.WindowsDefaultLocation;
            theme_2DCameraForm.TabIndex = 20;
@@ -736,7 +785,8 @@
            controlBox1.EnableMaximizeButton = false;
            controlBox1.EnableMinimizeButton = false;
            controlBox1.ForeColor = Color.FromArgb(155, 155, 155);
            controlBox1.Location = new Point(785, 18);
            controlBox1.Location = new Point(1009, 18);
            controlBox1.Margin = new Padding(4);
            controlBox1.MaximizeHoverColor = Color.FromArgb(74, 74, 74);
            controlBox1.MinimizeHoverColor = Color.FromArgb(63, 63, 65);
            controlBox1.Name = "controlBox1";
@@ -750,22 +800,24 @@
            panel_2DCameraForm.Controls.Add(splitContainer1);
            panel_2DCameraForm.Dock = DockStyle.Fill;
            panel_2DCameraForm.EdgeColor = Color.FromArgb(32, 41, 50);
            panel_2DCameraForm.Location = new Point(10, 70);
            panel_2DCameraForm.Location = new Point(13, 82);
            panel_2DCameraForm.Margin = new Padding(4);
            panel_2DCameraForm.Name = "panel_2DCameraForm";
            panel_2DCameraForm.Padding = new Padding(5);
            panel_2DCameraForm.Size = new Size(865, 601);
            panel_2DCameraForm.Padding = new Padding(6);
            panel_2DCameraForm.Size = new Size(1112, 707);
            panel_2DCameraForm.SmoothingType = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            panel_2DCameraForm.TabIndex = 20;
            panel_2DCameraForm.Text = "panel2";
            // 
            // CameraForm
            // 
            AutoScaleDimensions = new SizeF(7F, 17F);
            AutoScaleDimensions = new SizeF(9F, 20F);
            AutoScaleMode = AutoScaleMode.Font;
            ClientSize = new Size(885, 680);
            ClientSize = new Size(1138, 800);
            Controls.Add(theme_2DCameraForm);
            FormBorderStyle = FormBorderStyle.None;
            MinimumSize = new Size(261, 61);
            Margin = new Padding(4);
            MinimumSize = new Size(336, 72);
            Name = "CameraForm";
            Text = "2D相机设置界面";
            TransparencyKey = Color.Fuchsia;
LB_VisionProcesses/Cameras/CameraConfig.cs
@@ -206,11 +206,23 @@
                        return Result;
                    }
                    if (Camera == null || Camera.SN != SN)
                    {
                    // å§‹ç»ˆæ£€æŸ¥ç¼“存的Camera对象是否与全局字典中的一致
                    // é˜²æ­¢ç›¸æœºè¢«åˆ é™¤å¹¶é‡å»ºåŽ(SN相同), ä»ç„¶å¼•用旧的已销毁对象
                        if (dicCameras.ContainsKey(SN))
                            Camera = dicCameras[SN];
                    {
                        var currentCam = dicCameras[SN];
                        if (Camera != currentCam)
                        {
                            Camera = currentCam;
                            // å¦‚果切换了相机实例,且未开始采集,尝试开启采集
                            if (Camera != null && !Camera.isGrabbing)
                            {
                                try { Camera.StartGrabbing(); } catch { }
                            }
                        }
                    }
                        else
                    {
                            Camera = null;
                    }
LB_VisionProcesses/Cameras/HRCamera.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1136 @@
using MVSDK_Net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace LB_VisionProcesses.Cameras
{
    /// <summary>
    /// åŽç¿2D工业相机实现类
    /// åŸºäºŽMVSDK_Net SDK封装
    /// </summary>
    public class HRCamera : BaseCamera
    {
        #region ç§æœ‰å­—段
        private MyCamera _camera; // åŽç¿ç›¸æœºè®¾å¤‡å¯¹è±¡
        private Thread _grabThread; // å›¾åƒé‡‡é›†çº¿ç¨‹
        private bool _isGrabbing; // é‡‡é›†çŠ¶æ€æ ‡å¿—
        private bool _threadRunning; // çº¿ç¨‹è¿è¡Œæ ‡å¿—
        private bool _handleCreated = false; // å¥æŸ„是否已创建
        private Thread _callbackThread; // å›žè°ƒå¤„理线程
        private List<IMVDefine.IMV_Frame> _frameList; // å›¾åƒç¼“存列表
        private readonly object _frameLock = new object(); // å¸§ç¼“存锁
        // CopyMemory API声明
        [System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
        #endregion
        #region æž„造函数和析构函数
        /// <summary>
        /// æž„造函数
        /// </summary>
        public HRCamera()
        {
            _camera = new MyCamera();
            _frameList = new List<IMVDefine.IMV_Frame>();
            Brand = CameraBrand.HRCamera;
            _isGrabbing = false;
            _threadRunning = false;
        }
        /// <summary>
        /// æžæž„函数
        /// </summary>
        ~HRCamera()
        {
            Dispose(false);
        }
        #endregion
        #region è®¾å¤‡ç®¡ç†æ“ä½œ
        /// <summary>
        /// èŽ·å–ç›¸æœºSN枚举列表
        /// </summary>
        /// <returns>相机SN列表</returns>
        public override List<string> GetListEnum()
        {
            List<string> cameraList = new List<string>();
            try
            {
                // æžšä¸¾æ‰€æœ‰è®¾å¤‡
                IMVDefine.IMV_DeviceList deviceList = new IMVDefine.IMV_DeviceList();
                IMVDefine.IMV_EInterfaceType interfaceType = IMVDefine.IMV_EInterfaceType.interfaceTypeAll;
                int result = MyCamera.IMV_EnumDevices(ref deviceList, (uint)interfaceType);
                if (result == IMVDefine.IMV_OK && deviceList.nDevNum > 0)
                {
                    for (int i = 0; i < deviceList.nDevNum; i++)
                    {
                        IMVDefine.IMV_DeviceInfo deviceInfo = (IMVDefine.IMV_DeviceInfo)
                            Marshal.PtrToStructure(
                                deviceList.pDevInfo + Marshal.SizeOf(typeof(IMVDefine.IMV_DeviceInfo)) * i,
                                typeof(IMVDefine.IMV_DeviceInfo));
                        string cameraInfo = $"{deviceInfo.cameraName}[{deviceInfo.serialNumber}]";
                        cameraList.Add(cameraInfo);
                    }
                }
                else
                {
                    // è®°å½•日志或抛出异常
                    //throw new Exception($"枚举设备失败,错误码:{result}");
                    Debug.WriteLine("枚举设备失败!");
                }
            }
            catch (Exception ex)
            {
                // è®°å½•错误日志
                System.Diagnostics.Debug.WriteLine($"获取相机列表失败:{ex.Message}");
                throw;
            }
            return cameraList;
        }
        /// <summary>
        /// åˆå§‹åŒ–相机设备
        /// </summary>
        /// <param name="SN">相机序列号</param>
        /// <param name="Handle">窗口句柄(可选)</param>
        /// <returns>初始化是否成功</returns>
        public override bool InitDevice(string SN, object Handle = null)
        {
            try
            {
                // ç¡®ä¿å½»åº•关闭和清理
                if (_camera != null)
                {
                    if (_camera.IMV_IsOpen())
                    {
                        _camera.IMV_Close();
                    }
                    if (_handleCreated)
                    {
                        _camera.IMV_DestroyHandle();
                        _handleCreated = false;
                    }
                }
                // æžšä¸¾è®¾å¤‡å¹¶åŒ¹é…SN
                List<string> cameraList = GetListEnum();
                int cameraIndex = -1;
                for (int i = 0; i < cameraList.Count; i++)
                {
                    if (cameraList[i].Contains(SN))
                    {
                        cameraIndex = i;
                        break;
                    }
                }
                if (cameraIndex == -1)
                {
                    throw new Exception($"未找到序列号为 {SN} çš„相机");
                }
                // åˆ›å»ºè®¾å¤‡å¥æŸ„
                int result = _camera.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByIndex, cameraIndex);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"创建设备句柄失败,错误码:{result}");
                }
                _handleCreated = true;
                // æ‰“开设备
                result = _camera.IMV_Open();
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"打开相机失败,错误码:{result}");
                }
                // è®¾ç½®è®¾å¤‡å±žæ€§
                this.SN = SN;
                isGrabbing = false;
                // è®¾ç½®ç¼“存个数为8
                _camera.IMV_SetBufferCount(8);
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"初始化相机失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// å…³é—­ç›¸æœºè®¾å¤‡
        /// </summary>
        /// <returns>关闭是否成功</returns>
        public override bool CloseDevice()
        {
            try
            {
                if (_camera == null)
                    return true;
                // åœæ­¢é‡‡é›†
                if (_camera.IMV_IsGrabbing())
                {
                    StopGrabbing();
                }
                // å…³é—­è®¾å¤‡
                if (_camera.IMV_IsOpen())
                {
                    int result = _camera.IMV_Close();
                    if (result != IMVDefine.IMV_OK)
                    {
                        System.Diagnostics.Debug.WriteLine($"关闭相机失败,错误码:{result}");
                    }
                }
                // é”€æ¯å¥æŸ„
                if (_handleCreated)
                {
                    _camera.IMV_DestroyHandle();
                    _handleCreated = false;
                }
                // é‡Šæ”¾èµ„源
                _isGrabbing = false;
                isGrabbing = false;
                // æ¸…理帧缓存
                lock (_frameLock)
                {
                    foreach (var frame in _frameList)
                    {
                        var tempFrame = frame; // åˆ›å»ºä¸´æ—¶å˜é‡
                        _camera.IMV_ReleaseFrame(ref tempFrame);
                    }
                    _frameList.Clear();
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"关闭相机失败:{ex.Message}");
                return false;
            }
        }
        #endregion
        #region å›¾åƒé‡‡é›†æ“ä½œ
        /// <summary>
        /// å¼€å§‹å›¾åƒé‡‡é›†
        /// </summary>
        /// <returns>采集启动是否成功</returns>
        public override bool StartGrabbing()
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                // åœæ­¢çŽ°æœ‰é‡‡é›†
                if (_isGrabbing)
                {
                    StopGrabbing();
                }
                // å¯åЍ采集
                int result = _camera.IMV_StartGrabbing();
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"启动采集失败,错误码:{result}");
                }
                _isGrabbing = true;
                isGrabbing = true;
                // å¯åŠ¨é‡‡é›†çº¿ç¨‹
                _threadRunning = true;
                _grabThread = new Thread(GrabThreadProc);
                _grabThread.IsBackground = true;
                _grabThread.Start();
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"启动采集失败:{ex.Message}");
                _isGrabbing = false;
                isGrabbing = false;
                return false;
            }
        }
        /// <summary>
        /// åœæ­¢å›¾åƒé‡‡é›†
        /// </summary>
        /// <returns>停止是否成功</returns>
        public override bool StopGrabbing()
        {
            try
            {
                if (_camera == null || !_isGrabbing)
                    return true;
                // åœæ­¢çº¿ç¨‹
                _threadRunning = false;
                if (_grabThread != null && _grabThread.IsAlive)
                {
                    _grabThread.Join(1000);
                    _grabThread = null;
                }
                // åœæ­¢é‡‡é›†
                int result = _camera.IMV_StopGrabbing();
                if (result != IMVDefine.IMV_OK)
                {
                    System.Diagnostics.Debug.WriteLine($"停止采集失败,错误码:{result}");
                }
                _isGrabbing = false;
                isGrabbing = false;
                // æ¸…理缓存
                lock (_frameLock)
                {
                    foreach (var frame in _frameList)
                    {
                        var tempFrame = frame; // åˆ›å»ºä¸´æ—¶å˜é‡
                        _camera.IMV_ReleaseFrame(ref tempFrame);
                    }
                    _frameList.Clear();
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"停止采集失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// æ‰§è¡Œè½¯è§¦å‘
        /// </summary>
        /// <returns>触发是否成功</returns>
        public override bool SoftTrigger()
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                int result = _camera.IMV_ExecuteCommandFeature("TriggerSoftware");
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"软触发失败:{ex.Message}");
                return false;
            }
        }
        #endregion
        #region å‚数设置和获取
        /// <summary>
        /// è®¾ç½®è§¦å‘模式
        /// </summary>
        /// <param name="mode">触发模式</param>
        /// <param name="source">触发源</param>
        /// <returns>设置是否成功</returns>
        public override bool SetTriggerMode(TriggerMode mode, TriggerSource source = TriggerSource.Line0)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                // è®¾ç½®è§¦å‘模式
                string triggerMode = mode == TriggerMode.On ? "On" : "Off";
                int result = _camera.IMV_SetEnumFeatureSymbol("TriggerMode", triggerMode);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"设置触发模式失败,错误码:{result}");
                }
                // è®¾ç½®è§¦å‘源
                if (mode == TriggerMode.On)
                {
                    string triggerSource = GetTriggerSourceString(source);
                    result = _camera.IMV_SetEnumFeatureSymbol("TriggerSource", triggerSource);
                    if (result != IMVDefine.IMV_OK)
                    {
                        throw new Exception($"设置触发源失败,错误码:{result}");
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置触发模式失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–è§¦å‘æ¨¡å¼
        /// </summary>
        /// <param name="mode">触发模式</param>
        /// <param name="source">触发源</param>
        /// <returns>获取是否成功</returns>
        public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source)
        {
            mode = TriggerMode.Off;
            source = TriggerSource.Software;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                // èŽ·å–è§¦å‘æ¨¡å¼
                IMVDefine.IMV_String triggerMode = new IMVDefine.IMV_String();
                int result = _camera.IMV_GetEnumFeatureSymbol("TriggerMode", ref triggerMode);
                if (result == IMVDefine.IMV_OK)
                {
                    mode = triggerMode.str == "On" ? TriggerMode.On : TriggerMode.Off;
                }
                // èŽ·å–è§¦å‘æº
                IMVDefine.IMV_String triggerSource = new IMVDefine.IMV_String();
                result = _camera.IMV_GetEnumFeatureSymbol("TriggerSource", ref triggerSource);
                if (result == IMVDefine.IMV_OK)
                {
                    source = GetTriggerSourceFromString(triggerSource.str);
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取触发模式失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®æ›å…‰æ—¶é—´
        /// </summary>
        /// <param name="value">曝光时间(微秒)</param>
        /// <returns>设置是否成功</returns>
        public override bool SetExpouseTime(double value)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                // éªŒè¯æ›å…‰æ—¶é—´èŒƒå›´
                double minExp = 0, maxExp = 0;
                int result = _camera.IMV_GetDoubleFeatureMin("ExposureTime", ref minExp);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"获取曝光时间最小值失败,错误码:{result}");
                }
                result = _camera.IMV_GetDoubleFeatureMax("ExposureTime", ref maxExp);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"获取曝光时间最大值失败,错误码:{result}");
                }
                if (value < minExp || value > maxExp)
                {
                    throw new Exception($"曝光时间超出范围,有效范围:{minExp} - {maxExp}");
                }
                // è®¾ç½®æ›å…‰æ—¶é—´
                result = _camera.IMV_SetDoubleFeatureValue("ExposureTime", value);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"设置曝光时间失败,错误码:{result}");
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置曝光时间失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–æ›å…‰æ—¶é—´
        /// </summary>
        /// <param name="value">曝光时间</param>
        /// <returns>获取是否成功</returns>
        public override bool GetExpouseTime(out double value)
        {
            value = 0;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                int result = _camera.IMV_GetDoubleFeatureValue("ExposureTime", ref value);
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取曝光时间失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®å¢žç›Š
        /// </summary>
        /// <param name="gain">增益值</param>
        /// <returns>设置是否成功</returns>
        public override bool SetGain(double gain)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw";
                // éªŒè¯å¢žç›ŠèŒƒå›´
                double minGain = 0, maxGain = 0;
                int result = _camera.IMV_GetDoubleFeatureMin(gainFeature, ref minGain);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"获取增益最小值失败,错误码:{result}");
                }
                result = _camera.IMV_GetDoubleFeatureMax(gainFeature, ref maxGain);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"获取增益最大值失败,错误码:{result}");
                }
                if (gain < minGain) gain = minGain;
                if (gain > maxGain) gain = maxGain;
                // è®¾ç½®å¢žç›Š
                result = _camera.IMV_SetDoubleFeatureValue(gainFeature, gain);
                if (result != IMVDefine.IMV_OK)
                {
                    throw new Exception($"设置增益失败,错误码:{result}");
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置增益失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–å¢žç›Šå€¼
        /// </summary>
        /// <param name="gain">增益值</param>
        /// <returns>获取是否成功</returns>
        public override bool GetGain(out double gain)
        {
            gain = 0;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen())
                {
                    throw new Exception("相机未打开");
                }
                string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw";
                int result = _camera.IMV_GetDoubleFeatureValue(gainFeature, ref gain);
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取增益失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®è§¦å‘极性
        /// </summary>
        /// <param name="polarity">触发极性</param>
        /// <returns>是否成功</returns>
        public override bool SetTriggerPolarity(TriggerPolarity polarity)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                string activation = (polarity == TriggerPolarity.RisingEdge || polarity == TriggerPolarity.HighLevel)
                    ? "RisingEdge" : "FallingEdge";
                int result = _camera.IMV_SetEnumFeatureSymbol("TriggerActivation", activation);
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置触发极性失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–è§¦å‘æžæ€§
        /// </summary>
        /// <param name="polarity">触发极性</param>
        /// <returns>是否成功</returns>
        public override bool GetTriggerPolarity(out TriggerPolarity polarity)
        {
            polarity = TriggerPolarity.RisingEdge;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                IMVDefine.IMV_String activation = new IMVDefine.IMV_String();
                int result = _camera.IMV_GetEnumFeatureSymbol("TriggerActivation", ref activation);
                if (result == IMVDefine.IMV_OK)
                {
                    polarity = activation.str == "RisingEdge" ? TriggerPolarity.RisingEdge : TriggerPolarity.FallingEdge;
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取触发极性失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®è§¦å‘滤波时间 (us)
        /// </summary>
        public override bool SetTriggerFliter(double flitertime)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                // åŽç¿ç›¸æœºé€šå¸¸ä½¿ç”¨ LineDebouncerTime æŽ§åˆ¶æ»¤æ³¢
                if (_camera.IMV_FeatureIsValid("LineDebouncerTime"))
                {
                    int result = _camera.IMV_SetDoubleFeatureValue("LineDebouncerTime", flitertime);
                    return result == IMVDefine.IMV_OK;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置触发滤波失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–è§¦å‘æ»¤æ³¢æ—¶é—´ (us)
        /// </summary>
        public override bool GetTriggerFliter(out double flitertime)
        {
            flitertime = 0;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                if (_camera.IMV_FeatureIsValid("LineDebouncerTime"))
                {
                    int result = _camera.IMV_GetDoubleFeatureValue("LineDebouncerTime", ref flitertime);
                    return result == IMVDefine.IMV_OK;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取触发滤波失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®è§¦å‘å»¶æ—¶ (us)
        /// </summary>
        public override bool SetTriggerDelay(double delay)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                if (_camera.IMV_FeatureIsValid("TriggerDelay"))
                {
                    int result = _camera.IMV_SetDoubleFeatureValue("TriggerDelay", delay);
                    return result == IMVDefine.IMV_OK;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置触发延时失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–è§¦å‘å»¶æ—¶ (us)
        /// </summary>
        public override bool GetTriggerDelay(out double delay)
        {
            delay = 0;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                if (_camera.IMV_FeatureIsValid("TriggerDelay"))
                {
                    int result = _camera.IMV_GetDoubleFeatureValue("TriggerDelay", ref delay);
                    return result == IMVDefine.IMV_OK;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取触发延时失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®ä¿¡å·çº¿æ¨¡å¼
        /// </summary>
        public override bool SetLineMode(IOLines line, LineMode mode)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                // é€‰æ‹©çº¿è·¯
                int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString());
                if (result != IMVDefine.IMV_OK) return false;
                // è®¾ç½®æ¨¡å¼
                string lineMode = mode == LineMode.Input ? "Input" : "Output";
                result = _camera.IMV_SetEnumFeatureSymbol("LineMode", lineMode);
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置信号线模式失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è®¾ç½®ä¿¡å·çº¿ç”µå¹³çŠ¶æ€
        /// </summary>
        public override bool SetLineStatus(IOLines line, LineStatus linestatus)
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                // ä»…对输出线路有效
                int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString());
                if (result != IMVDefine.IMV_OK) return false;
                bool status = linestatus == LineStatus.Hight;
                result = _camera.IMV_SetBoolFeatureValue("UserOutputValue", status);
                return result == IMVDefine.IMV_OK;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"设置信号线状态失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–ä¿¡å·çº¿ç”µå¹³çŠ¶æ€
        /// </summary>
        public override bool GetLineStatus(IOLines line, out LineStatus lineStatus)
        {
            lineStatus = LineStatus.Low;
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString());
                if (result != IMVDefine.IMV_OK) return false;
                bool status = false;
                result = _camera.IMV_GetBoolFeatureValue("LineStatus", ref status);
                if (result == IMVDefine.IMV_OK)
                {
                    lineStatus = status ? LineStatus.Hight : LineStatus.Low;
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"获取信号线状态失败:{ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// è‡ªåŠ¨ç™½å¹³è¡¡
        /// </summary>
        public override bool AutoBalanceWhite()
        {
            try
            {
                if (_camera == null || !_camera.IMV_IsOpen()) return false;
                if (_camera.IMV_FeatureIsValid("BalanceWhiteAuto"))
                {
                    int result = _camera.IMV_SetEnumFeatureSymbol("BalanceWhiteAuto", "Once");
                    return result == IMVDefine.IMV_OK;
                }
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"自动白平衡失败:{ex.Message}");
                return false;
            }
        }
        #endregion
        #region é‡‡é›†å’Œè½¬æ¢è¾…助方法
        /// <summary>
        /// å›¾åƒé‡‡é›†çº¿ç¨‹å¤„理函数
        /// </summary>
        private void GrabThreadProc()
        {
            while (_threadRunning)
            {
                IMVDefine.IMV_Frame frame = new IMVDefine.IMV_Frame();
                try
                {
                    // èŽ·å–å›¾åƒå¸§
                    int result = _camera.IMV_GetFrame(ref frame, 1000);
                    if (result == IMVDefine.IMV_OK)
                    {
                        // å¤„理图像帧
                        ProcessFrame(frame);
                    }
                    else
                    {
                        // å³ä½¿èŽ·å–å¤±è´¥ï¼Œä¹Ÿå°è¯•é‡Šæ”¾å¸§ï¼Œé˜²æ­¢SDK内部缓存泄露
                        // æ³¨æ„ï¼šframe是每次新建的,如果GetFrame没填充,这里释放应该是安全的(视SDK实现而定)
                        var tempFrame = frame;
                        _camera.IMV_ReleaseFrame(ref tempFrame);
                        if ((uint)result != 0x80000001 && result != -119 && result != -102) // è¶…时错误代码
                        {
                            // éžè¶…时错误
                            System.Diagnostics.Debug.WriteLine($"获取图像帧失败,错误码:{result}");
                            Thread.Sleep(10); // å‡ºé”™æ—¶ç¨ä½œç­‰å¾…
                        }
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"采集线程异常:{ex.Message}");
                    Thread.Sleep(10);
                }
                Thread.Sleep(1);
            }
        }
        /// <summary>
        /// å¤„理图像帧
        /// </summary>
        /// <param name="frame">图像帧</param>
        private void ProcessFrame(IMVDefine.IMV_Frame frame)
        {
            try
            {
                // å°†å›¾åƒæ•°æ®è½¬æ¢ä¸ºBitmap
                Bitmap bitmap = ConvertFrameToBitmap(frame);
                if (bitmap != null)
                {
                    // è§¦å‘图像采集事件
                    CameraEventArgs args = new CameraEventArgs(SN, bitmap);
                    ImageGrabbed?.Invoke(this, args);
                    // æ›´æ–°å›žè°ƒå›¾åƒ
                    CallBackImg = bitmap;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"处理图像帧失败:{ex.Message}");
            }
            finally
            {
                // ç¡®ä¿æ— è®ºå¦‚何都释放帧,防止缓存占满
                var tempFrame = frame;
                _camera.IMV_ReleaseFrame(ref tempFrame);
            }
        }
        /// <summary>
        /// å°†å›¾åƒå¸§è½¬æ¢ä¸ºBitmap
        /// </summary>
        /// <param name="frame">图像帧</param>
        /// <returns>Bitmap对象</returns>
        private Bitmap ConvertFrameToBitmap(IMVDefine.IMV_Frame frame)
        {
            try
            {
                Bitmap bitmap = null;
                switch (frame.frameInfo.pixelFormat)
                {
                    case IMVDefine.IMV_EPixelType.gvspPixelMono8:
                        bitmap = CreateMono8Bitmap(frame);
                        break;
                    case IMVDefine.IMV_EPixelType.gvspPixelBGR8:
                        bitmap = CreateBgr8Bitmap(frame);
                        break;
                    default:
                        // å…¶ä»–格式需要转换
                        bitmap = ConvertToBGR8(frame);
                        break;
                }
                return bitmap;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"图像格式转换失败:{ex.Message}");
                return null;
            }
        }
        /// <summary>
        /// åˆ›å»ºMono8格式Bitmap
        /// </summary>
        private Bitmap CreateMono8Bitmap(IMVDefine.IMV_Frame frame)
        {
            Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format8bppIndexed);
            // è®¾ç½®ç°åº¦è°ƒè‰²æ¿
            ColorPalette palette = bitmap.Palette;
            for (int i = 0; i < 256; i++)
            {
                palette.Entries[i] = Color.FromArgb(i, i, i);
            }
            bitmap.Palette = palette;
            // å¤åˆ¶å›¾åƒæ•°æ®
            BitmapData bmpData = bitmap.LockBits(
                new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.WriteOnly,
                bitmap.PixelFormat);
            // ä½¿ç”¨CopyMemory API进行内存复制
            CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height)));
            bitmap.UnlockBits(bmpData);
            return bitmap;
        }
        /// <summary>
        /// åˆ›å»ºBGR8格式Bitmap
        /// </summary>
        private Bitmap CreateBgr8Bitmap(IMVDefine.IMV_Frame frame)
        {
            Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bitmap.LockBits(
                new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.WriteOnly,
                bitmap.PixelFormat);
            // ä½¿ç”¨CopyMemory API进行内存复制
            CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height)));
            bitmap.UnlockBits(bmpData);
            return bitmap;
        }
        /// <summary>
        /// è½¬æ¢ä¸ºBGR8格式
        /// </summary>
        private Bitmap ConvertToBGR8(IMVDefine.IMV_Frame frame)
        {
            IMVDefine.IMV_PixelConvertParam convertParam = new IMVDefine.IMV_PixelConvertParam
            {
                nWidth = frame.frameInfo.width,
                nHeight = frame.frameInfo.height,
                ePixelFormat = frame.frameInfo.pixelFormat,
                pSrcData = frame.pData,
                nSrcDataLen = frame.frameInfo.size,
                nPaddingX = frame.frameInfo.paddingX,
                nPaddingY = frame.frameInfo.paddingY,
                eBayerDemosaic = IMVDefine.IMV_EBayerDemosaic.demosaicBilinear,
                eDstPixelFormat = IMVDefine.IMV_EPixelType.gvspPixelBGR8,
                nDstBufSize = frame.frameInfo.width * frame.frameInfo.height * 3
            };
            IntPtr dstBuffer = Marshal.AllocHGlobal((int)convertParam.nDstBufSize);
            convertParam.pDstBuf = dstBuffer;
            try
            {
                int result = _camera.IMV_PixelConvert(ref convertParam);
                if (result == IMVDefine.IMV_OK)
                {
                    return CreateBgr8BitmapFromBuffer(dstBuffer, frame.frameInfo.width, frame.frameInfo.height);
                }
                return null;
            }
            finally
            {
                Marshal.FreeHGlobal(dstBuffer);
            }
        }
        /// <summary>
        /// ä»Žç¼“冲区创建BGR8 Bitmap
        /// </summary>
        private Bitmap CreateBgr8BitmapFromBuffer(IntPtr buffer, uint width, uint height)
        {
            Bitmap bitmap = new Bitmap((int)width, (int)height, PixelFormat.Format24bppRgb);
            BitmapData bmpData = bitmap.LockBits(
                new Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.WriteOnly,
                bitmap.PixelFormat);
            // ä½¿ç”¨CopyMemory API进行安全的内存复制
            CopyMemory(bmpData.Scan0, buffer, (uint)(width * height * 3));
            bitmap.UnlockBits(bmpData);
            return bitmap;
        }
        /// <summary>
        /// å°†è§¦å‘源枚举转换为字符串
        /// </summary>
        private string GetTriggerSourceString(TriggerSource source)
        {
            return source switch
            {
                TriggerSource.Software => "Software",
                TriggerSource.Line0 => "Line0",
                TriggerSource.Line1 => "Line1",
                TriggerSource.Line2 => "Line2",
                TriggerSource.Line3 => "Line3",
                TriggerSource.Line4 => "Line4",
                TriggerSource.Line5 => "Line5",
                _ => "Software"
            };
        }
        /// <summary>
        /// å°†å­—符串转换为触发源枚举
        /// </summary>
        private TriggerSource GetTriggerSourceFromString(string source)
        {
            return source switch
            {
                "Software" => TriggerSource.Software,
                "Line0" => TriggerSource.Line0,
                "Line1" => TriggerSource.Line1,
                "Line2" => TriggerSource.Line2,
                "Line3" => TriggerSource.Line3,
                "Line4" => TriggerSource.Line4,
                "Line5" => TriggerSource.Line5,
                _ => TriggerSource.Software
            };
        }
        #endregion
        #region IDisposable实现
        private bool _disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // é‡Šæ”¾æ‰˜ç®¡èµ„源
                }
                // é‡Šæ”¾éžæ‰˜ç®¡èµ„源
                CloseDevice();
                _disposed = true;
            }
        }
        public override void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}