Vision Pro 界面源码编写基础(C#配合vpp文件)

在康耐视实习,那就必须得掌握我们公司大大大厉害的产品Vision Pro啦。经过大半个月的学习训练,基本掌握了不少技能。在这里写个阶段性小结,记录一下子。
贴两个文档,一个中文的(版本较老,VB版),还有一个VP自带的英文文档。
传送门,提取码ny2r


下面进入正题

准备工具:

正版Vision Pro、Visual Studio 2012(笔者使用版本,也是公司默认版本)、
Cognex开发工具包

步骤0:添加引用如下
注意Cognex名下的包即可
(前4个是必须的基本引用)

步骤1:使用QuickBuild创建自己的操作程序(.vpp)

这里我就不多说了,默认大家都会一些PM、Blob这种基本操作,如果有需要详细说的,评论打出1让我看见哈(啧啧,这个人有点过分哈哈)

步骤2:配置VS

这里由于公司给我们配置了电脑,给的时候就有Cognex的工具包了。我想大家如果购买的正版Vision Pro也是会有的,这里提两个注意点:

(1).net框架是需要4.6版本的,2012默认4.5,记得升级。
VS 框架选择

大部分人下载下来的4.6安装时都会提示电脑已安装更高版本。这个问题困扰了我好一会儿,同事都纷纷转战2015或2017,但最后我发现是因为下载的.net 4.6的版本问题,导致的无法安装。应该安装开发包(给个官方链接),如果实在搞不懂,那就全下下来试一遍吧~

(2)Vision Pro工具箱的安装。
VisionPro 控件向导
傻瓜式点就完了,然后VS里面ToolBox应该就有了。
ToolBox安装完成

步骤3:根据pdf438页开始码

这里按照他说的来就行了,不过他给的是VB程序,我改成了C#,现在公司内部好像都用C#开发了。
过程比较多,我从我的难点一步步说,详细的大家自己看pdf。最后我会贴码。

(1)看的懂.net框架的构造
project框架
每个文件的作用是什么,得知道。
(2)JobManager在代码中就代表了你的整个vpp项目,JobManager.Job(i)代表你项目中得第i个Job。
// 加载vpp作为一个job manager
myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile("../../../Job722.vpp")
// 引用job manager中的第一个
myJob = myJobManager.Job(0);
(3)”委托“指A线程访问B线程的功能时使用的东西,其处理模式为:

首先为跨线程事务创建一个相同声明的委托

// 每个win窗体控件只能通过创建它的线程来访问
// 创建一个有预期签名的委托 用于指向下面的停止事
delegate void myJobManagerDelegate(Object ByVaklsender,CogJobManagerActionEventArgs _ByVale);

在该事务创建该委托实例,提供2中实例化方法如下

 // 创建委托实例 并指向停止事件的函数指
myJobManagerDelegate mydelegate = myJobManager_Stopped
myJobManagerDelegate mydelegate = new myJobManagerDelegate(myJobManager_Stopped);

创建一个该事务参数的数组

// 定义一个参数数
Object[] myobj = new Object[2] { ByValsender, _ByVale };

使用控件Invoke方法调用委托,完成操作

// 使用控件“魔术”调用Invoke,在GUI线程上调用我们的委托(当前我们在vpp线程,但是需要使用GUI线程来使按钮恢复可选,所以需要委托给GUI线程
this.Invoke(mydelegate, myobj);

最后,程序是怎么发现线程之间的不通融的呢?这就是为什么Invoke厉害的地方了,在事务中添加下列代码

if (InvokeRequired)
{
    # 创建一个指针指向该函数
    # 在正确的线程上调用同一个函数
}

InvokeRequired是每一个控件的”魔术“属性,如果程序处在错误的线程上,它就会返回”true“。这就是Microsoft的一个用于校正线程乱问题的标准方法。请牢记!

(4)Vision Pro 内部控件的调用,参数获取
运行状态在操作队列中

首先我们需要现实的代码状态,是一个ICogRecord类,所以我们需要实例化该对象,然后将JobManager.UserResult传递给他。这样我们就获取到我们Job的结果了。之后就是检索我们需要的result。

// 输出运行状
Cognex.VisionPro.ICogRecord myrecord = myJobManager.UserResult() as ICogRecord
RunStatusTextBox.Text = myrecord.SubRecords["UserResultTag"].Content.ToString() + ":" + myrecord.SubRecords["JobName"].Content.ToString() + "-->";
RunStatusTextBox.Text += myrecord.SubRecords["RunStatus"].Content.ToString();

如果是要显示Job工具里的运行结果或者运行参数等,需要先在QuickBuild里面将你想要的参数添加到发送项,这样在Code里面才能获取到,不然有的参数(如Item里面的参数)是没有权限获得的,而Count这样的public变量可以直接调用,见程序

// 输出参数
Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool2"] as Cognex.VisionPro.PMAlign.CogPMAlignTool
try
{
    textBox1.Text = myPM.Results.Count.ToString();
}
catch (Exception e5)
{
    textBox1.Text = e5.ToString();
}

输出图像的话,Vision Pro提供四类图像显示控件,分别是:
1)CogToolDisplay
2)CogRecordsDisplay
3)CogRecordDisplay
4)CogDisplay
ToolD最复杂,它知道如何连接到Vision Pro工具以及如何调用工具所有的执行结果,它通过显示区上放一个下拉框允许用户选择浏览哪张图像(就像QB里面的显示结果控件一样)。
RecordsD看上去和TD一样,也允许下拉选择,但是他对Vision Pro工具一无所知,他不能从工具中获取Records树,必须用编程的方法自己来获取。
RecordD(注意上面一个是Recordsssssssssss)显示单个图像记录及其图形的子记录(也就是你在fixture生成的图上进行操作,那么这些操作都会显示在fixture图上,这就是子记录),不可以自己选择下拉咯,所以他也是一无所知的,而且不提供Record树。
Display是最低级的显示控件,由其他所有显示控件来使用,一无所知,只显示一个图像和一组图形,图形图像必须通过编程提供。
这里我们选择RecordD。

