Spinnaker C++
2.6.0.0
ActionCommand.cpp

ActionCommand.cpp shows how to send action commands to the camera(s).And it also can perform frame synchronization using multiple cameras simultaneously. It uses IEEE 1588 timestamp, triggers and action commands.

One camera will be master and the other camera(s) will be slave(s). All cameras have to be on the same network.

//=============================================================================
// Copyright (c) 2001-2021 FLIR Systems, Inc. All Rights Reserved.
//
// This software is the confidential and proprietary information of FLIR
// Integrated Imaging Solutions, Inc. ("Confidential Information"). You
// shall not disclose such Confidential Information and shall use it only in
// accordance with the terms of the license agreement you entered into
// with FLIR Integrated Imaging Solutions, Inc. (FLIR).
//
// FLIR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
// SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE, OR NON-INFRINGEMENT. FLIR SHALL NOT BE LIABLE FOR ANY DAMAGES
// SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
// THIS SOFTWARE OR ITS DERIVATIVES.
//=============================================================================
#include "Spinnaker.h"
#include <iostream>
#include <sstream>
using namespace Spinnaker;
using namespace Spinnaker::GenApi;
using namespace Spinnaker::GenICam;
using namespace std;
// This helper function allows the example to sleep in both Windows and Linux
// systems. Note that Windows sleep takes milliseconds as a parameter while
// Linux systems take microseconds as a parameter.
void SleepyWrapper(int milliseconds)
{
#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
Sleep(milliseconds);
#else
usleep(1000 * milliseconds);
#endif
}
// This function prints the device information of the camera from the transport
// layer; please see NodeMapInfo example for more in-depth comments on printing
// device information from the nodemap.
int PrintDeviceInfo(INodeMap& nodeMap, const unsigned int camNum)
{
int result = 0;
cout << "Printing device information for camera " << camNum << "..." << endl << endl;
FeatureList_t features;
const CCategoryPtr category = nodeMap.GetNode("DeviceInformation");
if (IsAvailable(category) && IsReadable(category))
{
category->GetFeatures(features);
for (FeatureList_t::const_iterator it = features.begin(); it != features.end(); ++it)
{
const CNodePtr pfeatureNode = *it;
cout << pfeatureNode->GetName() << " : ";
CValuePtr pValue = (CValuePtr)pfeatureNode;
cout << (IsReadable(pValue) ? pValue->ToString() : "Node not readable");
cout << endl;
}
}
else
{
cout << "Device control information not available." << endl;
result = -1;
}
cout << endl;
return result;
}
// This function configure interface settings for action commands
// It configures action device key, group key and group mask.
int ConfigureInterface(const InterfaceList& interfaceList)
{
int result = 0;
InterfacePtr interfacePtr = nullptr;
cout << endl << endl << "*** CONFIGURING INTERFACE ***" << endl << endl;
try
{
const unsigned int numInterfaces = interfaceList.GetSize();
cout << "Number of Interface : " << numInterfaces << endl;
// Check cameras for each interface and apply the action command settings
for (unsigned int i = 0; i < numInterfaces; i++)
{
interfacePtr = interfaceList.GetByIndex(i);
INodeMap& nodeMapInterface = interfacePtr->GetTLNodeMap();
interfacePtr->UpdateCameras();
CameraList camList = interfacePtr->GetCameras();
// Check if interface has any camera
if (camList.GetSize() >= 1)
{
// Display interface name
CStringPtr ptrInterfaceDisplayName = nodeMapInterface.GetNode("InterfaceDisplayName");
const gcstring interfaceDisplayName = ptrInterfaceDisplayName->GetValue();
cout << interfaceDisplayName << endl;
// Apply Action device/group settings
CIntegerPtr ptrGevActionDeviceKey = nodeMapInterface.GetNode("GevActionDeviceKey");
if (!IsAvailable(ptrGevActionDeviceKey) || !IsWritable(ptrGevActionDeviceKey))
{
cout << "Interface " << i
<< " Unable to set Interface Action Device Key (node retrieval). Aborting..." << endl;
return -1;
}
// Set Action Device Key to 0
ptrGevActionDeviceKey->SetValue(0);
cout << "Interface " << i << " action device key is set" << endl;
CIntegerPtr ptrGevActionGroupKey = nodeMapInterface.GetNode("GevActionGroupKey");
if (!IsAvailable(ptrGevActionGroupKey) || !IsWritable(ptrGevActionGroupKey))
{
cout << "Interface " << i
<< " Unable to set Interface Action Group Key (node retrieval). Aborting..." << endl;
return -1;
}
// Set Action Group Key to 1
ptrGevActionGroupKey->SetValue(1);
cout << "Interface " << i << " action group key is set" << endl;
CIntegerPtr ptrGevActionGroupMask = nodeMapInterface.GetNode("GevActionGroupMask");
if (!IsAvailable(ptrGevActionGroupMask) || !IsWritable(ptrGevActionGroupMask))
{
cout << "Interface " << i
<< " Unable to set Interface Action Group Mask (node retrieval). Aborting..." << endl;
return -1;
}
// Set Action Group Mask to 1
ptrGevActionGroupMask->SetValue(1);
cout << "Interface " << i << " action group mask is set" << endl;
}
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function configures IEEE 1588 settings on each camera
// It enables IEEE 1588
int ConfigureIEEE1588(const CameraList& camList)
{
int result = 0;
CameraPtr pCam = nullptr;
cout << endl << endl << "*** CONFIGURING IEEE 1588 ***" << endl << endl;
try
{
// Enable IEEE 1588 settings for each camera
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Enable IEEE 1588 settings
CBooleanPtr ptrIEEE1588 = pCam->GetNodeMap().GetNode("GevIEEE1588");
if (!IsAvailable(ptrIEEE1588) || !IsWritable(ptrIEEE1588))
{
cout << "Camera " << i << " Unable to enable IEEE 1588 (node retrieval). Aborting..." << endl;
return -1;
}
// Enable IEEE 1588
ptrIEEE1588->SetValue(true);
cout << "Camera " << i << " IEEE 1588 is enabled." << endl;
}
// Requires delay for at least 6 seconds to enable 1588 settings
// Add 10 second delay here
cout << "Waiting for 10 seconds before sending action command. " << endl;
SleepyWrapper(10000);
// Check if IEEE 1588 settings is enabled for each camera
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
CCommandPtr ptrGevIEEE1588DataSetLatch = pCam->GetNodeMap().GetNode("GevIEEE1588DataSetLatch");
if (!IsAvailable(ptrGevIEEE1588DataSetLatch))
{
cout << "Camera " << i << " Unable to execute IEEE 1588 data set latch (node retrieval). Aborting..."
<< endl;
return -1;
}
ptrGevIEEE1588DataSetLatch->Execute();
// Check if 1588 status is not in intialization
CEnumerationPtr ptrGevIEEE1588StatusLatched = pCam->GetNodeMap().GetNode("GevIEEE1588StatusLatched");
if (!IsAvailable(ptrGevIEEE1588StatusLatched) || !IsReadable(ptrGevIEEE1588StatusLatched))
{
cout << "Camera " << i << " Unable to read IEEE1588 status (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrGevIEEE1588StatusLatchedInitializing =
ptrGevIEEE1588StatusLatched->GetEntryByName("Initializing");
if (!IsAvailable(ptrGevIEEE1588StatusLatchedInitializing) ||
!IsReadable(ptrGevIEEE1588StatusLatchedInitializing))
{
cout << "Camera " << i << " Unable to get IEEE1588 status (enum entry retrieval). Aborting..." << endl;
return -1;
}
if (ptrGevIEEE1588StatusLatched->GetIntValue() == ptrGevIEEE1588StatusLatchedInitializing->GetValue())
{
cout << "Camera" << i << " is in Initializing mode. It can't send action command." << endl;
return -1;
}
// Check if camera(s) is(are) synchronized to master camera
// Verify if camera offset from master is larger than 1000ns which means camera(s) is(are) not synchronized
CIntegerPtr ptrGevIEEE1588OffsetFromMasterLatched =
pCam->GetNodeMap().GetNode("GevIEEE1588OffsetFromMasterLatched");
if (!IsAvailable(ptrGevIEEE1588OffsetFromMasterLatched) ||
!IsReadable(ptrGevIEEE1588OffsetFromMasterLatched))
{
cout << "Camera " << i << " Unable to read IEEE1588 offset (node retrieval). Aborting..." << endl;
return -1;
}
if (ptrGevIEEE1588OffsetFromMasterLatched->GetValue() > 1000)
{
cout << "Camera " << i << " has offset higher than 1000ns. Camera(s) is(are) not synchronized" << endl;
return -1;
}
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function configures action control settings
// For each camera, it sets action device key, group key and group mask.
{
int result = 0;
CameraPtr pCam = nullptr;
cout << endl << endl << "*** CONFIGURING ACTION CONTROL ***" << endl << endl;
try
{
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Apply action group setting
CIntegerPtr ptrActionDeviceKey = pCam->GetNodeMap().GetNode("ActionDeviceKey");
if (!IsAvailable(ptrActionDeviceKey) || !IsWritable(ptrActionDeviceKey))
{
cout << "Camera " << i << " Unable to set Action Device Key (node retrieval). Aborting..." << endl;
return -1;
}
// Set action device key to 0
ptrActionDeviceKey->SetValue(0);
cout << "Camera " << i << " action device key is set" << endl;
CIntegerPtr ptrActionGroupKey = pCam->GetNodeMap().GetNode("ActionGroupKey");
if (!IsAvailable(ptrActionGroupKey) || !IsWritable(ptrActionGroupKey))
{
cout << "Camera " << i << " Unable to set Action Group Key (node retrieval). Aborting..." << endl;
return -1;
}
// Set action group key to 1
ptrActionGroupKey->SetValue(1);
cout << "Camera " << i << " action group key is set" << endl;
CIntegerPtr ptrActionGroupMask = pCam->GetNodeMap().GetNode("ActionGroupMask");
if (!IsAvailable(ptrActionGroupMask) || !IsWritable(ptrActionGroupMask))
{
cout << "Camera " << i << " Unable to retrieve Action Group Mask (node retrieval). Aborting...e"
<< endl;
return -1;
}
// Set action group mask to 1
ptrActionGroupMask->SetValue(1);
cout << "Camera " << i << " action group mask is set" << endl;
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function configures other nodes for frame synchronization.
// It sets acquisition frame rate , exposure settings,
// acquisition Timing and image timestamp
int ConfigureOtherNodes(const CameraList& camList)
{
int result = 0;
CameraPtr pCam = nullptr;
cout << endl << endl << "*** CONFIGURING OTHER NODES ***" << endl << endl;
try
{
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Frame rate setting
// Turn on frame rate control
CBooleanPtr ptrFrameRateEnable = pCam->GetNodeMap().GetNode("AcquisitionFrameRateEnable");
if (!IsAvailable(ptrFrameRateEnable) || !IsWritable(ptrFrameRateEnable))
{
cout << "Camera " << i << " Unable to enable Acquisition Frame Rate (node retrieval). Aborting..."
<< endl;
return -1;
}
// Enable Acquisition Frame Rate Enable
ptrFrameRateEnable->SetValue(true);
CFloatPtr ptrFrameRate = pCam->GetNodeMap().GetNode("AcquisitionFrameRate");
if (!IsAvailable(ptrFrameRate) || !IsWritable(ptrFrameRate))
{
cout << "Camera " << i << " Unable to set Acquisition Frame Rate (node retrieval). Aborting..." << endl;
return -1;
}
// Set 10fps for this example
const float frameRate = 10.0f;
ptrFrameRate->SetValue(frameRate);
cout << "Camera " << i << " Frame rate is set to " << frameRate << endl;
// Turn off exposure auto.
// Otherwise, it can miss action command because exposure is too long
CEnumerationPtr ptrExposureAuto = pCam->GetNodeMap().GetNode("ExposureAuto");
if (!IsAvailable(ptrExposureAuto) || !IsWritable(ptrExposureAuto))
{
cout << "Camera " << i << " Unable to disable Exposure Auto (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrExposureAutoOff = ptrExposureAuto->GetEntryByName("Off");
if (!IsAvailable(ptrExposureAutoOff) || !IsReadable(ptrExposureAutoOff))
{
cout << "Camera " << i << " Unable to set Exposure Auto (enum entry retrieval). Aborting..." << endl;
return -1;
}
// Turn off Exposure Auto
ptrExposureAuto->SetIntValue(ptrExposureAutoOff->GetValue());
CFloatPtr ptrExposureTime = pCam->GetNodeMap().GetNode("ExposureTime");
if (IsAvailable(ptrExposureTime) && IsWritable(ptrExposureTime))
{
// Set exposure time to 1000 for this example
const float exposureTime = 1000.0f;
ptrExposureTime->SetValue(exposureTime);
cout << "Camera " << i << " Exposure time is set to " << exposureTime << endl;
}
else
{
cout << "Camera " << i << " Unable to set Exposure Time (node retrieval). Aborting..." << endl;
}
// Apply acquisition timing, timestamp setting
CEnumerationPtr ptrAcquisitionTiming = pCam->GetNodeMap().GetNode("AcquisitionFrameAcquisitionTiming");
if (!IsAvailable(ptrAcquisitionTiming) || !IsWritable(ptrAcquisitionTiming))
{
cout << "Camera " << i
<< " Unable to set Acquisition Frame Acquisition Timing (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrAcquisitionTimingRealtimeClock = ptrAcquisitionTiming->GetEntryByName("RealtimeClock");
if (!IsAvailable(ptrAcquisitionTimingRealtimeClock) || !IsReadable(ptrAcquisitionTimingRealtimeClock))
{
cout << "Camera " << i << " Unable to set acquisition timing (enum entry retrieval). Aborting..."
<< endl;
return -1;
}
// Set Acquisition Frame Acquisition Timing to realtime clock
ptrAcquisitionTiming->SetIntValue(ptrAcquisitionTimingRealtimeClock->GetValue());
CEnumerationPtr ptrAcquisitionTimestamp = pCam->GetNodeMap().GetNode("AcquisitionImageTimestamp");
if (!IsAvailable(ptrAcquisitionTimestamp) || !IsWritable(ptrAcquisitionTimestamp))
{
cout << "Camera " << i
<< " Unable to retreive Acquisition Image Timestamp (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrAcquisitionTimestampStartOfExposure =
ptrAcquisitionTimestamp->GetEntryByName("StartOfExposure");
if (!IsAvailable(ptrAcquisitionTimestampStartOfExposure) ||
!IsReadable(ptrAcquisitionTimestampStartOfExposure))
{
cout << "Camera " << i << " Unable to set acquisition timestamp (enum entry retrieval). Aborting..."
<< endl;
return -1;
}
// Set Acquisition Image Timestamp to start of exposure
ptrAcquisitionTimestamp->SetIntValue(ptrAcquisitionTimestampStartOfExposure->GetValue());
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function configures the camera to use a trigger. First, trigger mode is
// set to off in order to select the trigger source. Once the trigger source
// has been selected, trigger mode is then enabled, which has the camera
// capture only a single image upon the execution of the chosen trigger
int ConfigureTrigger(const CameraList camList)
{
int result = 0;
CameraPtr pCam = nullptr;
cout << endl << endl << "*** CONFIGURING TRIGGER ***" << endl << endl;
try
{
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Trigger setting
//
// Ensure trigger mode off
//
// *** NOTES ***
// The trigger must be disabled in order to configure whether the source
// is software or hardware.
//
CEnumerationPtr ptrTriggerMode = pCam->GetNodeMap().GetNode("TriggerMode");
if (!IsAvailable(ptrTriggerMode) || !IsReadable(ptrTriggerMode))
{
cout << "Camera " << i << " Unable to disable trigger mode (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrTriggerModeOff = ptrTriggerMode->GetEntryByName("Off");
if (!IsAvailable(ptrTriggerModeOff) || !IsReadable(ptrTriggerModeOff))
{
cout << "Camera " << i << " Unable to disable trigger mode (enum entry retrieval). Aborting..." << endl;
return -1;
}
ptrTriggerMode->SetIntValue(ptrTriggerModeOff->GetValue());
cout << "Camera " << i << " Trigger mode disabled..." << endl;
// Select trigger selector
CEnumerationPtr ptrTriggerSelector = pCam->GetNodeMap().GetNode("TriggerSelector");
if (!IsAvailable(ptrTriggerSelector) || !IsWritable(ptrTriggerSelector))
{
cout << "Camera " << i << " Unable to get trigger selector (node retrieval). Aborting..." << endl;
return -1;
}
// Need to select AcquisitionStart for real time clock
CEnumEntryPtr ptrTriggerSelectorAcquisitionStart = ptrTriggerSelector->GetEntryByName("AcquisitionStart");
if (!IsAvailable(ptrTriggerSelectorAcquisitionStart) || !IsReadable(ptrTriggerSelectorAcquisitionStart))
{
cout << "Camera " << i << " Unable to set trigger selector (enum entry retrieval). Aborting..." << endl;
return -1;
}
ptrTriggerSelector->SetIntValue(ptrTriggerSelectorAcquisitionStart->GetValue());
//
// Select trigger source
//
// *** NOTES ***
// The trigger source must be set to hardware or software while trigger
// mode is off.
//
CEnumerationPtr ptrTriggerSource = pCam->GetNodeMap().GetNode("TriggerSource");
if (!IsAvailable(ptrTriggerSelector) || !IsWritable(ptrTriggerSelector))
{
cout << "Camera " << i << " Unable to get trigger source (node retrieval). Aborting..." << endl;
return -1;
}
CEnumEntryPtr ptrTriggerSourceAction0 = ptrTriggerSource->GetEntryByName("Action0");
if (!IsAvailable(ptrTriggerSourceAction0) || !IsReadable(ptrTriggerSourceAction0))
{
cout << "Camera " << i << " Unable to set trigger selector (enum entry retrieval). Aborting..." << endl;
return -1;
}
ptrTriggerSource->SetIntValue(ptrTriggerSourceAction0->GetValue());
//
// Turn trigger mode on
//
// *** LATER ***
// Once the appropriate trigger source has been set, turn trigger mode
// on in order to retrieve images using the trigger.
//
CEnumEntryPtr ptrTriggerModeOn = ptrTriggerMode->GetEntryByName("On");
if (!IsAvailable(ptrTriggerModeOn) || !IsReadable(ptrTriggerModeOn))
{
cout << "Camera " << i << " Unable to enable trigger mode (enum entry retrieval). Aborting..." << endl;
return -1;
}
ptrTriggerMode->SetIntValue(ptrTriggerModeOn->GetValue());
// NOTE: Blackfly and Flea3 GEV cameras need 1 second delay after trigger mode is turned on
cout << "Camera " << i << " Trigger mode turned back on..." << endl << endl;
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function configures chunk data settings
int ConfigureChunkData(const CameraList& camList)
{
int result = 0;
CameraPtr pCam = nullptr;
cout << endl << endl << "*** CONFIGURING CHUNK DATA ***" << endl << endl;
try
{
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
//
// Activate chunk mode
//
// *** NOTES ***
// Once enabled, chunk data will be available at the end of the payload
// of every image captured until it is disabled. Chunk data can also be
// retrieved from the nodemap.
//
CBooleanPtr ptrChunkModeActive = pCam->GetNodeMap().GetNode("ChunkModeActive");
if (!IsAvailable(ptrChunkModeActive) || !IsWritable(ptrChunkModeActive))
{
cout << "Camera " << i << " Unable to activate chunk mode. Aborting..." << endl << endl;
return -1;
}
ptrChunkModeActive->SetValue(true);
cout << "Camera " << i << " Chunk mode activated..." << endl;
CEnumerationPtr ptrChunkSelector = pCam->GetNodeMap().GetNode("ChunkSelector");
if (!IsAvailable(ptrChunkSelector) || !IsWritable(ptrChunkSelector))
{
cout << "Camera " << i << " Chunk Selector is not writable" << endl;
return -1;
}
// Select Timestamp for Chunk data
CEnumEntryPtr ptrChunkSelectorTimestamp = ptrChunkSelector->GetEntryByName("Timestamp");
if (!IsAvailable(ptrChunkSelectorTimestamp) || !IsReadable(ptrChunkSelectorTimestamp))
{
cout << "Camera " << i << " Unable to set Chunk Selector (node retrieval). Aborting..." << endl;
return -1;
}
ptrChunkSelector->SetIntValue(ptrChunkSelectorTimestamp->GetValue());
// Retrieve corresponding boolean
CBooleanPtr ptrChunkEnable = pCam->GetNodeMap().GetNode("ChunkEnable");
// Enable the boolean, thus enabling the corresponding chunk data
if (!IsAvailable(ptrChunkEnable))
{
cout << "Camera " << i << " not available" << endl;
result = -1;
}
else if (ptrChunkEnable->GetValue())
{
cout << "Camera " << i << " enabled" << endl;
}
else if (IsWritable(ptrChunkEnable))
{
ptrChunkEnable->SetValue(true);
cout << "Camera " << i << " enabled" << endl;
}
else
{
cout << "Camera " << i << " not writable" << endl;
result = -1;
}
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
int AcquireImages(const SystemPtr& system, const InterfaceList& interfaceList, CameraList camList)
{
int result = 0;
CameraPtr pCam = nullptr;
InterfacePtr interfacePtr = nullptr;
const int scheduleActionTimeInSec = 2; // scheduled action time in seconds.
// 2 seconds used for this example
cout << endl << "*** IMAGE ACQUISITION ***" << endl << endl;
try
{
//
// Prepare each camera to acquire images
//
// *** NOTES ***
// For pseudo-simultaneous streaming, each camera is prepared as if it
// were just one, but in a loop. Notice that cameras are selected with
// an index. We demonstrate pseudo-simultaneous streaming because true
// simultaneous streaming would require multiple process or threads,
// which is too complex for an example.
//
// Serial numbers are the only persistent objects we gather in this
// example, which is why a vector is created.
//
vector<gcstring> strSerialNumbers(camList.GetSize());
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Set acquisition mode to continuous
CEnumerationPtr ptrAcquisitionMode = pCam->GetNodeMap().GetNode("AcquisitionMode");
if (!IsAvailable(ptrAcquisitionMode) || !IsWritable(ptrAcquisitionMode))
{
cout << "Unable to set acquisition mode to continuous (node retrieval; camera " << i << "). Aborting..."
<< endl
<< endl;
return -1;
}
CEnumEntryPtr ptrAcquisitionModeContinuous = ptrAcquisitionMode->GetEntryByName("Continuous");
if (!IsAvailable(ptrAcquisitionModeContinuous) || !IsReadable(ptrAcquisitionModeContinuous))
{
cout << "Unable to set acquisition mode to continuous (entry 'continuous' retrieval " << i
<< "). Aborting..." << endl
<< endl;
return -1;
}
const int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue();
ptrAcquisitionMode->SetIntValue(acquisitionModeContinuous);
cout << "Camera " << i << " acquisition mode set to continuous..." << endl;
// Device Link Throughput Limit setting
CIntegerPtr ptrDeviceLinkThroughputLimit = pCam->GetNodeMap().GetNode("DeviceLinkThroughputLimit");
if (!IsAvailable(ptrDeviceLinkThroughputLimit) || !IsWritable(ptrDeviceLinkThroughputLimit))
{
cout << "Unable to set device link throughput limit (node retrieval; camera " << i << "). Aborting..."
<< endl
<< endl;
return -1;
}
// Set throughput close to the maximum limit 125000000
const int64_t deviceLinkThroughputLimit = 100000000;
ptrDeviceLinkThroughputLimit->SetValue(deviceLinkThroughputLimit);
// Packet size, Packet delay setting
CIntegerPtr ptrPacketSize = pCam->GetNodeMap().GetNode("GevSCPSPacketSize");
if (!IsAvailable(ptrPacketSize) || !IsWritable(ptrPacketSize))
{
cout << "Unable to set packet size (node retrieval; camera " << i << "). Aborting..." << endl << endl;
return -1;
}
// Set packet size to maximum value
const int64_t packetSize = 9000;
ptrPacketSize->SetValue(packetSize);
CIntegerPtr ptrPacketDelay = pCam->GetNodeMap().GetNode("GevSCPD");
if (!IsAvailable(ptrPacketDelay) || !IsWritable(ptrPacketDelay))
{
cout << "Unable to set packet delay (node retrieval; camera " << i << "). Aborting..." << endl << endl;
return -1;
}
// Set packet delay 9000 by using formula packet delay x 8
// for BFS only, there is 8 bit internal tick
// for Gen 2 cameras , just use packet delay 9000
const int64_t packetDelay = 72000;
ptrPacketDelay->SetValue(packetDelay);
// Begin acquiring images
pCam->BeginAcquisition();
cout << "Camera " << i << " started acquiring images..." << endl;
// Retrieve device serial number for filename
strSerialNumbers[i] = "";
CStringPtr ptrStringSerial = pCam->GetTLDeviceNodeMap().GetNode("DeviceSerialNumber");
if (IsAvailable(ptrStringSerial) && IsReadable(ptrStringSerial))
{
strSerialNumbers[i] = ptrStringSerial->GetValue();
cout << "Camera " << i << " serial number set to " << strSerialNumbers[i] << "..." << endl;
}
cout << endl;
}
// Find which interface has more than 1 camera, and send action commands
const unsigned int numInterfaces = interfaceList.GetSize();
for (unsigned int i = 0; i < numInterfaces; i++)
{
interfacePtr = interfaceList.GetByIndex(i);
INodeMap& nodeMapInterface = interfacePtr->GetTLNodeMap();
interfacePtr->UpdateCameras();
camList = interfacePtr->GetCameras();
// Action command works with just 1 camera
// However, requires more than 2 cameras to do frame synchronization
if (camList.GetSize() >= 1)
{
// Display interface name
CStringPtr ptrInterfaceDisplayName = nodeMapInterface.GetNode("InterfaceDisplayName");
if (IsAvailable(ptrInterfaceDisplayName) && IsReadable(ptrInterfaceDisplayName))
{
const gcstring interfaceDisplayName = ptrInterfaceDisplayName->GetValue();
cout << interfaceDisplayName << endl;
}
else
{
cout << "Interface " << i << " Unable to read Interface display name (node retrieval). Aborting..."
<< endl;
}
// Latch Timestamp for Camera 0
pCam = camList.GetByIndex(0);
CCommandPtr ptrTimestampLatch = pCam->GetNodeMap().GetNode("TimestampLatch");
if (!IsAvailable(ptrTimestampLatch))
{
cout << "Interface " << i << " Unable to retreive Time Stamp Latch (node retrieval). Aborting..."
<< endl;
return -1;
}
ptrTimestampLatch->Execute();
CIntegerPtr ptrTimestampLatchValue = pCam->GetNodeMap().GetNode("TimestampLatchValue");
if (!IsAvailable(ptrTimestampLatchValue) || !IsReadable(ptrTimestampLatchValue))
{
cout << "Interface " << i << " Unable to read Time Stamp Latch Value (node retrieval). Aborting.."
<< endl;
return -1;
}
// Print out timestamp value
int64_t timestampLatchValue = ptrTimestampLatchValue->GetValue();
cout << "Interface " << i << " Timestamp = " << timestampLatchValue << endl;
// Issue Scheduled action command
const int64_t scheduleTimestampLatchValue = scheduleActionTimeInSec * 1000000000;
timestampLatchValue += scheduleTimestampLatchValue;
cout << "Interface " << i << " Update ActionTime = " << timestampLatchValue << endl << endl;
// Start PTP frame sync
cout << "*** Start PTP Frame sync ***" << endl << endl;
// Set action time
CIntegerPtr ptrGevActionTime = nodeMapInterface.GetNode("GevActionTime");
if (!IsAvailable(ptrGevActionTime) || !IsWritable(ptrGevActionTime))
{
cout << "Interface " << i << " Unable to set Action Time (node retrieval). Aborting..." << endl;
return -1;
}
ptrGevActionTime->SetValue(timestampLatchValue);
// Send action command
unsigned int actionCommandResultsSize = camList.GetSize();
std::vector<ActionCommandResult> actionCommandResults(actionCommandResultsSize);
// Send action command
interfacePtr->SendActionCommand(
0, 1, 1, timestampLatchValue, &actionCommandResultsSize, actionCommandResults.data());
if (actionCommandResultsSize != camList.GetSize())
{
cout << "Not every camera responded to the action command" << endl;
return -1;
}
for (unsigned int j = 0; j < actionCommandResultsSize; j++)
{
if (actionCommandResults[j].Status != ACTION_COMMAND_STATUS_OK)
{
cout << "A device did not successfully acknowledge the action command, with status " +
to_string(static_cast<long long>(actionCommandResults[j].Status))
<< endl;
}
}
// Action Command to commence after GevActionTime, so wait a bit before retrieving the image
cout << "Waiting for " << scheduleActionTimeInSec << " seconds for the start of the action command. "
<< endl
<< endl;
SleepyWrapper(scheduleActionTimeInSec * 1000);
//
// Retrieve, convert, and save images for each camera
//
// *** NOTES ***
// In order to work with simultaneous camera streams, nested loops are
// needed. It is important that the inner loop be the one iterating
// through the cameras; otherwise, all images will be grabbed from a
// single camera before grabbing any images from another.
//
const unsigned int k_numImages = 10;
for (unsigned int imageCnt = 0; imageCnt < k_numImages; imageCnt++)
{
for (unsigned int index = 0; index < camList.GetSize(); index++)
{
try
{
// Select camera
pCam = camList.GetByIndex(index);
// Retrieve next received image and ensure image completion
ImagePtr pResultImage = pCam->GetNextImage(1000);
if (pResultImage->IsIncomplete())
{
cout << "Image incomplete with image status " << pResultImage->GetImageStatus() << "..."
<< endl
<< endl;
}
else
{
// Print image information
cout << "Camera " << index << " grabbed image " << imageCnt
<< ", width = " << pResultImage->GetWidth()
<< ", height = " << pResultImage->GetHeight() << endl;
}
// Get timestamp
ChunkData chunkData = pResultImage->GetChunkData();
// Retrieve timestamp
const int64_t timestamp = chunkData.GetTimestamp();
cout << "\tTimestamp: " << timestamp << endl;
// Release image
pResultImage->Release();
cout << endl;
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
}
}
//
// End acquisition for each camera
//
// *** NOTES ***
// Notice that what is usually a one-step process is now two steps
// because of the additional step of selecting the camera. It is worth
// repeating that camera selection needs to be done once per loop.
//
// It is possible to interact with cameras through the camera list with
// GetByIndex(); this is an alternative to retrieving cameras as
// CameraPtr objects that can be quick and easy for small tasks.
//
// End acquisition
for (unsigned int index = 0; index < camList.GetSize(); index++)
{
camList.GetByIndex(index)->EndAcquisition();
}
}
else
{
cout << "There are no cameras on interface " << i << endl;
}
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// This function acts as the body of the example; please see NodeMapInfo example
// for more in-depth comments on setting up cameras.
int RunMultipleCameras(const SystemPtr& system, const InterfaceList& interfaceList, const CameraList& camList)
{
int result = 0;
CameraPtr pCam = nullptr;
try
{
//
// Retrieve transport layer nodemaps and print device information for
// each camera
//
// *** NOTES ***
// This example retrieves information from the transport layer nodemap
// twice: once to print device information and once to grab the device
// serial number. Rather than caching the nodemap, each nodemap is
// retrieved both times as needed.
//
cout << endl << "*** DEVICE INFORMATION ***" << endl << endl;
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Retrieve TL device nodemap
INodeMap& nodeMapTLDevice = pCam->GetTLDeviceNodeMap();
// Print device information
result = PrintDeviceInfo(nodeMapTLDevice, i);
}
//
// Initialize each camera
//
// *** NOTES ***
// You may notice that the steps in this function have more loops with
// less steps per loop; this contrasts the AcquireImages() function
// which has less loops but more steps per loop. This is done for
// demonstrative purposes as both work equally well.
//
// *** LATER ***
// Each camera needs to be deinitialized once all images have been
// acquired.
//
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Initialize camera
pCam->Init();
}
// Configure Interface Settings
result = ConfigureInterface(interfaceList);
if (result < 0)
{
return result;
}
// Configure IEEE 1588 settings
result = ConfigureIEEE1588(camList);
if (result < 0)
{
return result;
}
// Configure Action control settings
result = ConfigureActionControl(camList);
if (result < 0)
{
return result;
}
// Configure other node settings for frame synchronization
result = ConfigureOtherNodes(camList);
if (result < 0)
{
return result;
}
// Configure trigger mode
result = ConfigureTrigger(camList);
if (result < 0)
{
return result;
}
// Configure chunk data
result = ConfigureChunkData(camList);
if (result < 0)
{
return result;
}
// Acquire images on all cameras
result = AcquireImages(system, interfaceList, camList);
if (result < 0)
{
return result;
}
//
// Deinitialize each camera
//
// *** NOTES ***
// Again, each camera must be deinitialized separately by first
// selecting the camera and then deinitializing it.
//
for (unsigned int i = 0; i < camList.GetSize(); i++)
{
// Select camera
pCam = camList.GetByIndex(i);
// Deinitialize camera
pCam->DeInit();
}
}
{
cout << "Error: " << e.what() << endl;
result = -1;
}
return result;
}
// Example entry point; please see Enumeration example for more in-depth
// comments on preparing and cleaning up the system.
int main(int /*argc*/, char** /*argv*/)
{
int result = 0;
// Print application build information
cout << "Application build date: " << __DATE__ << " " << __TIME__ << endl << endl;
// Retrieve singleton reference to system object
// Print out current library version
const LibraryVersion spinnakerLibraryVersion = system->GetLibraryVersion();
cout << "Spinnaker library version: " << spinnakerLibraryVersion.major << "." << spinnakerLibraryVersion.minor
<< "." << spinnakerLibraryVersion.type << "." << spinnakerLibraryVersion.build << endl
<< endl;
// Retrieve list of cameras from the system
CameraList camList = system->GetCameras();
const unsigned int numCameras = camList.GetSize();
cout << "Number of cameras detected: " << numCameras << endl << endl;
// Finish if there are no cameras
if (numCameras == 0)
{
// Clear camera list before releasing system
camList.Clear();
// Release system
system->ReleaseInstance();
cout << "Not enough cameras!" << endl;
cout << "Done! Press Enter to exit..." << endl;
getchar();
return -1;
}
// Get Interface
InterfaceList interfaceList = system->GetInterfaces();
result = RunMultipleCameras(system, interfaceList, camList);
cout << "Example complete..." << endl << endl;
// Clear camera list before releasing system
camList.Clear();
interfaceList.Clear();
// Release system
system->ReleaseInstance();
cout << endl << "Done! Press Enter to exit..." << endl;
getchar();
return result;
}
Contact Support Spinnaker® SDK Programmer's Guide and API Reference