EnumerationEvents¶
EnumerationEvents.cpp explores arrival and removal events on interfaces and the system. It relies on information provided in the Enumeration, Acquisition, and NodeMapInfo examples.
//=============================================================================
// Copyright (c) 2026 FLIR Integrated Imaging Solutions, 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.
//=============================================================================
/**
* @example EnumerationEvents.cpp
*
* @brief EnumerationEvents.cpp explores arrival and removal events on
* interfaces and the system. It relies on information provided in the
* Enumeration, Acquisition, and NodeMapInfo examples.
*
* It can also be helpful to familiarize yourself with the NodeMapCallback
* example, as nodemap callbacks follow the same general procedure as
* events, but with a few less steps.
*
* This example creates two user-defined classes: InterfaceEventHandlerImpl and
* SystemEventHandlerImpl. These child classes allow the user to define properties,
* parameters, and the event itself while the parent classes - DeviceArrivalEventHandler,
* DeviceRemovalEventHandler, and InterfaceEventHandler - allow the child classes to interface
* with Spinnaker.
*
* Please leave us feedback at: https://www.surveymonkey.com/r/TDYMVAPI
* More source code examples at: https://github.com/Teledyne-MV/Spinnaker-Examples
* Need help? Check out our forum at: https://teledynevisionsolutions.zendesk.com/hc/en-us/community/topics
*/
#include "Spinnaker.h"
#include "SpinGenApi/SpinnakerGenApi.h"
#include <iostream>
#include <sstream>
#if _MSC_VER != 1600
#include <mutex>
#endif
using namespace Spinnaker;
using namespace Spinnaker::GenApi;
using namespace Spinnaker::GenICam;
using namespace std;
// This class defines the properties and functions for device arrivals and removals
// on an interface. Take special note of the signatures of the OnDeviceArrival()
// and OnDeviceRemoval() methods. Also, enumeration event handlers must inherit at least
// one of the three event types - DeviceArrivalEventHandler, DeviceRemovalEventHandler, and InterfaceEventHandler
// if they are to be registered to interfaces, the system, or both.
class InterfaceEventHandlerImpl : public InterfaceEventHandler
{
public:
//
// Set the constructor and destructor
//
// *** NOTES ***
// When constructing a generic InterfaceEventHandler to be registered to the system,
// the handler will not have knowledge of which interface triggered the event callbacks.
// On the other hand, InterfaceEventHandlerImpl does not need knowledge about the system if we
// are constructing it to be registered to a specific interface.
//
InterfaceEventHandlerImpl(SystemPtr system)
: m_system(system), m_interface(nullptr), m_interfaceID(""), m_registerToSystem(true){};
InterfaceEventHandlerImpl(InterfacePtr iface, std::string interfaceID)
: m_system(nullptr), m_interface(iface), m_interfaceID(interfaceID), m_registerToSystem(false){};
~InterfaceEventHandlerImpl(){};
// Helper function to print the number of devices on an interface event registered to the system.
// Note: Using strings in event print statements prevents text from overlapping in the console output
void PrintGenericHandlerMessage(const unsigned long long deviceCount)
{
cout << "Generic interface event handler:\n";
const bool singular = deviceCount == 1;
const string genericMsg = "\tThere " + string(singular ? "is " : "are ") + to_string(deviceCount) +
string(singular ? " device " : " devices ") + "on the system.\n";
cout << genericMsg << endl;
}
// This function defines the arrival event handler on an interface. It prints out
// the device serial number of the camera arriving and the interface
// number. The argument is the serial number of the camera that triggered
// the arrival event.
// If the event handler was constructed to be registered to the system as a generic
// InterfaceEventHandler, then we just retrieve the number of cameras
// currently connected on the system and print it out.
void OnDeviceArrival(CameraPtr pCamera)
{
if (m_registerToSystem)
{
const auto deviceCount = static_cast<unsigned long long>(m_system->GetCameras().GetSize());
PrintGenericHandlerMessage(deviceCount);
}
else
{
cout << "Interface event handler:\n";
const std::string arrivalMsg = "\tDevice " + std::string(pCamera->GetDeviceSerialNumber()) +
" has arrived on interface '" + m_interfaceID + "'.\n";
cout << arrivalMsg << endl;
}
}
// This function defines removal event handlers on an interface. It prints out the
// device serial number of the camera being removed and the interface
// number. The argument is the serial number of the camera that triggered
// the removal event.
// If the event handler was constructed to be registered to the system as a generic
// InterfaceEventHandler, then we just retrieve the number of cameras
// currently connected on the system and print it out.
void OnDeviceRemoval(CameraPtr pCamera)
{
if (m_registerToSystem)
{
try
{
// At this point the interface that the camera is connected to
// may have been removed as well, so we need to handle
// potential exceptions when calling GetCameras().
const auto deviceCount = static_cast<unsigned long long>(m_system->GetCameras().GetSize());
PrintGenericHandlerMessage(deviceCount);
}
catch (Spinnaker::Exception& e)
{
cout << "Error updating cameras: " << e.what() << endl;
}
}
else
{
cout << "Interface event handler:\n";
const std::string removalMsg = "\tDevice " + std::string(pCamera->GetDeviceSerialNumber()) +
" was removed from interface '" + m_interfaceID + "'.\n";
cout << removalMsg << endl;
}
}
// This function returns the interface ID that the interface event handler is bound to.
std::string GetInterfaceId()
{
return m_interfaceID;
}
private:
SystemPtr m_system;
InterfacePtr m_interface;
std::string m_interfaceID;
bool m_registerToSystem;
};
// This class defines the properties and methods for interface arrivals and removals
// on the system. Take special note of the signatures of the OnInterfaceArrival()
// and OnInterfaceRemoval() methods. Interface enumeration event handlers must inherit from
// SystemEventHandler.
class SystemEventHandlerImpl : public SystemEventHandler
{
public:
SystemEventHandlerImpl(SystemPtr system) : m_system(system){};
~SystemEventHandlerImpl(){};
void LockEventHandlerMutex()
{
eventHandlersMutex.lock();
}
void UnlockEventHandlerMutex()
{
eventHandlersMutex.unlock();
}
// This method defines the interface arrival event on the system.
// It first prints the ID of the arriving interface, then
// registers the interface event on the newly arrived interface.
//
// *** NOTES ***
// Only arrival events for GEV interfaces are currently supported.
void OnInterfaceArrival(InterfacePtr pInterface)
{
cout << "System event handler:\n";
cout << "\tInterface '" << pInterface->TLInterface.InterfaceID.ToString() << "' has arrived on the system."
<< endl;
// UpdateInterfaceList() only updates newly arrived or newly removed interfaces.
// In particular, after this call:
//
// - Any pre-existing interfaces will still be valid.
// - Any pointers to newly removed interfaces will be invalid.
//
// *** NOTES ***
// - Invalid pointers will be re-validated if the interface comes back (arrives) with the same interface ID.
// If the interface ID changes, you can use the pointer populated by this callback or you must get a new
// pointer from the updated interface list.
//
// - Interface indices used to access an interface with GetInterfaces() may change after updating the interface
// list. The interface at a particular index cannot be expected to remain at that index after calling
// UpdateInterfaceList().
m_system->UpdateInterfaceList();
CameraList cameraList = pInterface->GetCameras();
const unsigned int numCameras = cameraList.GetSize();
for (unsigned int camIdx = 0; camIdx < numCameras; camIdx++)
{
CameraPtr pCam = cameraList.GetByIndex(camIdx);
INodeMap& nodeMapTLDevice = pCam->GetTLDeviceNodeMap();
CStringPtr ptrStringSerial = nodeMapTLDevice.GetNode("DeviceSerialNumber");
if (IsReadable(ptrStringSerial))
{
const gcstring deviceSerialNumber = ptrStringSerial->GetValue();
cout << "\tDevice " << string(deviceSerialNumber) << " is connected to interface '"
<< pInterface->TLInterface.InterfaceID.ToString() << "'." << endl;
}
}
// Create interface event
{
LockEventHandlerMutex();
try
{
shared_ptr<InterfaceEventHandlerImpl> interfaceEventHandler = shared_ptr<InterfaceEventHandlerImpl>(
new InterfaceEventHandlerImpl(pInterface, pInterface->TLInterface.InterfaceID.ToString().c_str()));
m_interfaceEventHandlers.push_back(interfaceEventHandler);
// Register interface event
pInterface->RegisterEventHandler(*interfaceEventHandler);
cout << "Event handler registered to interface '" << pInterface->TLInterface.InterfaceID.ToString()
<< "'..." << endl;
}
catch (exception& e)
{
cout << "Error registering interface event handler to '"
<< pInterface->TLInterface.InterfaceID.ToString() << "' :" << e.what() << endl;
}
UnlockEventHandlerMutex();
}
}
// This method defines the interface removal event on the system.
// It prints the ID of the interface removed.
//
// *** NOTES ***
// Only removal events for GEV interfaces are currently supported.
void OnInterfaceRemoval(InterfacePtr pInterface)
{
cout << "System event handler:" << endl;
cout << "\tInterface '" << pInterface->TLInterface.InterfaceID.ToString() << "' was removed from the system."
<< endl;
// Interface indices used to access an interface with GetInterfaces() may change after updating the interface
// list. The interface at a particular index cannot be expected to remain at that index after calling
// UpdateInterfaceList().
m_system->UpdateInterfaceList();
// Find the event handler that was registered to the removed interface and delete it.
// Interface events are automatically unregistered when the interface is removed so it is not necessary to
// manually unregister them.
{
LockEventHandlerMutex();
try
{
unsigned int handlerIdx = 0;
for (handlerIdx = 0; handlerIdx < m_interfaceEventHandlers.size(); handlerIdx++)
{
if (m_interfaceEventHandlers[handlerIdx]->GetInterfaceId() ==
pInterface->TLInterface.InterfaceID.ToString().c_str())
{
m_interfaceEventHandlers.erase(m_interfaceEventHandlers.begin() + handlerIdx);
break;
}
}
}
catch (exception& e)
{
cout << "Error erasing event handler from interface '" << pInterface->TLInterface.InterfaceID.ToString()
<< "' :" << e.what() << endl;
}
UnlockEventHandlerMutex();
}
}
void RegisterInterfaceEventToSystem()
{
if (!m_interfaceEventHandlerOnSystem)
{
//
// Create interface event handler for the system
//
// *** NOTES ***
// The InterfaceEventHandler has been constructed to accept a system object in
// order to print the number of cameras on the system.
//
m_interfaceEventHandlerOnSystem =
shared_ptr<InterfaceEventHandlerImpl>(new InterfaceEventHandlerImpl(m_system));
}
//
// Register interface event handler for the system
//
// *** NOTES ***
// Arrival, removal, and interface event handlers can all be registered to
// interfaces or the system. Do not think that interface event handlers can only be
// registered to an interface. An interface event is merely a combination
// of an arrival and a removal event.
// Only arrival and removal events for GEV interfaces are currently supported.
//
// *** LATER ***
// Arrival, removal, and interface event handlers must all be unregistered manually.
// This must be done prior to releasing the system and while they are still
// in scope.
//
m_system->RegisterEventHandler(*m_interfaceEventHandlerOnSystem);
cout << "Interface event handler registered on the system..." << endl;
}
void UnregisterInterfaceEventFromSystem()
{
//
// Unregister interface event handler from system object
//
// *** NOTES ***
// It is important to unregister all arrival, removal, and interface event handlers
// registered to the system.
//
if (m_interfaceEventHandlerOnSystem)
{
m_system->UnregisterEventHandler(*m_interfaceEventHandlerOnSystem);
cout << "Interface event handler unregistered from system..." << endl;
m_interfaceEventHandlerOnSystem = shared_ptr<InterfaceEventHandlerImpl>();
}
}
void RegisterAllInterfaceEvents()
{
{
LockEventHandlerMutex();
if (!m_interfaceEventHandlers.empty())
{
m_interfaceEventHandlers.clear();
}
UnlockEventHandlerMutex();
}
InterfaceList interfaceList = m_system->GetInterfaces();
unsigned int numInterfaces = interfaceList.GetSize();
//
// Create and register interface event handler to each interface
//
// *** NOTES ***
// The process of event handler creation and registration on interfaces is similar
// to the process of event handler creation and registration on the system. The
// class for interfaces has been constructed to accept an interface and an
// interface number (this is just to separate the interfaces).
//
// *** LATER ***
// Arrival, removal, and interface event handlers must all be unregistered manually.
// This must be done prior to releasing the system and while they are still
// in scope.
//
for (unsigned int i = 0; i < numInterfaces; i++)
{
// Select interface
InterfacePtr pInterface = interfaceList.GetByIndex(i);
INodeMap& nodeMap = pInterface->GetTLNodeMap();
const CStringPtr interfaceIDNode = nodeMap.GetNode("InterfaceID");
// Ensure the node is valid
if (!IsReadable(interfaceIDNode))
{
continue;
}
std::string interfaceID = std::string(interfaceIDNode->GetValue().c_str());
{
LockEventHandlerMutex();
try
{
// Create interface event handler
shared_ptr<InterfaceEventHandlerImpl> interfaceEventHandler =
shared_ptr<InterfaceEventHandlerImpl>(new InterfaceEventHandlerImpl(pInterface, interfaceID));
m_interfaceEventHandlers.push_back(interfaceEventHandler);
// Register interface event handler
pInterface->RegisterEventHandler(*m_interfaceEventHandlers[i]);
cout << "Event handler registered to interface '" << interfaceID << "'..." << endl;
}
catch (exception& e)
{
cout << "Error registering event handler to interface '" << interfaceID << "' :" << e.what()
<< endl;
}
UnlockEventHandlerMutex();
}
}
cout << endl;
}
void UnregisterAllInterfaceEvents()
{
InterfaceList interfaceList = m_system->GetInterfaces(false);
unsigned int numInterfaces = interfaceList.GetSize();
//
// Unregister interface event handler from each interface
//
// *** NOTES ***
// It is important to unregister all arrival, removal, and interface event handlers
// from all interfaces that they may be registered to.
//
for (unsigned int i = 0; i < numInterfaces; i++)
{
// Select interface
InterfacePtr pInterface = interfaceList.GetByIndex(i);
INodeMap& nodeMap = pInterface->GetTLNodeMap();
const CStringPtr interfaceIDNode = nodeMap.GetNode("InterfaceID");
// Ensure the node is valid
if (!IsReadable(interfaceIDNode))
{
continue;
}
std::string interfaceID = std::string(interfaceIDNode->GetValue().c_str());
{
LockEventHandlerMutex();
try
{
// Ensure that that the event is unregistered from the matching interface
for (unsigned int j = 0; j < m_interfaceEventHandlers.size(); j++)
{
if (interfaceID == m_interfaceEventHandlers[j]->GetInterfaceId())
{
pInterface->UnregisterEventHandler(*m_interfaceEventHandlers[j]);
}
}
}
catch (exception& e)
{
cout << "Error unreigstering event handler to interface '" << interfaceID << "' :" << e.what()
<< endl;
}
UnlockEventHandlerMutex();
}
}
{
LockEventHandlerMutex();
m_interfaceEventHandlers.clear();
cout << "Event handler unregistered from interfaces..." << endl;
UnlockEventHandlerMutex();
}
}
private:
SystemPtr m_system;
shared_ptr<InterfaceEventHandlerImpl> m_interfaceEventHandlerOnSystem;
vector<shared_ptr<InterfaceEventHandlerImpl>> m_interfaceEventHandlers;
std::mutex eventHandlersMutex;
};
// This function checks if GEV enumeration is enabled on the system.
void CheckGevEnabled(SystemPtr& pSystem)
{
// Retrieve the System TL Nodemap and EnumerateGEVInterfaces node
INodeMap& nodeMap = pSystem->GetTLNodeMap();
const CBooleanPtr enumerateGevInterfacesNode = nodeMap.GetNode("EnumerateGEVInterfaces");
// Ensure the node is valid
if (IsReadable(enumerateGevInterfacesNode))
{
const bool gevEnabled = enumerateGevInterfacesNode->GetValue();
// Check if node is enabled
if (!gevEnabled)
{
cout << endl
<< "WARNING: GEV Enumeration is disabled." << endl
<< "If you intend to use GigE cameras please run the EnableGEVInterfaces shortcut" << endl
<< "or set EnumerateGEVInterfaces to true and relaunch your application." << endl
<< endl;
}
else
{
cout << "EnumerateGEVInterfaces is enabled. Continuing.." << endl;
}
}
else
{
cout << "EnumerateGEVInterfaces node is not readable" << endl;
}
}
// Example entry point; this function sets up the example to act appropriately
// upon arrival and removal events; please see Enumeration example for more
// in-depth comments on preparing and cleaning up the system.
int main(int /*argc*/, char** /*argv*/)
{
// Print application build information
cout << "Application build date: " << __DATE__ << " " << __TIME__ << endl << endl;
// Retrieve singleton reference to system object
SystemPtr system = System::GetInstance();
// Print out current library version
const LibraryVersion spinnakerLibraryVersion = system->GetLibraryVersion();
cout << "Spinnaker library version: " << spinnakerLibraryVersion.major << "." << spinnakerLibraryVersion.minor
<< "." << spinnakerLibraryVersion.type << "." << spinnakerLibraryVersion.build << endl
<< endl;
// Check if GEV enumeration is enabled.
CheckGevEnabled(system);
// Retrieve list of cameras from the system
CameraList camList = system->GetCameras();
unsigned int numCameras = camList.GetSize();
cout << "Number of cameras detected: " << numCameras << endl << endl;
//
// Retrieve list of interfaces from the system
//
// *** NOTES ***
// macOS interfaces are only registered if they are active.
// This example will have varied outcomes depending on the order
// in which we receive the interface and the device arrival events.
// In most cases on macOS, since the interface and the device arrive
// at the same time, we will see the interface arrival event and
// the interface will already have one device connected to it.
// This means we will not see another device arrival event.
// Device removal events however, are guaranteed to be fired before
// interface removal events.
//
InterfaceList interfaceList = system->GetInterfaces();
unsigned int numInterfaces = interfaceList.GetSize();
cout << "Number of interfaces detected: " << numInterfaces << endl << endl;
cout << endl << "*** CONFIGURING ENUMERATION EVENTS ***" << endl << endl;
//
// Create system event handler
//
// *** NOTES ***
// The SystemEventHandlerImpl has been constructed to accept a system object in
// order to register/unregister events to/from the system object
//
SystemEventHandlerImpl systemEventHandler(system);
//
// Register system event to the system
//
// *** NOTES ***
// A system event is merely a combination of an interface arrival and an
// interface removal event.
// This feature is currently only supported for GEV interface arrivals and removals.
//
// *** LATER ***
// Interface arrival and removal events must all be unregistered manually.
// This must be done prior to releasing the system and while they are still
// in scope.
//
system->RegisterEventHandler(systemEventHandler);
systemEventHandler.RegisterInterfaceEventToSystem();
systemEventHandler.RegisterAllInterfaceEvents();
// Wait for user to plug in and/or remove camera devices
cout << endl << "Ready! Remove/Plug in cameras to test or press Enter to exit..." << endl << endl;
getchar();
systemEventHandler.UnregisterAllInterfaceEvents();
systemEventHandler.UnregisterInterfaceEventFromSystem();
//
// Unregister system event handler from system object
//
// *** NOTES ***
// It is important to unregister all interface arrival and removal event handlers
// registered to the system.
//
system->UnregisterEventHandler(systemEventHandler);
cout << "System event handler unregistered from system..." << endl;
// Clear camera list before releasing system
camList.Clear();
// Clear interface list before releasing system
interfaceList.Clear();
// Release system
system->ReleaseInstance();
cout << endl << "Done! Press Enter to exit..." << endl;
getchar();
return 0;
}