Baumer工业相机堡盟相机如何实现相机掉线重连(C++)

简介: Baumer工业相机堡盟相机如何实现相机掉线重连(C++)

项目场景:

Baumer工业相机堡盟相机传统开发包BGAPI SDK进行工业视觉软件整合时,常常需要将SDK中一些功能整合到图像处理软件中,方便项目的推进使用;


在项目的图像处理任务中,可能会因为一些硬件比如线缆网卡的原因导致出现偶尔掉线,而软件重启则可以重新连上,这时为了适用于自动化设备不停线的情况下,需要图像处理软件可以自动进行断线重新连接。


注意:本文是基于Baumer的BGAPI SDK的基础上使用C++语言来实现相机的掉线重新连接。


如何发现掉线

Baumer工业相机BGAPI SDK中在相机事件中存在一种PnpEvent事件可以监控相机是否掉线,从而能够及时发现相机的问题。


在BGAPI SDK的C++开发包里存在PnpEvent的事件例程,关键字为:013_PnPEventMode_Handler


下面为例程中PnPEvent事件代码

// CALLBACK
void BGAPI2CALL PnPEventHandler(void * callbackOwner, BGAPI2::Events::PnPEvent * pPnPEvent) {
    if (NULL != pPnPEvent) {
        std::cout << std::endl;
        std::cout << std::endl;
        std::cout << " [callback of " << ((BGAPI2::Interface *)callbackOwner)->GetDisplayName() << "] ";
        std::cout << " EventID " << pPnPEvent->GetId() << " PnPType: "
            << ((pPnPEvent->GetPnPType() == 0) ? "removed" : "added  ")
            << " SerialNumber: " << pPnPEvent->GetSerialNumber() << std::endl;
        std::cout << std::endl;
#if defined(_WIN32)
        Sleep(1000);
#else
        usleep(1000000);
#endif
        // WARNING ACCESS IS NOT THREAD SAVE - YOU SHOULD ALWAYS USE A LOCK (LIKE MUTEX)
        // TO ACCESS THEM FROM DIFFERENT THREADS
        // This example does not use std::mutex to support old compiler without C++11
        gDisplayDeviceLists = true;  // enable refresh of device list display
    } else {
        std::cout << " [callback] - received invalid Interface Event! " << std::endl;
    }
    return;
}

相机掉线重连实现方法:

1、在相机初始化的过程中,在相机对应的网口中注册SDK中PnPEvent回调函数


代码如下所示:

//为相机对应数据流注册掉线触发事件
m_pInterface->RegisterPnPEvent(BGAPI2::Events::EVENTMODE_EVENT_HANDLER);        
m_pInterface->RegisterPnPEventHandler(this, (Events::PnPEventHandler) &PnPEventHandler);

2、在PnPEvent回调函数里进行相机掉线事件的分析并对应实现相机的再次查找


注意在回调里尽量不要处理太多任务,可以从回调里发出信息,在外部处理回调发出的任务信息。本例在回调里处理任务方便参考,请大家理解。


PnPEvent回调函数代码如下所示:

//相机掉线事件
void BGAPI2CALL PnPEventHandler(void * callbackOwner, BGAPI2::Events::PnPEvent * pPnPEvent) 
{
  USES_CONVERSION;
  if (NULL != pPnPEvent) 
  {  
  AfxMessageBox(_T("Trigger Camera lost signal"));  
  CGigeDemoDlg* pDlg = (CGigeDemoDlg*)callbackOwner;  
  int syscount = 0;
  while(syscount==0)
  {
    pDlg->m_pSystemList = SystemList::GetInstance();
    pDlg->m_pSystemList->Refresh();
    int sysindex = 0; 
    for(SystemList::iterator sysIterator = pDlg->m_pSystemList->begin();sysIterator != pDlg->m_pSystemList->end();sysIterator++)
    {
    CString sysDisplay0 = A2W(sysIterator->second->GetDisplayName());
    CString sysModel0 = A2W(sysIterator->second->GetModel()); //bgapi_gige
    CString sysPathName0 = A2W(sysIterator->second->GetPathName());
    if(sysModel0.Find(_T("gige"))!=-1)    //此例程只连接网口Gige相机
    {   
      syscount = pDlg->m_pSystemList->size();
      AfxMessageBox(_T("Camera find")); 
      break;
    }
    sysindex++;
    }
  }
  if(syscount>0&!pDlg->ReConnectCheck)
  {
    pDlg->ReConnectCheck = true;
    pDlg->OnBnClickedBtntestremove();       //释放相机资源
    //pDlg->OnBnClickedBtntestreconnect();  //重新检查连接,此处可以用message触发外部重连功能
    pDlg->ReConnectCheck = false;
  }
#if defined(_WIN32)
  Sleep(1000);
#else
  usleep(1000000);
#endif  
  }
  else 
  {
  //std::cout << " [callback] - received invalid Interface Event! " << std::endl;
  }
  return;
}