Cognex.VisionPro.ICogRecord graphic_record;
graphic_record = myrecord.SubRecords["ShowLastRunRecordForUserQueue"];
graphic_record = graphic_record.SubRecords["LastRun"];
graphic_record = graphic_record.SubRecords[1];
// graphic_record = graphic_record.SubRecords["CogFixtureTool1.OutputImage"]; 方法2
CogRecordDisplay1.Record = graphic_record;
CogRecordDisplay1.Fit(true);

差不多基本的功能就是这样了,这一套做完无Bug,大家基本上可以在此基础上进行二次开发了。下面贴一下

源码

控件外形

设置的各控件名称
控件定义

Form1.cs整体

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.QuickBuild;

namespace mywind
{
    public partial class Form1 : Form
    {
        private CogJobManager myJobManager;
        private CogJob myJob;
        private CogJobIndependent myIndepenentJob;

        public Form1()
        {
            InitializeComponent();
            // 临时办法:线程间相互访问
            //Control.CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 加载vpp作为一个job manager
            myJobManager = (CogJobManager)CogSerializer.LoadObjectFromFile("../../../Job722.vpp");
            // 引用job manager中的第一个job
            myJob = myJobManager.Job(0);
            // 初始化一个对象,其参数引用包含更多关于job manager的信息
            myIndepenentJob = myJob.OwnedIndependent;
            // 清空所有job manager队列,刷新陈旧的图像和结果
            myJobManager.UserQueueFlush();
            myJobManager.FailureQueueFlush();
            myJob.ImageQueueFlush();
            myIndepenentJob.RealTimeQueueFlush();

            // 为停止操作绑定事件
            // CJM没有在GUI创建按钮,但是它调用了一个按钮停止事件!这个事件需要访问GUI线程的按钮!故需要“委托”
            myJobManager.Stopped += new CogJobManager.CogJobManagerStoppedEventHandler(myJobManager_Stopped);
            //myJobManager.Stopped += myJobManager_Stopped;
            // 状态显示操作绑定事件
            myJobManager.UserResultAvailable += myJobManager_UserResultAvailable;

            // 连接bar状态栏
            CogDisplaystatusBar1.Display = CogRecordDisplay1;

        }



        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // 避免死锁
                //ControlBox = false;

