Commit 43f161b7 authored by d11's avatar d11
Browse files

no

parent 31c894b5
#ifndef _BUFFER_INFORMATION_
#define _BUFFER_INFORMATION_
#include <thread>
#include <vector>
#include <mutex>
#include <list>
#define _GNULINUX
#include <bgapi2_genicam/bgapi2_genicam.hpp>
// structure which holds additional information
// transferred together with the camera buffer
// this structure should be adapted to needs of the application
struct BufferInformation{
BufferInformation() {
frameid = 0;
number_of_incomplete_images = 0;
resend_requests = 0;
resend_requests_single = 0;
resend_requests_range = 0;
}
// frame id of the camera buffer
bo_uint64 frameid;
// statistic value, number of incomplete images
bo_uint64 number_of_incomplete_images;
// statistic value, number of resend requests
bo_int64 resend_requests;
// statistic value, number resend requests requesting a single data packet
bo_int64 resend_requests_single;
// statistic value, number of resend requests requesting a bunch of data packet
bo_int64 resend_requests_range;
};
#endif //_BUFFER_INFORMATION_
#include "../../../../../NomadSpecialModules/src/drivers/gigecam/photonics/CameraHandler.h"
#include <iostream>
#include <sstream>
#include <limits>
#include "../../../../../NomadSpecialModules/src/drivers/gigecam/photonics/BufferInformation.h"
#include "../../../../../NomadSpecialModules/src/drivers/gigecam/photonics/DoubleBufferHandler.h"
// this function initializes the Baumer GAPI SDK and
// searches for connected cameras
// all found cameras will be opened to work with
void InitThreadRoutine(CCameraHandler* camera_handler);
// this function handles the image capture command
// every camera has its own capture thread
void CaptureThreadRoutine(CCameraHandler* camera_handler, CCameraControl * camera_control);
// this function handles the feature command (reading and writing features)
// this example shows only read and write to the ExposureTime feature
// every camera has its own feature thread
void FeatureThreadRoutine(CCameraHandler* camera_handler, CCameraControl * camera_control);
CCameraHandler::CCameraHandler()
: finish_worker_threads_(false)
, init_ok_(true)
{
}
CCameraHandler::~CCameraHandler()
{
}
// this function starts the initialisation thread
// and blocks the execution until the thread is finished
void CCameraHandler::Start(CDoubleBufferHandler buffer_handler) {
buffer_handler_ = buffer_handler;
init_thread = std::thread(InitThreadRoutine, this);
init_thread.join();
}
// DeinitializeBGAPI is processed in the thread of the caller
// it is also possible to call DeinitializeBGAPI in a separate thread
void CCameraHandler::Stop() {
DeinitializeBGAPI();
camera_control_.clear();
init_ok_ = true;
}
size_t CCameraHandler::NumberOfCameras() {
return camera_control_.size();
}
// initialize BGAPI buffer management is not performed in an extra thread
void CCameraHandler::StartCameras() {
finish_worker_threads_ = false;
for (std::vector<CCameraControl*>::iterator camera_iter = camera_control_.begin();
camera_iter != camera_control_.end();
camera_iter++) {
(*camera_iter)->InitializeBGAPIBufferManagement();
// start the worker threads
(*camera_iter)->capture_thread_ = std::thread(CaptureThreadRoutine, this, (*camera_iter));
(*camera_iter)->feature_thread_ = std::thread(FeatureThreadRoutine, this, (*camera_iter));
}
}
// this function informs all worker threads to exit themselves and waits with join() until the threads are finished
// if the thread is already finished before join() is called, the join() function doesn't wait
// and return immediately
// unlike initialization, deinitialization is not performed in an extra thread
void CCameraHandler::StopCameras() {
// inform all worker threads to exit themselves
finish_worker_threads_ = true;
for (std::vector<CCameraControl*>::iterator camera_iter = camera_control_.begin();
camera_iter != camera_control_.end();
camera_iter++) {
// wait until the thread loop was left
(*camera_iter)->capture_thread_.join();
(*camera_iter)->feature_thread_.join();
(*camera_iter)->DeinitializeBGAPIBufferManagement();
}
}
// this function is called from the initialisation thread
void CCameraHandler::AddCamera(BGAPI2::Device* camera_pointer) {
CCameraControl * camera_control_obj = new CCameraControl();
camera_control_obj->camera_pointer_ = camera_pointer;
camera_control_obj->number_of_captured_images_ = 0;
camera_control_obj->number_of_incomplete_images_ = 0;
camera_control_obj->buffer_handler_ = buffer_handler_;
camera_control_.push_back(camera_control_obj);
}
// to make sure that this function returns 0 if no image was captured
// the variable (*camera_iter)->number_of_captured_images_ will be reset to 0
// when the capture command starts (StartStreamingBGAPI)
// and if no camera is connected an additional check is included to set number_of_captured_images to 0
// to calculate the variable number_of_captured_images there is no need for a mutex,
// because this function performs only reads on (*camera_iter)->number_of_captured_images_
unsigned int CCameraHandler::CapturedImages() {
unsigned int number_of_captured_images = std::numeric_limits<unsigned int>::max();
for (std::vector<CCameraControl*>::iterator camera_iter = camera_control_.begin();
camera_iter != camera_control_.end();
camera_iter++) {
unsigned int current_device_images = (*camera_iter)->number_of_captured_images_;
number_of_captured_images = current_device_images < number_of_captured_images ?
current_device_images : number_of_captured_images;
}
if (number_of_captured_images == std::numeric_limits<unsigned int>::max()) {
number_of_captured_images = 0;
}
return number_of_captured_images;
}
void CCameraHandler::SetInitError() {
init_ok_ = false;
}
bool CCameraHandler::IsInitOK() {
return init_ok_;
}
//this function protects the logging_list_ against parallel access from function LoggingString
void CCameraHandler::LoggingStringAdd(std::string logging_message) {
std::lock_guard<std::mutex> lock(logging_list_lock_);
logging_list_.push_back(logging_message);
}
//this function protects the logging_list_ against parallel access from function LoggingStringAdd
std::string CCameraHandler::LoggingString() {
std::lock_guard<std::mutex> lock(logging_list_lock_);
std::string tmp_string;
if (logging_list_.size() > 0) {
tmp_string = logging_list_.front();
logging_list_.pop_front();
}
return tmp_string;
}
// the function loads only one GenTL producer of type GEV and U3V at a time
// function has some special debug code when working with GigE cameras
// the heartbeat will be enabled disabled to allow debugging with losing the camera
void CCameraHandler::InitializeBGAPI() {
bool device_found = false;
bool gev_producer_found = false;
bool u3v_producer_found = false;
try
{
// get system list (list of GenTL producer)
// and refresh the list
BGAPI2::SystemList *system_list = BGAPI2::SystemList::GetInstance();
system_list->Refresh();
if (system_list->size() > 0) {
LoggingStringAdd("GenTL producers loaded.");
} else {
LoggingStringAdd("No GenTL producers loaded.");
}
// use only the first system
// remove the break keyword at the end of the loop to allow all systems
for (BGAPI2::SystemList::iterator sys_iter = system_list->begin(); sys_iter != system_list->end(); sys_iter++)
{
BGAPI2::System *system_pointer = sys_iter->second;
BGAPI2::String tl_type = system_pointer->GetTLType();
if (tl_type == "GEV" && !gev_producer_found ||
tl_type == "U3V" && !u3v_producer_found) {
if (tl_type == "GEV") {
gev_producer_found = true;
}
if (tl_type == "U3V") {
u3v_producer_found = true;
}
LoggingStringAdd(" GenTL producer " + std::string(system_pointer->GetPathName()) + " found");
LoggingStringAdd(" GenTL producer version is " + std::string(system_pointer->GetVersion()));
system_pointer->Open();
// get and refresh the interface list of the current system
BGAPI2::InterfaceList *interface_list = system_pointer->GetInterfaces();
interface_list->Refresh(100);
// iterate over all interfaces
for (BGAPI2::InterfaceList::iterator ifc_iter = interface_list->begin(); ifc_iter != interface_list->end(); ifc_iter++)
{
// open the interface to work with
ifc_iter->second->Open();
// get and refresh the device list of the current interface
BGAPI2::DeviceList *device_list = ifc_iter->second->GetDevices();
device_list->Refresh(100);
if (device_list->size() > 0)
{
// iterate and open all devices within the device list
for (BGAPI2::DeviceList::iterator dev_iter = device_list->begin(); dev_iter != device_list->end(); dev_iter++)
{
BGAPI2::Device * camera_pointer = dev_iter->second;
try {
camera_pointer->Open();
#ifdef _DEBUG
if (camera_pointer->GetRemoteNodeList()->GetNodePresent("DeviceLinkHeartbeatMode")) {
camera_pointer->GetRemoteNode("DeviceLinkHeartbeatMode")->SetValue("Off");
}
#endif
// add the device pointer to the finder class
AddCamera(camera_pointer);
device_found = true;
LoggingStringAdd(" Device " + std::string(camera_pointer->GetModel()) + " found");
}
catch (BGAPI2::Exceptions::AccessDeniedException& /*ex*/) {
LoggingStringAdd(" Device " + std::string(camera_pointer->GetModel()) +
" skipped. No access. Maybe the device is used by an other application.");
}
catch (BGAPI2::Exceptions::LowLevelException& /*ex*/) {
LoggingStringAdd(" Device " + std::string(camera_pointer->GetModel()) +
" skipped. No access. Maybe the device is used by an other application.");
}
}
}
else
{
ifc_iter->second->Close();
}
}
}
else {
LoggingStringAdd(" Skipped GenTL producer " + std::string(system_pointer->GetPathName()));
}
}
if (!device_found) {
LoggingStringAdd("No device found");
}
}
catch (BGAPI2::Exceptions::IException& ex)
{
// something goes wrong, thread finished unexpected
LoggingStringAdd("Init thread finished unexpected!");
std::stringstream strstream;
strstream << "Error in function: " << ex.GetFunctionName() << std::endl <<
"Error description: " << ex.GetErrorDescription() << "Type: " << ex.GetType();
LoggingStringAdd(strstream.str());
BGAPI2::SystemList::ReleaseInstance();
SetInitError();
}
}
// this function has some special debug code when working with GigE cameras
// the heartbeat mode will be enabled again
// at the end of the function only ReleaseInstance is called
// all the other functions to deinitialise the BGAPI (BGAPI2::System::Close, BGAPI2::Interface::Close)
// will be called automatically by ReleaseInstance
// the called function BGAPI2::Device::Close is also optional
void CCameraHandler::DeinitializeBGAPI() {
for (std::vector<CCameraControl*>::iterator camera_iter = camera_control_.begin();
camera_iter != camera_control_.end();
camera_iter++) {
#ifdef _DEBUG
if ((*camera_iter)->camera_pointer_->GetRemoteNodeList()->GetNodePresent("DeviceLinkHeartbeatMode")) {
(*camera_iter)->camera_pointer_->GetRemoteNode("DeviceLinkHeartbeatMode")->SetValue("On");
}
#endif
(*camera_iter)->camera_pointer_->Close();
delete (*camera_iter);
}
BGAPI2::SystemList::ReleaseInstance();
}
// this function uses a mutex to protect message list for parallel access
bool CCameraControl::ExposureFeatureBGAPI() {
BGAPI2::String device_name = camera_pointer_->GetModel();
try {
BGAPI2::Node* exposure_node = camera_pointer_->GetRemoteNode("ExposureTimeRaw");
// check if the feature is writeable and write new value
if (exposure_node->IsWriteable()) {
exposure_node->SetDouble(exposure_time_);
}
// write some status information
std::lock_guard<std::mutex> lock(feature_command_message_list_lock_);
feature_command_message_list_.clear();
std::stringstream strstream;
strstream << "ExposureTime set to: " << exposure_node->GetDouble();
feature_command_message_list_.push_back(strstream.str());
return true;
}
catch (BGAPI2::Exceptions::IException& ex)
{
LoggingStringAdd("Command thread finished unexpected for camera " + std::string(device_name) + "!");
std::stringstream strstream;
strstream << "Error in function: " << ex.GetFunctionName() << std::endl
<< "Error description: " << ex.GetErrorDescription() << std::endl;
LoggingStringAdd(strstream.str());
return false;
}
}
// the mutex protects the command_feature_ flag
// against parallel access from function CommandExposure
void CCameraControl::StartFeatureCommandExposure(double exposure_time) {
std::lock_guard<std::mutex> lock(feature_command_lock_);
exposure_time_ = exposure_time;
ExposureFeatureBGAPI();
// command_feature_ = true;
}
// this function works like a auto reset and deactivates the exposure command immediately
// the mutex protects the command_feature_ flag
// against parallel access from function StartFeatureCommandExposure
bool CCameraControl::IsCommandExposureActivated() {
std::lock_guard<std::mutex> lock(feature_command_lock_);
if (command_feature_) {
command_feature_ = false;
return true;
}
else {
return false;
}
}
// the mutex protects the command_capture_ flag
// against parallel access from function CommandCapture
void CCameraControl::StartCaptureCommand(unsigned int images) {
std::lock_guard<std::mutex> lock(capture_command_lock_);
number_of_images_ = images;
command_capture_ = true;
}
// this function works like a auto reset and deactivates the capture command immediately
// the mutex protects the command_capture_ flag
// against parallel access from function StartCaptureCommand
bool CCameraControl::IsCommandCaptureActivated() {
std::lock_guard<std::mutex> lock(capture_command_lock_);
if (command_capture_) {
command_capture_ = false;
return true;
}
else {
return false;
}
}
// this function opens always the first data stream,
// other possible data streams will be ignored,
// because they are not relevant
// this function creates the camera buffer objects and passes a new instance of BufferInformation structure,
// the camera buffers and BufferInformation structures will be deleted in function DeinitializeBGAPIBufferManagement
void CCameraControl::InitializeBGAPIBufferManagement() {
BGAPI2::DataStream *datastream_pointer = nullptr;
// at first a refresh of data stream is needed to fill the stream list
camera_pointer_->GetDataStreams()->Refresh();
// set buffer handler to every device
buffer_handler_ = buffer_handler_; // @suppress("Assignment to itself")
BGAPI2::DataStreamList *datastream_list = camera_pointer_->GetDataStreams();
datastream_list->Refresh();
datastream_pointer = datastream_list->begin()->second;
datastream_pointer->Open();
BGAPI2::BufferList *buffer_list = datastream_pointer->GetBufferList();
BGAPI2::Buffer *buffer = nullptr;
for (int i = 0; i < 10; i++) {
// create the buffer object and pass a new instance of BufferInformation,
// which holds additional buffer information
buffer = new BGAPI2::Buffer(new BufferInformation());
// announce the camera buffer to the data streams buffer list
// at this point the camera buffer will be allocated
buffer_list->Add(buffer);
// move the camera buffer into the input queue
// this makes the buffer available for image capture
buffer->QueueBuffer();
}
}
// the BufferInformation objects and the camera buffers created in StartCameras will be deleted in this function
void CCameraControl::DeinitializeBGAPIBufferManagement()
{
BGAPI2::DataStream *datastream_pointer = camera_pointer_->GetDataStreams()->begin()->second;
BGAPI2::BufferList *buffer_list = datastream_pointer->GetBufferList();
// returns the buffer to the user application
buffer_list->DiscardAllBuffers();
// revoke and delete the image buffers
while (buffer_list->size() > 0) {
BGAPI2::Buffer* buffer = buffer_list->begin()->second;
// delete buffer information
delete reinterpret_cast<BufferInformation*>(buffer->GetUserObj());
// revoke the buffer from the buffer list
buffer_list->RevokeBuffer(buffer);
// delete the buffer itself
delete buffer;
}
// close the data stream
datastream_pointer->Close();
}
// this function starts the data stream on host and on camera
// and implements a loop to capture the desired number of images
// it also exchange the received buffer with the buffer handler
bool CCameraControl::CaptureBGAPIImages(const bool * abort_flag, unsigned int number_of_images) {
bool return_flag = true;
BGAPI2::String camera_name = camera_pointer_->GetModel();
try {
number_of_images_ = number_of_images;
return_flag = StartStreamingBGAPI();
if (return_flag) {
// we assume the camera supports at least one data stream
BGAPI2::DataStream* datastream_pointer = camera_pointer_->GetDataStreams()->begin()->second;
// loop over the number of images to capture
do {
// fetch the buffers
BGAPI2::Buffer* buffer = datastream_pointer->GetFilledBuffer(exposure_time_ + 1000);
if (buffer) {
std::cout <<"GetFilledBuffer OK "<< std::endl;
FillBufferInformation(datastream_pointer, buffer);
number_of_captured_images_++;
buffer_handler_.PushBuffer(buffer);
}
// break the thread also at this point,
// to avoid long shut down times in case you capture a huge number of images
if (*abort_flag) {
return_flag = false;
}
} while (number_of_captured_images_ < number_of_images_ && return_flag);
}
}
catch (BGAPI2::Exceptions::IException& ex)
{
LoggingStringAdd("streaming thread finished unexpected for camera " + std::string(camera_name) + "!");
std::stringstream strstream;
strstream << "error in function: " << ex.GetFunctionName() << std::endl
<< "error description: " << ex.GetErrorDescription() << std::endl;
LoggingStringAdd(strstream.str());
return_flag = false;
}
try {
// stop acquisition on host and device side
StopStreamingBGAPI();
}
catch (BGAPI2::Exceptions::IException& ex)
{
LoggingStringAdd("stop streaming failed" + std::string(camera_name) + "!");
std::stringstream strstream;
strstream << "error in function: " << ex.GetFunctionName() << std::endl
<< "error description: " << ex.GetErrorDescription() << std::endl;
LoggingStringAdd(strstream.str());
return_flag = false;
}
return return_flag;
}
// this function starts the streaming on host and on camera
// this function may throw an exception, the exception will be caught by the caller
bool CCameraControl::StartStreamingBGAPI() {
BGAPI2::String camera_name = camera_pointer_->GetModel();
// we assume the camera supports at least one data stream
BGAPI2::DataStream* datastream_pointer = camera_pointer_->GetDataStreams()->begin()->second;
// initialize image counter
number_of_captured_images_ = 0;
// initialize the buffer mode object
buffer_handler_.Init();
// at first make sure the buffer list is ok -> all buffers has to be in the input queue
datastream_pointer->GetBufferList()->DiscardAllBuffers();
datastream_pointer->GetBufferList()->FlushAllToInputQueue();
// start the acquisition on the host side with the selected number of images
datastream_pointer->StartAcquisition(number_of_images_);
// start the acquisition on the device side
// to work with this example the device must be in free running mode
if (camera_pointer_->GetRemoteNode("AcquisitionStart")->IsWriteable()) {
camera_pointer_->GetRemoteNode("AcquisitionStart")->Execute();
capture_active_ = true;
return true;
}
else {
LoggingStringAdd(
"Streaming Acquisition not started for camera " +
std::string(camera_name) +
"! The feature AcquisitionStart is not writeable");
return false;
}
}
// this function may throw an exception, the exception will be caught by the caller
void CCameraControl::StopStreamingBGAPI() {
BGAPI2::DataStream* datastream_pointer = camera_pointer_->GetDataStreams()->begin()->second;
capture_active_ = false;
camera_pointer_->GetRemoteNode("AcquisitionStop")->Execute();
datastream_pointer->StopAcquisition();
}
// this function may throw an exception, the exception will be caught by the caller
void CCameraControl::FillBufferInformation( BGAPI2::DataStream* datastream_pointer, BGAPI2::Buffer *buffer) {
BufferInformation * buffer_information = reinterpret_cast<BufferInformation*>(buffer->GetUserObj());
if (buffer->GetIsIncomplete() == false) {
buffer_information->frameid = buffer->GetFrameID();
}
else {
number_of_incomplete_images_++;
buffer_information->number_of_incomplete_images = number_of_incomplete_images_;
}
if (datastream_pointer->GetNodeList()->GetNodePresent("ResendRequests") == true) {
if (datastream_pointer->GetNode("ResendRequests")->IsReadable()) {
buffer_information->resend_requests = datastream_pointer->GetNode("ResendRequests")->GetInt();
}
}
if (datastream_pointer->GetNodeList()->GetNodePresent("PacketResendRequestSingle") == true) {
if (datastream_pointer->GetNode("PacketResendRequestSingle")->IsReadable()) {
buffer_information->resend_requests_single =
datastream_pointer->GetNode("PacketResendRequestSingle")->GetInt();
}
}
if (datastream_pointer->GetNodeList()->GetNodePresent("PacketResendRequestRange") == true) {
if (datastream_pointer->GetNode("PacketResendRequestRange")->IsReadable()) {
buffer_information->resend_requests_range =
datastream_pointer->GetNode("PacketResendRequestRange")->GetInt();
}
}
}
//this function protects the logging_list_ against parallel access from function LoggingString
void CCameraControl::LoggingStringAdd(std::string logging_message) {
std::lock_guard<std::mutex> lock(logging_list_lock_);
logging_list_.push_back(logging_message);
}
//this function protects the logging_list_ against parallel access from function LoggingStringAdd
std::string CCameraControl::LoggingString() {
std::lock_guard<std::mutex> lock(logging_list_lock_);
std::string tmp_string;
if (logging_list_.size() > 0) {
tmp_string = logging_list_.front();
logging_list_.pop_front();
}
return tmp_string;
}
// this thread routine just calls one singe function without loops
// after the function InitializeBGAPI has been processed the initialisation thread ends
void InitThreadRoutine(CCameraHandler* camera_handler) {
camera_handler->InitializeBGAPI();
}
// the main loop of the capture thread, leaving this loop means finishing the thread
// wait with the join() function in the main thread (StopCameras) until this loop was left
// the thread also finished, if the capture command fails
// the thread waits in idle state (sleep 10 msec), if the capture command is not active
void CaptureThreadRoutine(CCameraHandler* camera_handler, CCameraControl * camera_control) {
while (!(*camera_handler->FinishWorkerThreads())) {
// check if the user a activated the capture command
if (camera_control->IsCommandCaptureActivated()) {
// function CaptureBGAPIImages blocks until the
// specified number of images (camera_control->number_of_images_) was captured
if (!camera_control->CaptureBGAPIImages(camera_handler->FinishWorkerThreads(), camera_control->number_of_images_)) {
while (!camera_control->LoggingListEmpty()) {
camera_handler->LoggingStringAdd(camera_control->LoggingString());
};
break;
}
} else {
// just wait until capture command become active
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
// the main loop of the feature thread, leaving this loop means finishing the thread
// wait with the join() function in the main thread (StopCameras) until this loop was left
// the thread also finished, if the feature command fails
// the thread waits in idle state (sleep 10 msec), if the feature command is not active
void FeatureThreadRoutine(CCameraHandler* camera_handler, CCameraControl * camera_control) {