Handling changes¶
Interaction with a device is not always unidirectional. In some cases, an application may need to react to changes or events occurring in the camera. For example, users might want to monitor the device’s temperature during an acquisition or receive a notification when an exposure has finished.
This functionality is exposed on the device nodemap and can be configured via GenApi, which provides callbacks to detect and react to node changes.
Registering a callback¶
The most direct way to react to a change is to register a callback function to a specific node. This function will be executed automatically whenever the node changes.
The following examples show how to register a callback that prints the new value of the DeviceTemperature node whenever it changes.
// Define a callback function to be called by GenApi
void OnDeviceTemperatureChange(GenApi::INode* pNode)
{
GenApi::CFloatPtr pDeviceTemperature = pNode;
std::cout << "Device temperature is " << pDeviceTemperature->GetValue() << std::endl;
}
// Get the desired node
GenApi::CNodePtr pDeviceTemperature =
pDevice->GetNodeMap().GetNode("DeviceTemperature");
// Register the callback to the node
GenApi::CallbackHandleType hCallback =
GenApi::Register(pDeviceTemperature, OnDeviceTemperatureChange);
// ...
// Deregister the callback when done
GenApi::Deregister(hCallback);
// Define a callback function to be called by GenApi
void OnDeviceTemperatureChanged(H_NODE hNode, void* pContext)
{
double value = 0;
NODE_FloatGetValue(hNode, &value);
printf("\tDevice temperature node changed. New value is %f C\n", value);
}
// Get the desired node
H_NODE hNodeDeviceTemperature = NULL;
NODEMAP_GetNode(hNodeMap, "DeviceTemperature", &hNodeDeviceTemperature);
// Register the callback and store its handle
H_NODECALLBACK hNodeCallback = NULL;
NODE_RegisterCallback(hNodeDeviceTemperature, OnDeviceTemperatureChanged, &hNodeCallback);
// ...
// Deregister the callback when done
NODE_DeregisterCallback(hNodeDeviceTemperature, hNodeCallback);
// Define a function to be called when the node changes
NodeChangedEventHandler onDeviceTemperatureChanged = (GenApi.INode node) =>
{
var deviceTemperature = node.TryGetAs<GenApi.IFloat>();
if(deviceTemperature != null)
{
Console.WriteLine("Device temperature is " + deviceTemperature.Value);
}
};
// Get the desired node
GenApi.INode deviceTemperature = device.GetNodeMap().GetNode("DeviceTemperature");
// Register the function to the node's event
deviceTemperature.NodeChanged += onDeviceTemperatureChanged;
// ...
// Deregister the function when done
deviceTemperature.NodeChanged -= onDeviceTemperatureChanged;
// Define a function to be called when the node changes
NodeChangedEventHandler onDeviceTemperatureChanged = (GenApi.INode node) =>
{
var deviceTemperature = node.TryGetAs<GenApi.IFloat>();
if(deviceTemperature != null)
{
Console.WriteLine("Device temperature is " + deviceTemperature.Value);
}
};
// Get the desired node
GenApi.INode deviceTemperature = device.GetNodeMap().GetNode("DeviceTemperature");
// Register the function to the node's event
deviceTemperature.NodeChanged += onDeviceTemperatureChanged;
// ...
// Deregister the function when done
deviceTemperature.NodeChanged -= onDeviceTemperatureChanged;
# Define a callback function to be called by GenApi
def on_device_temperature_change(node):
device_temperature = itala.FloatNode(node)
print(f"Device temperature is {device_temperature.value}")
# Get the desired node
device_temperature_node = device.node_map.DeviceTemperature
# Register the callback to the node
callback_handle = itala.register(device_temperature_node.node, on_device_temperature_change)
# ...
# Deregister the callback when done
itala.deregister(callback_handle)
Why nodes change¶
Node values can change for several reasons. Understanding the mechanism behind the change is key to handling it correctly.
Invalidation¶
Some features are linked to others. A classic example is the PayloadSize feature. If the camera’s pixel format changes (e.g., from Mono8 to Mono12p), the size of the image payload changes accordingly. This means the PayloadSize feature is invalidated by the pixel format change and should be read again. By registering a callback to the PayloadSize node, user applications can react to this change automatically.
Polling¶
The value of some read-only nodes, like DeviceTemperature, is not directly tied to other features. If the temperature changes inside the camera, users must manually poll the nodemap to see the updated value.
Every “pollable” feature has a specific polling time defined by the manufacturer. When users call the polling function on the nodemap, they must specify the time elapsed since the last poll. GenApi will automatically read and update every feature with a polling time shorter than the elapsed time, triggering any registered callback.
// Poll every 500ms
deviceNodemap.Poll(500);
// Poll every 500ms
NODEMAP_Poll(hNodeMap, 500);
// Poll every 500ms
device.GetNodeMap().Poll(500);
# Poll every 1000ms
device.node_map.poll(1000)
Events¶
Events allow signaling from the device to the user application. Each event is represented by a dedicated feature on the device nodemap (e.g., EventExposureEnd), often with related features that provide more data (e.g., EventExposureEndTimestamp).
When a configured event occurs, its related features are automatically updated by an internal event-listening thread in the ItalaApi.
Note
Event feature names and types are standardized by the SFNC (Standard Feature Naming Convention). Please refer to the official SFNC specification to gather information about a particular event. For more details, refer to the official GenICam documentation, which is freely accessible from the GenICam Introduction page.
To use events, users must enable them on the device and also in ItalaApi, so that it can start listening for events coming from that device.
1. Configure and Enable Events
// === Enable events ===
// Select the ExposureEnd event and enable notifications on the device
GenApi::CEnumerationPtr pEventSelector = pDevice->GetNodeMap().GetNode("EventSelector");
pEventSelector->FromString("ExposureEnd");
GenApi::CEnumerationPtr pEventNotification = pDevice->GetNodeMap().GetNode("EventNotification");
pEventNotification->FromString("On");
// Enable the event listening functionality on the host
pDevice->EnableEvents();
// ...
// === Disable events ===
pEventSelector->FromString("ExposureEnd");
pEventNotification->FromString("Off");
pDevice->DisableEvents();
// === Enable events ===
// Select the ExposureEnd event and enable notifications on the device
NODEMAP_GetNode(hNodeMap, "EventSelector", &hNodeEventSelector);
NODE_FromString(hNodeEventSelector, "ExposureEnd");
NODEMAP_GetNode(hNodeMap, "EventNotification", &hNodeEventNotification);
NODE_FromString(hNodeEventNotification, "On");
// Enable the event listening functionality on the host
DEV_EnableEvents(hDevice);
// ...
// === Disable events ===
NODE_FromString(hNodeEventSelector, "ExposureEnd");
NODE_FromString(hNodeEventNotification, "Off");
DEV_DisableEvents(hDevice);
// === Enable events ===
// Select the ExposureEnd event and enable notifications on the device
var eventSelector = device.GetNodeMap().GetNode<GenApi.IEnumeration>("EventSelector");
eventSelector.FromString("ExposureEnd");
var eventNotification = device.GetNodeMap().GetNode<GenApi.IEnumeration>("EventNotification");
eventNotification.FromString("On");
// Enable the event listening functionality on the host
device.EnableEvents();
// ...
// === Disable events ===
eventSelector.FromString("ExposureEnd");
eventNotification.FromString("Off");
device.DisableEvents();
# === Enable events ===
# Select the ExposureEnd event and enable notifications on the device
event_selector = device.node_map.EventSelector
event_selector.from_string("ExposureEnd")
event_notification = device.node_map.EventNotification
event_notification.from_string("On")
# Enable the event listening functionality on the host
device.enable_events()
# ...
# === Disable events ===
event_selector.from_string("ExposureEnd")
event_notification.from_string("Off")
device.disable_events()
2. Alternative: Using a Device Event Handler
As an alternative to GenApi node callbacks, users can implement a dedicated event handler. This provides a centralized place to handle all device events. Note that they still need to configure and enable the events on the device and host as shown above.
// Implement a custom handler
class ExposureEndEventHandler : public Itala::DeviceEventHandler
{
public:
ExposureEndEventHandler(Itala::IDevice* pDevice) : m_pDevice(pDevice) { }
void OnDeviceEvent(uint64_t eventId) override
{
GenApi::CIntegerPtr pEventExposureEnd = m_pDevice->GetNodeMap().GetNode("EventExposureEnd");
if (eventId == pEventExposureEnd->GetValue())
{
GenApi::CIntegerPtr pTimestamp = m_pDevice->GetNodeMap().GetNode("EventExposureEndTimestamp");
std::cout << "Handler detected ExposureEnd event. Timestamp: " << pTimestamp->GetValue() << std::endl;
}
}
private:
Itala::IDevice* m_pDevice;
};
// Instantiate and register the handler
ExposureEndEventHandler expEndHandler(pDevice);
pDevice->RegisterHandler(&expEndHandler);
// ...
// Deregister the handler
pDevice->DeregisterHandler();
// Implement the callback function
DeviceEventCallback callbackDeviceEvent = (IDevice sender, ulong eventId) =>
{
var eventExposureEnd = sender.GetNodeMap().GetNode<GenApi.IInteger>("EventExposureEnd");
if (eventExposureEnd.Value == (long)eventId)
{
var timestamp = sender.GetNodeMap().GetNode<GenApi.IInteger>("EventExposureEndTimestamp").Value;
Console.WriteLine($"Handler detected ExposureEnd event. Timestamp: {timestamp}");
}
};
// Register the function to the event
device.DeviceEventCallback += callbackDeviceEvent;
// ...
// Deregister the function
device.DeviceEventCallback -= callbackDeviceEvent;
# Implement a custom handler
class MyEventHandler(itala.DeviceEventHandler):
def __init__(self, device):
super().__init__()
self.device = device
def on_device_event(self, event_id):
nodemap = self.device.node_map
event_exposure_end_node = nodemap.EventExposureEnd
if event_id == event_exposure_end_node.value:
timestamp_node = nodemap.EventExposureEndTimestamp
print(f"Handler detected ExposureEnd event. Timestamp: {timestamp_node.value}")
# Instantiate and register the handler
my_handler = MyEventHandler(device)
device.register_handler(my_handler)
# ...
# Deregister the handler
device.deregister_handler()