创建基于QuickBuild的C#应用程序
使用的QuickBuild应用程序位于%VPRO_ROOT%/ Samples / Programming / QuickBuild / advancedAppOne.vpp中。在继续之前,可以在QuickBuild中运行该应用程序。
QuickBuild应用程序使用PatMax查找支架的“耳朵”之一,使用CogFixture工具设置图像的坐标空间,使用Blob工具查找两个中心孔,并使用CogDistancePointToPointTool测量孔之间的距离。最后,结果分析工具通过测试两个孔之间的距离是否在可接受的范围内来确定检查是否通过。
该指南分为四个步骤:
- 加载QuickBuild应用程序
- 添加GUI并使用线程
- 增强GUI并显示结果
- 显示影像
1.加载QuickBuild应用程序
1. 打开Visual Studio,创建一个C#.NetFramework的Windows应用程序.
2. 添加以下引用
- Cognex.VisionPro
- Cognex.VisionPro.Core
- Cognex.VisionPro.QuickBuild.Core
3. 打开Form1.cs,添加代码如下:
using Cognex.VisionPro;
using Cognex.VisionPro.QuickBuild;
4. 声明变量
private CogJobManager myJobManager;
private CogJob myJob;
private CogJobIndependent myIndependentJob;
5. 在窗体Load事件中加载QuickBuild 应用程序
/// <summary>
/// 从一个文件中加载作业管理器对象。
/// 获取该管理器中的第一个作业。
/// 从该作业中获取与之关联的独立作业。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
myJobManager = CogSerializer.LoadObjectFromFile(Environment.GetEnvironmentVariable("VPRO_ROOT")+"\\Samples\\Programming\\QuickBuild\\advancedAppOne.vpp") as CogJobManager;
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
//确保队列为空:
//在加载一个 VisionPro 持久化文件时,需要确保所有队列都是空的。这是因为加载的过程会精确地重建保存时的对象状态。如果在加载过程中队列中仍然保留着未处理的数据,这可能会导致不一致或错误的行为。
//冲洗队列:
//“冲洗所有队列”意味着清空所有待处理的数据,以确保系统在加载新的作业或状态时处于一个干净的状态。这种做法是为了避免在处理新数据时与旧数据发生冲突,从而确保系统的稳定性和可靠性。
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
}
6. 关闭
QuickBuild 应用程序的 CogJobManager 对象在运行时会创建多个线程。关闭 CogJobManager 是非常重要的。
当窗体关闭时,将调用 Form1_FormClosing 方法。
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myJobManager.Shutdown();
}
2. 添加GUI并使用线程
1. 创建一次运行按钮
您将添加的第一个用户界面元素是运行已保存的QuickBuild应用程序的按钮。在Visual Studio工具箱中,将“按钮”从“公共控件”部分拖到Form1。将此按钮命名为RunOnceButton并将其文本设置为Run一次。
双击按钮以创建其Click处理程序,然后键入以下代码:
private void RunOnceButton_Click(object sender, EventArgs e)
{
try
{
myJobManager.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Run方法在myJobManager中运行所有作业,其中包含已保存的QuickBuild应用程序。按钮的Click处理程序在Try / Catch块内调用Run,以处理QuickBuild应用程序开始运行时可能发生的任何错误。
立即运行程序,然后单击“运行一次”按钮以运行应用程序。(由于此时您未显示任何输出,因此您将看不到任何内容。)尝试尽快重复单击RunOnce按钮。您应该收到一条错误消息。
2. 处理作业事件
快速单击“运行一次”按钮时出现错误的原因是“运行”是立即返回的异步方法。如果在作业运行时再次调用它。您会收到CogNotStoppedException错误。
为避免此问题,您将在作业运行时禁用“运行一次”按钮,并在作业停止时重新启用它。在作业开始时禁用按钮非常容易。您所需要做的就是禁用其Click处理程序中的按钮:
private void RunOnceButton_Click(object sender, EventArgs e)
{
try
{
RunOnceButton.Enabled = false;
myJobManager.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
在作业停止运行时重新启用按钮会稍微复杂一些。当作业管理器中的所有作业均已完成运行时,CogJobManager会触发Stopped事件。您将把代码重新放入作业管理器的Stopped事件处理程序中的按钮。
在Form1类的末尾,为作业管理器编写Stopped处理程序,如下所示:
private void MyJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
RunOnceButton.Enabled = true;
}
定义了Stopped事件处理程序后,需要确保作业管理器知道它在哪里。使用AddHandler注册事件的处理程序。AddHandler的第一个参数是事件;第二个参数是处理程序的地址。将以下代码添加到Form1_Load方法的末尾:
//注册停止事件
myJobManager.Stopped += MyJobManager_Stopped;
每当您注册事件处理程序时,最好在关闭程序之前将其删除。在此示例中,执行此操作的最佳位置是在关闭作业管理器之前的窗体的FormClosing处理程序中。现在,FormClosing处理程序应如下所示:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myJobManager.Stopped -= MyJobManager_Stopped;
myJobManager.Shutdown();
}
如果此时运行程序,则会出现错误:跨线程操作无效。
3.处理跨线程函数调用
请记住,作业管理器会创建多个线程。这些线程之一触发运行已停止事件处理程序的Stopped事件,并最终尝试重新启用“运行一次”按钮。此事件序列是非法的,因为按钮-或任何其他Microsoft Windows Forms控件-只能由创建该控件的线程访问。该按钮不是由任何作业管理器线程创建的。
Microsoft提供了一种对控件进行线程安全调用的方法。
注意:您可以在此MDSN文章中了解有关进行线程安全调用的更多信息。
每个控件都有一个InvokeRequired属性,如果正在从未创建控件的线程中调用它,则该属性返回True。如果为True,则必须创建一个指向将访问该控件的函数的指针,切换到创建该控件的线程,然后使用该指针再次调用该函数。
在.NET中,指向函数的指针是委托。您将声明一个与您要处理的事件具有相同功能签名(参数)的委托。在这种情况下,您的委托人具有与Stopped事件相同的签名。在Stopped事件处理程序之前添加以下代码行:
private delegate void myJobManagerDelegate(object sender,CogJobManagerActionEventArgs e);
现在,在Stopped事件处理程序中,您将检查是否从错误的线程调用了该处理程序。如果是这样,则将使用定义的委托创建指向处理程序的指针,然后使用Invoke方法以递归方式调用它。这是Stopped事件处理程序的外观:
private void MyJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new myJobManagerDelegate(MyJobManager_Stopped),new object[] { sender,e});
return;
}
RunOnceButton.Enabled = true;
}
3. 增强GUI并显示结果
1. 添加连续运行按钮
您将添加的下一个用户界面元素是“运行连续”按钮。当您单击此按钮时,它将保持按下状态,并且该应用程序将运行,直到再次单击该按钮。若要获得此行为,本示例使用一个复选框并将其外观更改为按钮。
确保Form1.cs [设计]选项卡处于活动状态。从Visual Studio工具箱的“公共控件”部分中,将一个CheckBox控件拖到窗体上。将其文本更改为“连续运行”。将其名称更改为RunContCheckBox。最后,将其外观更改为Button。
双击新的“连续运行”按钮以创建其Check_Changed处理程序,并添加以下代码:
private void RunContentCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (RunContentCheckBox.Checked)
{
try
{
myJobManager.RunContinuous();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
{
try
{
myJobManager.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
RunContinuous方法重复运行myJobManager中的所有作业,该作业包含您保存的QuickBuild应用程序,直到Stop停止它们为止。CheckedChanged处理程序负责在作业开始时禁用“运行一次”按钮,并在等待作业停止时禁用“连续运行”按钮。
将以下代码行添加到“已停止”处理程序中,以在所有作业停止后重新启用“连续运行”按钮。
private void MyJobManager_Stopped(object sender, CogJobManagerActionEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new myJobManagerDelegate(MyJobManager_Stopped),new object[] { sender,e});
return;
}
RunOnceButton.Enabled = true;
RunContentCheckBox.Enabled = true;
}
并将以下代码行添加到“运行一次”按钮的单击处理程序,以在作业运行一次时禁用“连续运行”按钮。
RunContentCheckBox.Enabled = false;
如果此时运行程序,则会在适当的时间看到按钮变为启用和禁用状态。您也可以单击窗口的关闭框以退出应用程序,即使该应用程序正在运行。
2. 显示程序结果
当用户队列中有新结果可用时,作业管理器将触发UserResultAvailable事件。在那儿,您将从工作中提取数据并将其显示在表单中。
确保Form1.cs [设计]选项卡处于活动状态。从Visual Studio工具箱的“公共控件”部分中,将一个TextBox拖到您的窗体上。将其名称更改为RunStatusTextBox。在这里您将显示应用程序的运行状态。
在Form1类的末尾,添加以下UserResultAvailable事件处理程序。请注意,由于它修改了用户界面项(文本框),因此您需要使用之前学习的Invoke习惯用法。
private void MyJobManager_UserResultAvailable(object sender, CogJobManagerActionEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new myJobManagerDelegate(MyJobManager_UserResultAvailable), new object[] { sender, e });
return;
}
ICogRecord topRecord = myJobManager.UserResult();
RunStatusTextBox.Text = topRecord.SubRecords["UserResultTag"].Content + ":" + topRecord.SubRecords["JobName"].Content + "-->" + topRecord.SubRecords["RunStatus"].Content.ToString();
}
UserResult方法从作业管理器的用户队列中删除并返回最早的结果记录。该记录包含“已发布项目”列表中每个项目的子记录。它还包含这些其他子记录:
UserResultTag
作业运行的序列号
职位名称
返回结果的作业的名称
运行状态
作业的RunStatus
在运行程序之前,您需要在窗体的Load处理程序中注册UserResultsAvailable处理程序,然后在其FormClosing处理程序中注销它。现在,Load处理程序应如下所示:
private void Form1_Load(object sender, EventArgs e)
{
myJobManager = CogSerializer.LoadObjectFromFile(Environment.GetEnvironmentVariable("VPRO_ROOT")+"\\Samples\\Programming\\QuickBuild\\advancedAppOne.vpp") as CogJobManager;
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
//确保队列为空:
//在加载一个 VisionPro 持久化文件时,需要确保所有队列都是空的。这是因为加载的过程会精确地重建保存时的对象状态。如果在加载过程中队列中仍然保留着未处理的数据,这可能会导致不一致或错误的行为。
//冲洗队列:
//“冲洗所有队列”意味着清空所有待处理的数据,以确保系统在加载新的作业或状态时处于一个干净的状态。这种做法是为了避免在处理新数据时与旧数据发生冲突,从而确保系统的稳定性和可靠性。
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
//注册停止事件
myJobManager.Stopped += MyJobManager_Stopped;
myJobManager.UserResultAvailable += MyJobManager_UserResultAvailable;
}
FormClosing处理程序如下所示:
//取消注册与 myJobManager 相关的事件处理程序,以避免在窗体关闭后接收事件。
//调用 Application.DoEvents() 来处理任何挂起的消息,确保在执行后续操作(如关闭作业管理器)时,应用程序不会冻结。
//调用 myJobManager.Shutdown() 方法以正确清理资源和停止后台任务。
//使用 DoEvents() 可以确保在执行 Shutdown 操作之前,所有与用户界面相关的事件都得到处理,从而提高用户体验。尽管 DoEvents() 在某些情况下有其用途,但在大多数情况下,过度使用可能会导致复杂性增加,因此应谨慎使用。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myJobManager.Stopped -= MyJobManager_Stopped;
myJobManager.UserResultAvailable -= MyJobManager_UserResultAvailable;
Application.DoEvents();
myJobManager.Shutdown();
}
除了删除UserResultAvailable处理程序外,FormClosing方法还调用Application.DoEvents()来清除Windows消息队列。它可以防止在单击表单的关闭框之后但未注册UserResultAvailable处理程序之前,如果有新的用户结果可用,则可能发生死锁。
3. 运行应用程序
运行应用程序时,您将在文本框中看到结果。在下一步中,您将在应用程序中添加一个显示。
4. 显示图像
- 使用显示控件显示结果记录
- 访问适当的记录以显示
- 将显示状态栏连接到显示器
1. 显示控件概述
VisionPro提供了四种显示控件,可用于显示图像和图形。
显示控制 | 描述 |
---|---|
CogToolDisplay | 这是您在QuickBuild和视觉工具中看到的显示控件。您可以将VisionPro工具连接到显示控件,并直接从该工具提取记录。它使用下拉列表选择要显示的图像记录。 |
CogRecordsDisplay | 此控件类似于CogToolDisplay控件。它使用户可以从下拉列表中选择图像记录,并显示图像的任何图形子记录。与CogToolDisplay不同,此控件对VisionPro工具一无所知。您必须提供要显示的记录。 |
CogRecordDisplay | 该控件显示单个图像记录及其图形子记录。它没有下拉列表框,并且不允许用户选择图像。 |
CogDisplay | 这是所有其他显示控件用作基础的最低级别的显示控件。此控件显示单个图像和一组图形,这些图形必须直接提供给该控件。 |
在此示例中,您将使用CogRecordDisplay控件显示LastRun.CogFixtureTool1.OutputImage记录,其中包含括号的图像以及显示孔之间距离的图形。
添加对以下程序集的引用:
- Cognex.VisionPro.Controls
- Cognex.VisionPro.Display.Controls
确保Form1.cs [设计]选项卡处于活动状态。从Visual Studio工具箱的VisionPro显示控件部分中,将CogRecordDisplay控件拖到窗体上。
注意:如果在工具箱中看不到CogRecordDisplay,请在工具箱中单击鼠标右键,然后选择“选择项目”。在出现的“选择工具箱项”对话框中,向下滚动直到找到CogRecordDisplay并确保已选中该复选框。
在FormClosing方法的末尾,添加以下代码以在应用程序关闭时处置控件:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
myJobManager.Stopped -= MyJobManager_Stopped;
myJobManager.UserResultAvailable -= MyJobManager_UserResultAvailable;
Application.DoEvents();
myJobManager.Shutdown();
cogDisplayStatusBarV21.Dispose();
cogRecordDisplay1.Dispose();
}
然后在Load方法的末尾添加以下代码,以将显示状态栏与显示器链接起来。
private void Form1_Load(object sender, EventArgs e)
{
myJobManager = CogSerializer.LoadObjectFromFile(Environment.GetEnvironmentVariable("VPRO_ROOT")+"\\Samples\\Programming\\QuickBuild\\advancedAppOne.vpp") as CogJobManager;
myJob = myJobManager.Job(0);
myIndependentJob = myJob.OwnedIndependent;
//确保队列为空:
//在加载一个 VisionPro 持久化文件时,需要确保所有队列都是空的。这是因为加载的过程会精确地重建保存时的对象状态。如果在加载过程中队列中仍然保留着未处理的数据,这可能会导致不一致或错误的行为。
//冲洗队列:
//“冲洗所有队列”意味着清空所有待处理的数据,以确保系统在加载新的作业或状态时处于一个干净的状态。这种做法是为了避免在处理新数据时与旧数据发生冲突,从而确保系统的稳定性和可靠性。
myJobManager.UserQueueFlush();
myJobManager.FailureQueueFlush();
myJob.ImageQueueFlush();
myIndependentJob.RealTimeQueueFlush();
//注册停止事件
myJobManager.Stopped += MyJobManager_Stopped;
myJobManager.UserResultAvailable += MyJobManager_UserResultAvailable;
cogDisplayStatusBarV21.Display = cogRecordDisplay1;
}
获取记录以显示在CogRecordDisplay控件中的代码位于UserResultAvailable处理程序的末尾:
private void MyJobManager_UserResultAvailable(object sender, CogJobManagerActionEventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(new myJobManagerDelegate(MyJobManager_UserResultAvailable), new object[] { sender, e });
return;
}
ICogRecord topRecord = myJobManager.UserResult();
RunStatusTextBox.Text = topRecord.SubRecords["UserResultTag"].Content + ":" + topRecord.SubRecords["JobName"].Content + "-->" + topRecord.SubRecords["RunStatus"].Content.ToString();
ICogRecord tmpRecord = topRecord.SubRecords["ShowLastRunRecordForUserQueue"];
tmpRecord = tmpRecord.SubRecords["LastRun"];
tmpRecord = tmpRecord.SubRecords["CogFixtureTool1.OutputImage"];
cogRecordDisplay1.Record = tmpRecord;
cogRecordDisplay1.Fit(true);
}
对SubRecords的连续调用遵循子记录路径,以获取QuickBuild应用程序的Fixture工具的输出图像。Record属性指定要显示的记录。Fit方法缩放图像及其图形以适合显示控件。
界面设计如下:
如果立即运行该程序,您将看到CogRecordDisplay控件中显示的每个图像。