CameraHandler.cpp 25.6 KB
Newer Older
d11's avatar
no    
d11 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
#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) {
    // the main loop of the thread, leaving this loop means finishing the thread
    // wait with the join function in the main thread until this loop was left
    while (!(*camera_handler->FinishWorkerThreads())) {
        // check if the user activated the exposure command
        if (camera_control->IsCommandExposureActivated()) {
            if (!camera_control->ExposureFeatureBGAPI()) {
                while (!camera_control->LoggingListEmpty()) {
                    camera_handler->LoggingStringAdd(camera_control->LoggingString());
                };
                break;
            }
        }
        else {
            // just wait until exposure command become active
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
}