Basler相机掉线重连(c++版)

Posted by BY Blog on November 26, 2019

Basler相机掉线重连(C++版)

​ Basler相机在使用过程中,会由于网络、断电或者其它因素出现掉线问题,这时候添加重连机制是十分必要的,能够确保相机在恢复正常后能重新连接。以下为记录相机重连步骤。

一、更改相机默认心跳

​ Basler相机的默认心跳频率为 五分钟,也就是说相机SDK在五分钟内没有接收到相机的反馈,将会判断相机处于掉线状态。很显然,在实际开发过程中,数据的处理需要很高的实时性,我们需要在短时间内知道相机是否掉线,所以这里将修改相机的心跳频率,设置为1000ms,相机在一秒钟内没有发送心跳连接则默认相机已掉线。以下为官方提供接口函数。

// 类实现
// Simple helper class to set the HeartbeatTimeout safely.
class CHeartbeatHelper
{
public:
	explicit CHeartbeatHelper(CInstantCamera& camera)
		: m_pHeartbeatTimeout(NULL)
	{
		// m_pHeartbeatTimeout may be NULL
		m_pHeartbeatTimeout = camera.GetTLNodeMap().GetNode("HeartbeatTimeout");
	}

	bool SetValue(int64_t NewValue)
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;
	
		// Apply the increment and cut off invalid values if neccessary.
		int64_t correctedValue = NewValue - (NewValue % m_pHeartbeatTimeout->GetInc());
		m_pHeartbeatTimeout->SetValue(correctedValue);
		cout << "设置相机心跳成功--CHeartbeat:"<< NewValue << std::endl;
		return true;
	}
	
	bool SetMax()
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;
	
		int64_t maxVal = m_pHeartbeatTimeout->GetMax();
		return SetValue(maxVal);
	}

protected:
	GenApi::CIntegerPtr m_pHeartbeatTimeout; 
};
// 调用实现
camera.Open();
CHeartbeatHelper heartbeatHelper(camera);
heartbeatHelper.SetValue(1000);

值得注意的是,设置心跳需要在相机打开后。

二、设置掉线回调函数

掉线回调函数的设置有助于我们检测到相机什么时候处于掉线模式下。对应实现代码为:

// 类实现
class CSampleConfigurationEventHandler : public Pylon::CConfigurationEventHandler
{
public:
	void OnCameraDeviceRemoved(CInstantCamera& /*camera*/)
	{
		cout << "相机已掉线。" << std::endl;
	}
};
// For demonstration purposes only, register another configuration event handler that handles device removal.
	camera.RegisterConfiguration(new CSampleConfigurationEventHandler, RegistrationMode_Append, Cleanup_Delete);

三、开启子线程检测重连

相机在出于某种特殊情况掉线,但为确保相机能在恢复正常使用的情况下程序能实现自动重连,需要添加子线程检测相机是否恢复状态,并进行连接。

camera.IsCameraDeviceRemoved()

函数返回值能判断是否处于连接状态。下面代码为实现检测上一部连接的相机是否在线,若在线,则重新连接并打开图像抓取。

if (camera.IsCameraDeviceRemoved()) {
		CTlFactory& tlFactory = CTlFactory::GetInstance();
		CDeviceInfo info;
		// Remember the camera properties that allow detecting the same camera again.
		info.SetDeviceClass(camera.GetDeviceInfo().GetDeviceClass());
		info.SetSerialNumber(camera.GetDeviceInfo().GetSerialNumber());

		// Create a filter containing the CDeviceInfo object info which describes the properties of the device we are looking for.
		DeviceInfoList_t filter;
		filter.push_back(info);
		DeviceInfoList_t devices;
		cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<" << endl;
		if (tlFactory.EnumerateDevices(devices, filter) > 0)
		{
			cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
			// Destroy the Pylon Device representing the detached camera device.
			// It cannot be used anymore.
			camera.DestroyDevice();
			// The camera has been found. Create and attach it to the Instant Camera object.
			camera.Attach(tlFactory.CreateDevice(devices[0]));
			camera.Open();
			CHeartbeatHelper heartbeatHelper(camera);
			//Reset heartbeat 1000
			heartbeatHelper.SetValue(1000);
			//StartGrabing();
		}
	}