3、在PnPEvent掉线发生后在回调中检查相机是否存在,若已经完全掉线,则需要释放对应的资源,防止内存泄漏,若再次发现相机,在未知掉线的情况下,建议对该相机重新初始化。


释放相机资源代码如下所示:


void CGigeDemoDlg::OnBnClickedBtntestremove()
{
  //设置相机移除功能:该Demo只移除当前连接的相机
  StopShowFrame_hThread1();//停止UI上帧率和网口数据通量的显示
  #pragma region
  try
  {
    try
    {
    if (m_pDevice != NULL)
      m_pDevice->GetRemoteNode("AcquisitionStop")->Execute();//发送相机停止采集命令
    }
    catch (BGAPI2::Exceptions::IException& ex)
    {}  
    try
    {
    if (m_pDataStream != NULL)
    {
      m_pDataStream->StopAcquisition();/停止采集相机数据流
      m_pBufferList->DiscardAllBuffers();//释放输入缓冲区队列和输出缓冲区队列中的所有Buffer对象。
    }
    }
    catch (BGAPI2::Exceptions::IException& ex)
    {}    
    if (m_pDataStream != NULL)
    {
    try
    {
      while (m_pBufferList->size() > 0)              //对Bufferlist里每个buffer对象进行释放删除
      {
      m_pBuffer = m_pBufferList->begin()->second;
      m_pBufferList->RevokeBuffer(m_pBuffer);    //对当前buffer对象进行释放
      delete m_pBuffer;                          //对当前buffer对象进行删除
      }
    }
    catch (BGAPI2::Exceptions::IException& ex)
    {}
    try
    {
      //下面关闭步骤和初始化相机正好相反
      m_pDataStream->Close();                        //关闭相机数据流
      m_pDevice->Close();                            //关闭相机本身
      m_pInterface->Close();                         //关闭相机对应网口
      m_pSystem->Close();                            //关闭BGAPI相机主类System
      //BGAPI2::SystemList::ReleaseInstance();       //释放BGAPI相机主类Systemlist资源
    }
    catch (BGAPI2::Exceptions::IException& ex)
    {}
    try
    {     
      BGAPI2::SystemList::ReleaseInstance();         //释放BGAPI相机主类Systemlist资源
    }
    catch (BGAPI2::Exceptions::IException& ex)
    {}
    //Enable the button_inialize
    GetDlgItem(ID_BTNINITIALIZE)->EnableWindow(TRUE);//恢复初始化按钮
    AfxMessageBox(_T("相机已经断开"));
    }
  }
  catch (BGAPI2::Exceptions::IException& ex)
  {
    CString Errinfo;
    Errinfo.Format(_T("ExceptionType:%s! ErrorDescription:%s in function:%s"),ex.GetType(),ex.GetErrorDescription(),ex.GetFunctionName());
    //MessageBox(Errinfo);
  }  
#pragma endregion
}


4、在对应相机释放资源后,对相机进行断线重连操作,先初始化该相机,在重新注册相机流、相机PnPEvent事件、相机图像事件等等,基本上是对相机进行重新连接。


重连函数代码如下所示:

void CGigeDemoDlg::OnBnClickedBtntestreconnect()
{
  // 测试相机重连功能
  OnBnClickedBtninitialize();
  OnBnClickedBtnplay();
  MessageBox(_T("相机已经重新连接"));
}

5、重连操作:先初始化对应相机,在进行相机的重新采集图像。


下面的代码注释较为详细,可以作为参考。


相机初始化代码如下所示:

void CGigeDemoDlg::OnBnClickedBtninitialize()
{
  #pragma region//初始化BGAPI采集类
  USES_CONVERSION;
  //m_imgProcessor = new BGAPI2::ImageProcessor();
  m_pSystemList = SystemList::GetInstance();
  m_pSystemList->Refresh();
  int syscount = m_pSystemList->size();
  #pragma endregion
  #pragma region//循环检查获取的Systemlist序列信息:即获取网口和USB相机的系统配置文件来查询当前已连接相机
  int sysindex = 0; 
  for(SystemList::iterator sysIterator = m_pSystemList->begin();sysIterator != m_pSystemList->end();sysIterator++)
  {
  CString sysDisplay0 = A2W(sysIterator->second->GetDisplayName());
  CString sysModel0 = A2W(sysIterator->second->GetModel()); //bgapi_gige
  CString sysPathName0 = A2W(sysIterator->second->GetPathName());
  if(sysModel0.Find(_T("gige"))!=-1)    //此例程只连接网口Gige相机
  {
    //m_pSystem = (*m_pSystemList)[sysindex];//将Gige接口赋予m_pSystem
    m_pSystem = sysIterator->second;
    break;
  }
  sysindex++;
  }
  #pragma endregion
  #pragma region //陆续执行Gige接口查询和连接、接口上相机设备的查询和连接、相机设备的数据流的注册和连接、相机设备的信息获取和初始化开启
  if(m_pSystem!=NULL)
  {
  #pragma region//测试获取Gige相机接口的相关参数(供参考)
  CString sysType;CString sysVersion;CString sysName;CString sysID;
  CString sysVendor;CString sysModel;CString sysPathName;CString sysDisplay;      
  //USES_CONVERSION;
  sysName = A2W(m_pSystem->GetFileName());
  sysType = A2W(m_pSystem->GetTLType());
  sysVersion = A2W(m_pSystem->GetVersion());
  sysID = A2W(m_pSystem->GetID());
  sysVendor = A2W(m_pSystem->GetVendor()); //Baumer
  sysModel = A2W(m_pSystem->GetModel());   //bgapi_gige
  sysPathName = A2W(m_pSystem->GetPathName());
  sysDisplay = A2W(m_pSystem->GetDisplayName());
  #pragma endregion
  #pragma region  //System开启后执行查询网口interface操作
  if(!m_pSystem->IsOpen())           //使用IsOpen检查,可以关闭相机后重新打开初始化功能
    m_pSystem->Open();  
  if(m_pSystem->IsOpen())
  {
    m_pInterfaceList = m_pSystem->GetInterfaces();
    m_pInterfaceList->Refresh(100);
    int infcount = m_pInterfaceList->size();     //可以找到多个interface网络接口(包含实际接口和虚拟接口)
    for(InterfaceList::iterator ifIterator = m_pInterfaceList->begin(); ifIterator != m_pInterfaceList->end();ifIterator++)
    {
    CString infType = A2W(ifIterator->second->GetTLType());
    CString infName = A2W(ifIterator->second->GetDisplayName());
    BGAPI2::Interface* m_pInterface0 = ifIterator->second;  
    bool isopen1 = m_pInterface0->IsOpen();
    if(m_pInterface0->IsOpen())              //检查该接口是否已经被占用使用中,被占用则检查下一个
      continue;
    if(!m_pInterface0->IsOpen())    //打开该接口
      m_pInterface0->Open();    
    if(m_pInterface0->IsOpen())     //接口确认开启的情况下检查接口连接的设备数量
    {
      BGAPI2::DeviceList* m_pDeviceList0 = m_pInterface0->GetDevices();
      m_pDeviceList0->Refresh(100); 
      int devcount = m_pDeviceList0->size();//检查接口上是否连接相机设备
      if(devcount>0)
      {
      BGAPI2::Device * m_pDevice0 = (*m_pDeviceList0)[0];
      CString Accessstatus =  A2W(m_pDevice0->GetAccessStatus());//判断相机设备是否可连接
      if(Accessstatus!=_T("RW"))    //"RO"代表被占用,"RW"代表可连接,"U"代表未知情况
        continue;       //若不可连接,则继续检查下一个接口
      m_pInterface = ifIterator->second;//此处确认可连接相机连接接口后跳出循环,若要连接多个相机请自主修改          
      break;
      }
    }
    }
  }
  #pragma endregion
  #pragma region //打开该接口上已存在的连接相机设备:此处只连接一个相机
  if(m_pInterface==NULL)         //若没有可用接口连接相机设备,则返回
    return; 
  CString infName = A2W(m_pInterface->GetDisplayName());//获取接口名称
  if(m_pInterface->IsOpen())               //检查网口是否开启
  {
    m_pDeviceList = m_pInterface->GetDevices();
    m_pDeviceList->Refresh(100);  
    int devcount = m_pDeviceList->size();     //一般为1
    if(devcount>0)
    m_pDevice = (*m_pDeviceList)[0];  
    if(m_pDevice!=NULL)          //检查相机设备是否存在和开启
    if(!m_pDevice->IsOpen())  
      m_pDevice->Open();  
    //为相机对应数据流注册掉线触发事件
    m_pInterface->RegisterPnPEvent(BGAPI2::Events::EVENTMODE_EVENT_HANDLER);        
    m_pInterface->RegisterPnPEventHandler(this, (Events::PnPEventHandler) &PnPEventHandler);
  }
  #pragma endregion
  #pragma region     //打开该接口上已存在的相机设备并进行数据流连接
  if(m_pDevice==NULL)         //若接口上无可用相机设备,则返回
    return;
  //获取相机设备名称
  CString devName = A2W(m_pDevice->GetDisplayName());
  bool DevicealreadyOpen = false;      //相机设备数据流是否已开启
  if(m_pDevice->IsOpen())
  {
    CString infID = A2W(m_pDevice->GetID());        //获取相机设备ID
    m_pDatastreamList = m_pDevice->GetDataStreams();//获取相机设备数据流序列
    m_pDatastreamList->Refresh();
    int iDscount = m_pDatastreamList->size();//一般为1 
    if(iDscount>0)
    m_pDataStream = (*m_pDatastreamList)[0];
    if(m_pDataStream!=NULL)
    {
    if(!m_pDataStream->IsOpen())
      m_pDataStream->Open();
    else
      DevicealreadyOpen =true;
    }
  }
  #pragma endregion
  #pragma region         //设置相机上对应数据流的buffer序列、注册相机回调函数,初始化相机参数设置
  if(m_pDataStream==NULL)          //若相机设备上无可用数据流,则返回
    return;
  if(m_pDataStream->IsOpen()&!DevicealreadyOpen)    //检查相机设备是否被占用或者设备数据流是否开启
  {  
    m_pBufferList = m_pDataStream->GetBufferList();//获取相机设备数据流的Buffer序列
    for(int i=0; i<6; i++)                         //设置的Buffer序列为4组
    {
    m_pBuffer = new BGAPI2::Buffer();
    m_pBufferList->Add(m_pBuffer);
    }
    for (BGAPI2::BufferList::iterator bfIterator = m_pBufferList->begin();bfIterator != m_pBufferList->end();bfIterator++)
    {
    bfIterator->second->QueueBuffer();
    }
    int iQBuffer = m_pBufferList->GetQueuedCount();
    int x=0;
    //为相机对应数据流注册回调函数事件
    m_pDataStream->RegisterNewBufferEvent(BGAPI2::Events::EVENTMODE_EVENT_HANDLER);
    m_pDataStream->RegisterNewBufferEventHandler(this,(Events::NewBufferEventHandler) &BufferHandler);
    //获取当前相机的触发模式的数据
    String strTrgStatus;
    strTrgStatus = m_pDevice->GetRemoteNode("TriggerMode")->GetString();
    CString strTrgStatusC = A2W(strTrgStatus);
    //初始化相机时将触发模式设为Off
    m_pDevice->GetRemoteNode("TriggerMode")->SetString("Off");
    m_pDevice->GetRemoteNodeList()->GetNodePresent("ExposureAuto");
    //开启相机流
    m_pDataStream->StartAcquisitionContinuous();
    //将相机名称显示在对应编辑框内
    m_edtCameraType.SetWindowTextW(devName);
    //将相机的曝光值和增益值显示在界面上
    if (m_pDevice->GetRemoteNodeList()->GetNodePresent("ExposureTime")) 
    {
    long iShutter;CString strShutter;
    float fGain;CString strGain;
    iShutter = (long)m_pDevice->GetRemoteNode("ExposureTime")->GetDouble();
    strShutter.Format(_T("%ld"),iShutter);
    fGain = (float)m_pDevice->GetRemoteNode("Gain")->GetDouble();
    strGain.Format(_T("%.2f"),fGain);
    //将相机曝光值显示界面上
    m_edtShutter.SetWindowTextW(strShutter);
    //将相机增益值显示界面上
    m_edtGain.SetWindowText(strGain);
    }      
    //Enable the button_inialize
    GetDlgItem(ID_BTNINITIALIZE)->EnableWindow(FALSE);
    //AfxMessageBox(devName+_T("已连接"));
  }
  #pragma endregion
  }
  else
  {
  AfxMessageBox(_T("未找到可用的相机接口"));
  }
  #pragma endregion                          
}