                ShowTrainCheckBox.Enabled = false;
                RunContCheckBox.Enabled = false;
                // 防止在单词任务完成前再次调用任务 让按钮不可点击就行了
                RunOnceButton.Enabled = false;
                myJobManager.Run();
               
            }
            catch (Exception e1)
            {
                MessageBox.Show(e1.Message);
            }

        }

        // 每个win窗体控件只能通过创建它的线程来访问
        // 创建一个有预期签名的委托 用于指向下面的停止事件
        delegate void myJobManagerDelegate(Object ByVaklsender,CogJobManagerActionEventArgs _ByVale);

        // 所有工作完成 CJM运行一个停止事件 重启按钮
        private void myJobManager_Stopped(Object ByValsender, CogJobManagerActionEventArgs _ByVale)
        {
            if (InvokeRequired)
            {
                // 创建委托实例 并指向停止事件的函数指针
                //myJobManagerDelegate mydelegate = myJobManager_Stopped;
                myJobManagerDelegate mydelegate = new myJobManagerDelegate(myJobManager_Stopped);
                // 定义一个参数数组
                Object[] myobj = new Object[2] { ByValsender, _ByVale };
                // 使用控件“魔术”调用Invoke,在GUI线程上调用我们的委托(当前我们在vpp线程,但是需要使用GUI线程来使按钮恢复可选,所以需要委托给GUI线程)
                this.Invoke(mydelegate, myobj);
            }
            //线程报错 暂时不管
            //ControlBox = true;
            RunOnceButton.Enabled = true;
            RunContCheckBox.Enabled = true;
            ShowTrainCheckBox.Enabled = true;
        }

        private void RunContCheckBox_CheckedChanged(object sender, EventArgs e)
        {

            if (RunContCheckBox.Checked)
            {
                try
                {
                    //ControlBox = false;
                    ShowTrainCheckBox.Enabled = false;
                    RunOnceButton.Enabled = false;
                    myJobManager.RunContinuous();
                }
                catch (Exception e2)
                {
                    MessageBox.Show(e2.Message);
                }
            }
            else
            {
                try
                {
                    
                    RunContCheckBox.Enabled = false;
                    myJobManager.Stop();
                }
                catch(Exception e3)
                {
                    MessageBox.Show(e3.Message);

                }
            }
        }

        private void myJobManager_UserResultAvailable(Object sender, CogJobManagerActionEventArgs e)
        {
            if (InvokeRequired)
            {
                myJobManagerDelegate mydel = new myJobManagerDelegate(myJobManager_UserResultAvailable);
                Object[] args = new Object[2] { sender, e };
                Invoke(mydel, args);
                return;
            }
            // 输出运行状态
            Cognex.VisionPro.ICogRecord myrecord = myJobManager.UserResult() as ICogRecord;
            RunStatusTextBox.Text = myrecord.SubRecords["UserResultTag"].Content.ToString() + ":" + myrecord.SubRecords["JobName"].Content.ToString() + "-->";
            RunStatusTextBox.Text += myrecord.SubRecords["RunStatus"].Content.ToString();

            // 输出参数
            Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
            Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
            Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool2"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
            Cognex.VisionPro.ICogRecord tmpRecord;
            try
            {
                textBox1.Text = myPM.Results.Count.ToString();
            }
            catch (Exception e5)
            {
                textBox1.Text = e5.ToString();
            }
            // 输出图像
            Cognex.VisionPro.ICogRecord graphic_record;
            graphic_record = myrecord.SubRecords["ShowLastRunRecordForUserQueue"];
            graphic_record = graphic_record.SubRecords["LastRun"];
            graphic_record = graphic_record.SubRecords[1];
            //graphic_record = graphic_record.SubRecords["CogFixtureTool1.OutputImage"];
            CogRecordDisplay1.Record = graphic_record;
            CogRecordDisplay1.Fit(true);
            //RunStatusTextBox.Text = "";
        }

        private void RetrainButton_Click(object sender, EventArgs e)
        {
            // 调取工具
            Cognex.VisionPro.ToolGroup.CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
            Cognex.VisionPro.ToolBlock.CogToolBlock myTB = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
            Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB.Tools["CogPMAlignTool1"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
            // 重训练
            myPM.Pattern.Train();
            RunStatusTextBox.Text = myPM.RunStatus.ToString();
        }

        private void ShowTrainCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (ShowTrainCheckBox.Checked)
            {
                RunOnceButton.Enabled = false;
                RunContCheckBox.Enabled = false;
                RetrainButton.Enabled = true;
                Cognex.VisionPro .ToolGroup .CogToolGroup myTG = myJob.VisionTool as Cognex.VisionPro.ToolGroup.CogToolGroup;
                Cognex.VisionPro.ToolBlock.CogToolBlock myTB1 = myTG.Tools["Detect_Defects"] as Cognex.VisionPro.ToolBlock.CogToolBlock;
                Cognex.VisionPro.PMAlign.CogPMAlignTool myPM = myTB1.Tools["CogPMAlignTool1"] as Cognex.VisionPro.PMAlign.CogPMAlignTool;
                Cognex.VisionPro.ICogRecord tmpRecord;
                tmpRecord = myPM.CreateCurrentRecord();
                tmpRecord = tmpRecord.SubRecords["TrainImage"];
                CogRecordDisplay1.Record = tmpRecord;
                CogRecordDisplay1.Fit(true);

            }
            else
            {
                RunOnceButton.Enabled = true;
                RunContCheckBox.Enabled = true;
                RetrainButton.Enabled = false;
                CogRecordDisplay1.Record = null;
            }
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

最后把我用的Job的框架给大家看一眼,不然不好理解我里面的一些名字是啥意思
Job
Detect_Defects的part1
Detect_Defects的part2

这四部分给出来,大家按照我给的名字命名你的控件,然后如果想先爽一下,直接把我给的Form1.cs覆盖掉你原本的就应该ok了哦。但是我建议大家,还是先按照pdf自己做,不懂了或者卡壳了,再来看,可以理解更深哦~


最后笔者想说,如果有不理解,或者读后有所感想,想说一说哪里写的不够清楚,或者思路或者语言构造,甚至一句感谢,都是我十分看重的财富。我写了也不少了,但真的没什么人有学术互动的。怎么说呢,有点悲伤。
还有一点,我写的所有东西,如果大家有需要,我可以放到Git里,哪怕有一个人,我也不嫌麻烦。所以,还是想大家跟我说的。。。

也不知道怎么结尾,就给大家拜个早年吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351