diff --git a/rclpy/rclpy/qos_event.py b/rclpy/rclpy/qos_event.py index b8a1b1f1f..9b574e173 100644 --- a/rclpy/rclpy/qos_event.py +++ b/rclpy/rclpy/qos_event.py @@ -49,6 +49,7 @@ class QoSSubscriptionEventType(IntEnum): RCL_SUBSCRIPTION_LIVELINESS_CHANGED = 1 RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS = 2 + """ Payload type for Subscription Deadline callback. diff --git a/rclpy/src/rclpy/_rclpy_qos_event.c b/rclpy/src/rclpy/_rclpy_qos_event.c index d95e695a4..0cc258b8e 100644 --- a/rclpy/src/rclpy/_rclpy_qos_event.c +++ b/rclpy/src/rclpy/_rclpy_qos_event.c @@ -165,10 +165,10 @@ _requested_incompatible_qos_to_py_object(_qos_event_callback_data_t * data) { rmw_requested_incompatible_qos_status_t * actual_data = &data->requested_incompatible_qos; PyObject * args = Py_BuildValue( - "iii", - actual_data->total_count, - actual_data->total_count_change, - actual_data->last_policy_id); + "iii", + actual_data->total_count, + actual_data->total_count_change, + actual_data->last_policy_id); if (!args) { return NULL; } @@ -211,10 +211,10 @@ _offered_incompatible_qos_to_py_object(_qos_event_callback_data_t * data) { rmw_offered_incompatible_qos_status_t * actual_data = &data->offered_incompatible_qos; PyObject * args = Py_BuildValue( - "iii", - actual_data->total_count, - actual_data->total_count_change, - actual_data->last_policy_id); + "iii", + actual_data->total_count, + actual_data->total_count_change, + actual_data->last_policy_id); if (!args) { return NULL; } diff --git a/rclpy/test/test_qos_event.py b/rclpy/test/test_qos_event.py index 88ad7c1be..c0ac86e2e 100644 --- a/rclpy/test/test_qos_event.py +++ b/rclpy/test/test_qos_event.py @@ -22,8 +22,10 @@ from rclpy.qos_event import QoSLivelinessChangedInfo from rclpy.qos_event import QoSLivelinessLostInfo from rclpy.qos_event import QoSOfferedDeadlineMissedInfo +from rclpy.qos_event import QoSOfferedIncompatibleQoSInfo from rclpy.qos_event import QoSPublisherEventType from rclpy.qos_event import QoSRequestedDeadlineMissedInfo +from rclpy.qos_event import QoSRequestedIncompatibleQoSInfo from rclpy.qos_event import QoSSubscriptionEventType from rclpy.qos_event import SubscriptionEventCallbacks @@ -47,6 +49,7 @@ def test_publisher_constructor(self): callbacks = PublisherEventCallbacks() liveliness_callback = Mock() deadline_callback = Mock() + incompatible_qos_callback = Mock() # No arg publisher = self.node.create_publisher(EmptyMsg, 'test_topic', 10) @@ -66,18 +69,26 @@ def test_publisher_constructor(self): self.assertEqual(len(publisher.event_handlers), 1) self.node.destroy_publisher(publisher) - # Arg with both callbacks + # Arg with two callbacks callbacks.liveliness = liveliness_callback publisher = self.node.create_publisher( EmptyMsg, 'test_topic', 10, event_callbacks=callbacks) self.assertEqual(len(publisher.event_handlers), 2) self.node.destroy_publisher(publisher) + # Arg with three callbacks + callbacks.incompatible_qos = incompatible_qos_callback + publisher = self.node.create_publisher( + EmptyMsg, 'test_topic', 10, event_callbacks=callbacks) + self.assertEqual(len(publisher.event_handlers), 3) + self.node.destroy_publisher(publisher) + def test_subscription_constructor(self): callbacks = SubscriptionEventCallbacks() liveliness_callback = Mock() deadline_callback = Mock() message_callback = Mock() + incompatible_qos_callback = Mock() # No arg subscription = self.node.create_subscription(EmptyMsg, 'test_topic', message_callback, 10) @@ -97,13 +108,20 @@ def test_subscription_constructor(self): self.assertEqual(len(subscription.event_handlers), 1) self.node.destroy_subscription(subscription) - # Arg with both callbacks + # Arg with two callbacks callbacks.liveliness = liveliness_callback subscription = self.node.create_subscription( EmptyMsg, 'test_topic', message_callback, 10, event_callbacks=callbacks) self.assertEqual(len(subscription.event_handlers), 2) self.node.destroy_subscription(subscription) + # Arg with three callbacks + callbacks.incompatible_qos = incompatible_qos_callback + subscription = self.node.create_subscription( + EmptyMsg, 'test_topic', message_callback, 10, event_callbacks=callbacks) + self.assertEqual(len(subscription.event_handlers), 3) + self.node.destroy_subscription(subscription) + def _create_event_handle(self, parent_entity, event_type): with parent_entity.handle as parent_capsule: event_capsule = _rclpy.rclpy_create_event(event_type, parent_capsule) @@ -120,6 +138,8 @@ def test_publisher_event_create_destroy(self): publisher, QoSPublisherEventType.RCL_PUBLISHER_OFFERED_DEADLINE_MISSED) self._do_create_destroy( publisher, QoSPublisherEventType.RCL_PUBLISHER_LIVELINESS_LOST) + self._do_create_destroy( + publisher, QoSPublisherEventType.RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS) self.node.destroy_publisher(publisher) def test_subscription_event_create_destroy(self): @@ -129,6 +149,8 @@ def test_subscription_event_create_destroy(self): subscription, QoSSubscriptionEventType.RCL_SUBSCRIPTION_LIVELINESS_CHANGED) self._do_create_destroy( subscription, QoSSubscriptionEventType.RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED) + self._do_create_destroy( + subscription, QoSSubscriptionEventType.RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS) self.node.destroy_subscription(subscription) def test_call_publisher_rclpy_event_apis(self): @@ -136,7 +158,7 @@ def test_call_publisher_rclpy_event_apis(self): # Make no assumptions about being able to actually receive the events publisher = self.node.create_publisher(EmptyMsg, 'test_topic', 10) wait_set = _rclpy.rclpy_get_zero_initialized_wait_set() - _rclpy.rclpy_wait_set_init(wait_set, 0, 0, 0, 0, 0, 2, self.context.handle) + _rclpy.rclpy_wait_set_init(wait_set, 0, 0, 0, 0, 0, 3, self.context.handle) deadline_event_handle = self._create_event_handle( publisher, QoSPublisherEventType.RCL_PUBLISHER_OFFERED_DEADLINE_MISSED) @@ -150,11 +172,20 @@ def test_call_publisher_rclpy_event_apis(self): liveliness_event_index = _rclpy.rclpy_wait_set_add_entity('event', wait_set, capsule) self.assertIsNotNone(liveliness_event_index) + incompatible_qos_event_handle = self._create_event_handle( + publisher, QoSPublisherEventType.RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS) + with incompatible_qos_event_handle as capsule: + incompatible_qos_event_index = _rclpy.rclpy_wait_set_add_entity( + 'event', wait_set, capsule) + self.assertIsNotNone(incompatible_qos_event_index) + # We live in our own namespace and have created no other participants, so # there can't be any of these events. _rclpy.rclpy_wait(wait_set, 0) self.assertFalse(_rclpy.rclpy_wait_set_is_ready('event', wait_set, deadline_event_index)) self.assertFalse(_rclpy.rclpy_wait_set_is_ready('event', wait_set, liveliness_event_index)) + self.assertFalse(_rclpy.rclpy_wait_set_is_ready( + 'event', wait_set, incompatible_qos_event_index)) # Calling take data even though not ready should provide me an empty initialized message # Tests data conversion utilities in C side @@ -182,6 +213,20 @@ def test_call_publisher_rclpy_event_apis(self): except NotImplementedError: pass + try: + with incompatible_qos_event_handle as event_capsule, \ + publisher.handle as publisher_capsule: + event_data = _rclpy.rclpy_take_event( + event_capsule, + publisher_capsule, + QoSPublisherEventType.RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS) + self.assertIsInstance(event_data, QoSOfferedIncompatibleQoSInfo) + self.assertEqual(event_data.total_count, 0) + self.assertEqual(event_data.total_count_change, 0) + self.assertEqual(event_data.last_policy_id, 0) + except NotImplementedError: + pass + self.node.destroy_publisher(publisher) def test_call_subscription_rclpy_event_apis(self): @@ -189,7 +234,7 @@ def test_call_subscription_rclpy_event_apis(self): # Make no assumptions about being able to actually receive the events subscription = self.node.create_subscription(EmptyMsg, 'test_topic', Mock(), 10) wait_set = _rclpy.rclpy_get_zero_initialized_wait_set() - _rclpy.rclpy_wait_set_init(wait_set, 0, 0, 0, 0, 0, 2, self.context.handle) + _rclpy.rclpy_wait_set_init(wait_set, 0, 0, 0, 0, 0, 3, self.context.handle) deadline_event_handle = self._create_event_handle( subscription, QoSSubscriptionEventType.RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED) @@ -203,11 +248,20 @@ def test_call_subscription_rclpy_event_apis(self): liveliness_event_index = _rclpy.rclpy_wait_set_add_entity('event', wait_set, capsule) self.assertIsNotNone(liveliness_event_index) + incompatible_qos_event_handle = self._create_event_handle( + subscription, QoSSubscriptionEventType.RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS) + with incompatible_qos_event_handle as capsule: + incompatible_qos_event_index = _rclpy.rclpy_wait_set_add_entity( + 'event', wait_set, capsule) + self.assertIsNotNone(incompatible_qos_event_index) + # We live in our own namespace and have created no other participants, so # there can't be any of these events. _rclpy.rclpy_wait(wait_set, 0) self.assertFalse(_rclpy.rclpy_wait_set_is_ready('event', wait_set, deadline_event_index)) self.assertFalse(_rclpy.rclpy_wait_set_is_ready('event', wait_set, liveliness_event_index)) + self.assertFalse(_rclpy.rclpy_wait_set_is_ready( + 'event', wait_set, incompatible_qos_event_index)) # Calling take data even though not ready should provide me an empty initialized message # Tests data conversion utilities in C side @@ -237,4 +291,18 @@ def test_call_subscription_rclpy_event_apis(self): except NotImplementedError: pass + try: + with incompatible_qos_event_handle as event_capsule, \ + subscription.handle as parent_capsule: + event_data = _rclpy.rclpy_take_event( + event_capsule, + parent_capsule, + QoSSubscriptionEventType.RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS) + self.assertIsInstance(event_data, QoSRequestedIncompatibleQoSInfo) + self.assertEqual(event_data.total_count, 0) + self.assertEqual(event_data.total_count_change, 0) + self.assertEqual(event_data.last_policy_id, 0) + except NotImplementedError: + pass + self.node.destroy_subscription(subscription)