相机连接代码如下所示:


void CGigeDemoDlg::OnBnClickedBtnplay()
{
  // TODO: 在此添加控件通知处理程序代码
  USES_CONVERSION;
  if(m_pDevice != NULL)
  {
  try
  {
    m_pDevice->GetRemoteNode("TriggerMode")->SetString("Off"); //关闭触发模式,进入自由采集图片流模式
    m_pDevice->GetRemoteNode("AcquisitionStart")->Execute();
    #pragma region 线程显示帧率和网口数据通量(做参考)
    m_bRun0 = true;
    //AfxBeginThread(ShowFrame_hThread1, (void*)this);    
    #pragma endregion 
  }
  catch (BGAPI2::Exceptions::IException& ex)
  {
    CString str1;
    str1.Format(_T("ExceptionType:%s! ErrorDescription:%s in function:%s"),ex.GetType(),ex.GetErrorDescription(),ex.GetFunctionName());
    MessageBox(str1);
  }
  }
}

6、完成上面操作后,就可以及时对掉线相机进行断线重连操作,可以尝试重新连接,也可以检查对应相机状态,需要对应掉线事件操作什么,取决于开发者想要什么的功能实现。


结论要点

工业相机的重连机制依托于相机的PnPEvent事件触发,并且出现PnPEvent事件后可以触发一个间隔1-2s的线程,反复刷新对应网口interface的相机,找到相机后,然后线程终止,然后再触发一个重新连接的功能或者方法。


PnPEvent事件类型有移除和增加两种类型,可以针对两种类型做相应的自定义处理。


事件类型如下图所示:


4df302c8fee24d4dbde744c9051f5e5e.png


并且在刷新工业相机List的过程中可以根据类的构成和习惯有所不同,比如锁定好接口,方便快速只刷新这个接口,从而适用于多相机的系统。


注意

工业相机的重连功能基本上是属于软件层面的重新连接,若是涉及到硬件层面的变更和重连则需要更为严格的对象设计和逻辑优化。

目录
相关文章
|
6月前
|
数据采集 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Force IP强制修改网口IP功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用Force IP强制修改网口IP功能(C++)
61 0
|
3月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
245 0
|
6月前
|
监控 API 开发工具
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取每张图像的微秒时间和FrameID功能(C++)
Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取每张图像的微秒时间和FrameID功能(C++)
75 0
|
1天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
14 2
|
7天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
33 5
|
14天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
46 4
|
15天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
43 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1