总结

相机自动重连的主要步骤在于心跳的设置和子线程循环检测重连的机制。实现逻辑为检测到相机掉线后执行检测重连函数。

// Camera.cpp
#include "Camera.h"
#include <stdexcept>
#include <pylon/PylonIncludes.h>


#ifdef PYLON_WIN_BUILD
#include <pylon/PylonGUI.h>    
#endif
using namespace Pylon;
using namespace std;
using namespace GenApi;
using namespace std;

String_t ID_t;

MyCamera::MyCamera() {
	
}
MyCamera::~MyCamera() {
	Dispose();
}
void MyCamera::OpenCamera() {
	//Pylon::PylonAutoInitTerm autoInitTerm;
	PylonInitialize();
	//camera = new CInstantCamera();//CTlFactory::GetInstance().CreateFirstDevice()
	camera.Attach(CTlFactory::GetInstance().CreateFirstDevice());

	cout << camera.GetDeviceInfo().GetFullName() << endl;
	camera.RegisterImageEventHandler(new CImageEventPrinter, RegistrationMode_Append, Cleanup_Delete);
	// For demonstration purposes only, register another configuration event handler that handles device removal.
	camera.RegisterConfiguration(new CSampleConfigurationEventHandler, RegistrationMode_Append, Cleanup_Delete);
	camera.Open();
	CHeartbeatHelper heartbeatHelper(camera);
	heartbeatHelper.SetValue(1000);
}
void MyCamera::OpenCameraByID(String_t ID) {
	ID_t = ID;
	PylonInitialize();
	CDeviceInfo cInfo;
	cInfo.SetSerialNumber(ID);
	camera.Attach(CTlFactory::GetInstance().CreateDevice(cInfo));
	cout << camera.GetDeviceInfo().GetFullName() << endl;
	camera.RegisterImageEventHandler(new CImageEventPrinter, RegistrationMode_Append, Cleanup_Delete);
	// For demonstration purposes only, register another configuration event handler that handles device removal.
	camera.RegisterConfiguration(new CSampleConfigurationEventHandler, RegistrationMode_Append, Cleanup_Delete);
	camera.Open();
	CHeartbeatHelper heartbeatHelper(camera);
	heartbeatHelper.SetValue(1000);
}
void MyCamera::CloseCamera() {
	camera.Close();
}
void MyCamera::StartGrabing() {
	camera.StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera);
	camera.ExecuteSoftwareTrigger();
	// 打印相机的名称
	std::cout << "Using device 11" << camera.GetDeviceInfo().GetModelName() << endl;
	//获取相机节点映射以获得相机参数
	
}
void MyCamera::StopGrabing() {
	camera.StopGrabbing();
}

void MyCamera::Dispose() {
	camera.DestroyDevice();
}

bool MyCamera::SetPixelForamt(char* PixelForamt) {
	GenApi::INodeMap& nodemap = camera.GetNodeMap();
	// Set the pixel data format.
	CEnumerationPtr(nodemap.GetNode("PixelFormat"))->FromString(PixelForamt);
	return true;
}

bool MyCamera::SetROI(int W, int H, int X, int Y) {
	GenApi::INodeMap& nodemap = camera.GetNodeMap();
	//获取相机成像宽度和高度
	const CIntegerPtr width = nodemap.GetNode("Width");
	const CIntegerPtr height = nodemap.GetNode("Height");
	const CIntegerPtr offsetX = nodemap.GetNode("OffsetX");
	const CIntegerPtr offsetY = nodemap.GetNode("OffsetY");
	width->SetValue(W);
	height->SetValue(H);
	offsetX->SetValue(X);
	offsetY->SetValue(Y);
	cout << W << H << X << Y << endl;
	return true;
}
void MyCamera::setConvertImageHandler(ConvertImageHandler handler) {
	m_hanlder = handler;
}

