Handling EVENT_NEW_BUFFER¶
You have learned how to handle events in the previous chapter. In this chapter, we demonstrate a more practical example: How about to learn how we can interact with EVENT_NEW_BUFFER
event?
The following class diagram shows the relationship between relevant classes. Note that relationship with the parent object classes of DataStream
class were intentionally omitted to save space in the diagram. The image is stretched following the browser size, or you could see it in the original size opening the image in another tab/window.
The event data of EVENT_NEW_BUFFER
can be accessed through EventManagerNewBuffer
class object. So let’s walk through the following steps to see how we can handle event data of EVENT_NEW_BUFFER
.
The following import statements are required for this tutorial.
from genicam.gentl import EventManagerNewBuffer, GenTLProducer, BufferToken, Port
from genicam.gentl import TimeoutException
from genicam.gentl import DEVICE_ACCESS_FLAGS_LIST, EVENT_TYPE_LIST, \
ACQ_START_FLAGS_LIST, ACQ_STOP_FLAGS_LIST, ACQ_QUEUE_TYPE_LIST
from genicam.genapi import NodeMap, AbstractPort, EAccessMode
First, instantiate a NodeMap
object of the target remote device like we did in a previous chapter.
concrete_port = ConcretePort(device.remote_port)
url_index = 0
url = device.remote_port.url_info_list[url_index].url
location, others = url.split(':', 1)
# Check if the URL contains a schema version.
if '?' in others:
others, schema = others.split('?')
file_name, address, size = others.split(';')
else:
file_name, address, size = others.split(';')
# Download the device description file content from the
# remote device.
address = int(address, 16)
size = int(size, 16)
file_content = device.remote_port.read(address, size)
# Instantiate a Node Map object and load the device description.
remote_device = NodeMap()
_, extension = os.path.splitext(file_name)
if '.zip' == extension.lower():
with open(file_name, 'wb') as zip_file:
zip_file.write(file_content[1])
remote_device.load_xml_from_zip_file(file_name)
zip_file.close()
else:
remote_device.load_xml_from_string(file_content[1])
remote_device.connect(concrete_port, device.remote_port.name)
Note that we used the following class deriving from AbstractPort
class of genapi
module.
class ConcretePort(AbstractPort):
def __init__(self, gentl_port=None):
super().__init__()
self._assign_port_impl(gentl_port)
def is_open(self):
return False if self._gentl_port is None else True
def write(self, address, value):
self._gentl_port.write(address, value)
def read(self, address, length):
buffer = self._gentl_port.read(address, length)
return buffer[1]
def open(self, gentl_port):
self._assign_port_impl(gentl_port)
def close(self):
self._gentl_port = None
def get_access_mode(self):
return EAccessMode.RW if self.is_open else EAccessMode.NA
def _assign_port_impl(self, obj):
# Check if the object is a GenTL Port module object.
if isinstance(obj, Port):
self._gentl_port = obj
else:
raise TypeError('Supplied object is not a GenTL Port object.')
In general having instantiated a NodeMap
object for your remote device you would set up the device through the object so that it fits to your application. After setting up the device, in other words if the device is ready for working on your image acquisition task, accessing data_stream_ids
property of the Device
class, instantiate a DataStream
object calling open_data_stream()
method. In the following example, it opens the first DataStream
object.
data_stream = device.create_data_stream()
data_stream.open(device.data_stream_ids[0])
Next, determine the minimum number of buffers to be prepared. You must prepare sufficient numbers of buffers anyway. Anyway!
num_buffers = data_stream.buffer_announce_min
So, let’s determine the buffer size. It must not be less than the required at least. If the DataStream
object defines payload size, you can determine the buffer size using its payload_size
property. If the DataStream
object defines the payload size, its defines_payload_size
property returns True
. If it doesn’t, the remote device would provide GenICam PayloadSize
node to let you know the required buffer size.
if data_stream.defines_payload_size():
buffer_size = data_stream.payload_size
else:
buffer_size = remote_device.PayloadSize.value
After that, instantiate a BufferToken
object. It’s used to announce a raw buffer and its tag data on the DataStream
object. The constructor of BufferToken
class takes bytes
object as a raw buffer where the transmitter device delivers images and an arbitrary object
object to tag it on the raw buffer; maybe it would be an int
object if we go on a simple case.
Instantiating a BufferToken
object, announce the raw buffer as the place where images are delivered. To announce a raw buffer, you call announce_buffer()
method of the DataStream
object. announce_buffer()
method takes a BufferToken
object as its parameter and the method returns a Buffer
object as its return. After announcing the raw buffer, you queue the Buffer
object to the acquisition engine calling queue_buffer()
method to get prepared for image acquisition.
raw_buffers = [] # is a list of raw buffers.
set_of_context = [] # is a list of user data.
buffer_tokens = [] # is a list of Buffer Token objects.
announced_buffers = [] # is a list of announced Buffer objects.
for i in range(num_buffers):
# Instantiate a Buffer Token object supplying a raw buffer and
# its user data as a tag.
raw_buffers.append(bytes(buffer_size))
set_of_context.append(i)
buffer_tokens.append(
BufferToken(
raw_buffers[i], set_of_context[i]
)
)
# Then announce the raw buffer (it's wrapped by the Buffer Token
# object. You'll get an announced Buffer class object.
announced_buffers.append(
data_stream.announce_buffer(buffer_tokens[i])
)
# Finally, queue the announced Buffer object to get prepared
# for image acquisition.
data_stream.queue_buffer(announced_buffers[i])
Then register an event calling register_event()
method of the DataStream
object and get an EventToken
object that the method returns. In this example, we register EVENT_NEW_BUFFER
event here. Then instantiate an EventManagerNewBuffer
object passing the returned EventToken
object. You have nothing to interact with EventToken
object. Everything will be fine if you just pass it to the constructor of EventManagerNewBuffer
class to have a way to access the event data later.
event_token = data_stream.register_event(EVENT_TYPE_LIST.EVENT_NEW_BUFFER)
event_manager = EventManagerNewBuffer(event_token)
Now we can start image acquisition and acquire 10 images.
num_images = 10
data_stream.start_acquisition(
ACQ_START_FLAGS_LIST.ACQ_START_FLAGS_DEFAULT,
num_images
)
remote_device.AcquisitionStart.execute()
If you want to keep acquisition until you call stop_acquisition()
method of DataStream
object, just omit passing the second parameter.
data_stream.start_acquisition(
ACQ_START_FLAGS_LIST.ACQ_START_FLAGS_DEFAULT
)
Here we start acquiring images and you would manipulate them. You can access the delivered event data over buffer
property of the EventManagerNewBuffer
object. The buffer
property returns a Buffer
object then you can read the newly delivered buffer content through raw_buffer
property and its user data through user_data
property of the Buffer
object. In addition, this is just for your information but note that the object that is returned from raw_buffer
property and the object that is returned from user_data
are identical.
i = 0
while i < num_images:
try:
# We have one or more events to process.
# Update the event data.
event_manager.update_event_data(3000)
except TimeoutException as e:
# Nothing happened during the timeout period.
print(e)
else:
# You would manipulate the buffer content having
# been supported by the tagged user data.
print(
'Buffer #{} has delivered image #{}. ' \
'The buffer size is {} Bytes.'.format(
event_manager.buffer.context,
i,
len(event_manager.buffer.raw_buffer)
)
)
# Queue the buffer again for the following image
# acquisition.
data_stream.queue_buffer(event_manager.buffer)
# Increments the index.
i += 1
# It's not necessary at all but let me demonstrate that
# the raw buffer returned from Buffer.raw_buffer property and
# the user data returned from Buffer.user_data property are
# identical to the ones you instantiated in the beginning.
assert event_manager.buffer.raw_buffer is \
raw_buffers[event_manager.buffer.context]
assert event_manager.buffer.context is \
set_of_context[event_manager.buffer.context]
# Note that the raw buffer is identical to the bytes object
# that you instantiated earlier.
Okay, we have finished to acquire the required number of images. Now we tear it down. First, stop acquisition and flush the event queue.
# Stop image acquisition. Note that TLSimu cannot announce or revoke
# during acquisition, stop before revoking.
data_stream.stop_acquisition(
ACQ_STOP_FLAGS_LIST.ACQ_STOP_FLAGS_KILL
)
data_stream.flush_buffer_queue(
ACQ_QUEUE_TYPE_LIST.ACQ_QUEUE_ALL_DISCARD
)
Here’s another tear down. Stop acquisition and stop monitoring EVENT_NEW_BUFFER
event. Then revoke the announced raw buffers calling revoke_buffer()
method.
# Unregister the registered event.
event_manager.unregister_event()
# Revoke the buffers.
# Stop image acquisition on the transmitter side.
remote_device.AcquisitionStop.execute()
for i in range(num_buffers):
# You may have something to do with the revoked user data.
buffer_token = data_stream.revoke_buffer(announced_buffers[i])
print(
'Buffer #{0} has been revoked.'.format(buffer_token.context)
)
That’s all. We hope you have got feeling of handling events with the GenTL-Python Binding.