void MyCamera::DetectCamera() {

	if (camera.IsCameraDeviceRemoved()) {
		CTlFactory& tlFactory = CTlFactory::GetInstance();
		CDeviceInfo info;
		// Remember the camera properties that allow detecting the same camera again.
		info.SetDeviceClass(camera.GetDeviceInfo().GetDeviceClass());
		info.SetSerialNumber(camera.GetDeviceInfo().GetSerialNumber());

		// Create a filter containing the CDeviceInfo object info which describes the properties of the device we are looking for.
		DeviceInfoList_t filter;
		filter.push_back(info);
		DeviceInfoList_t devices;
		cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<" << endl;
		if (tlFactory.EnumerateDevices(devices, filter) > 0)
		{
			cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
			// Destroy the Pylon Device representing the detached camera device.
			// It cannot be used anymore.
			camera.DestroyDevice();
			// The camera has been found. Create and attach it to the Instant Camera object.
			camera.Attach(tlFactory.CreateDevice(devices[0]));
			camera.Open();
			CHeartbeatHelper heartbeatHelper(camera);
			//Reset heartbeat 1000
			heartbeatHelper.SetValue(1000);
			StartGrabing();
		}
	}

}

// Camera.h
#pragma once
#include <stdexcept>
#include <pylon/PylonIncludes.h>

#ifdef PYLON_WIN_BUILD
#include <pylon/PylonGUI.h>    
#endif
using namespace Pylon;
using namespace std;
using namespace GenApi;
//定义抓取的图像数
static const uint32_t c_countOfImagesToGrab = 10;
using namespace std;
typedef void (*ConvertImageHandler)(uint8_t* cameraData);
static ConvertImageHandler m_hanlder;
class CImageEventPrinter : public CImageEventHandler
{
public:
	virtual void OnImageGrabbed(CInstantCamera& camera, const CGrabResultPtr& ptrGrabResult)
	{
		if (ptrGrabResult->GrabSucceeded())
		{
			uint8_t* pImageBuffer = (uint8_t*)ptrGrabResult->GetBuffer();
			m_hanlder(pImageBuffer);
			//std::cout << "SizeX: " << ptrGrabResult->GetWidth() << std::endl;
			//std::cout << "SizeY: " << ptrGrabResult->GetHeight() << std::endl;
			//const uint8_t* pImageBuffer = (uint8_t*)ptrGrabResult->GetBuffer();
			//std::cout << "Gray value of first pixel: " << (uint32_t)pImageBuffer[0] << std::endl;
			//std::cout << std::endl;
		}
		else
		{
			std::cout << "Error: " << ptrGrabResult->GetErrorCode() << " " << ptrGrabResult->GetErrorDescription() << std::endl;
		}
	}
};
// Simple helper class to set the HeartbeatTimeout safely.
class CHeartbeatHelper
{
public:
	explicit CHeartbeatHelper(CInstantCamera& camera)
		: m_pHeartbeatTimeout(NULL)
	{
		// m_pHeartbeatTimeout may be NULL
		m_pHeartbeatTimeout = camera.GetTLNodeMap().GetNode("HeartbeatTimeout");
	}

	bool SetValue(int64_t NewValue)
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;

		// Apply the increment and cut off invalid values if neccessary.
		int64_t correctedValue = NewValue - (NewValue % m_pHeartbeatTimeout->GetInc());
		m_pHeartbeatTimeout->SetValue(correctedValue);
		cout << "设置相机心跳成功--CHeartbeat:"<< NewValue << std::endl;
		return true;
	}

	bool SetMax()
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;

		int64_t maxVal = m_pHeartbeatTimeout->GetMax();
		return SetValue(maxVal);
	}

protected:
	GenApi::CIntegerPtr m_pHeartbeatTimeout; 
};

class CSampleConfigurationEventHandler : public Pylon::CConfigurationEventHandler
{
public:
	void OnCameraDeviceRemoved(CInstantCamera& /*camera*/)
	{
		cout << "相机已掉线。" << std::endl;
	}
};
class MyCamera
{
public:
	//构造函数
	MyCamera();
	//析构函数
	~MyCamera();
	void OpenCamera();
	void OpenCameraByID(String_t ID); 
	void CloseCamera();
	void StartGrabing();
	void StopGrabing();
	void Dispose();
	void DetectCamera();
	bool SetPixelForamt(char* PixelForamt);
	bool SetROI(int W, int H, int X, int Y);
	void setConvertImageHandler(ConvertImageHandler handler);
private:
	CInstantCamera camera;
	
};