Skip to content

Commit

Permalink
Support multiple "on parameter set" callbacks (#457)
Browse files Browse the repository at this point in the history
Addresses #393

Signed-off-by: Jacob Perron <[email protected]>
Signed-off-by: AbhinavSingh <[email protected]>
  • Loading branch information
suab321321 authored Feb 5, 2020
1 parent c01a0ee commit 822f194
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 9 deletions.
45 changes: 37 additions & 8 deletions rclpy/rclpy/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ def __init__(
self.__guards: List[GuardCondition] = []
self.__waitables: List[Waitable] = []
self._default_callback_group = MutuallyExclusiveCallbackGroup()
self._parameters_callbacks: List[Callable[[List[Parameter]], SetParametersResult]] = []
self._rate_group = ReentrantCallbackGroup()
self._parameters_callback = None
self._allow_undeclared_parameters = allow_undeclared_parameters
self._parameter_overrides = {}
self._descriptors = {}
Expand Down Expand Up @@ -525,7 +525,7 @@ def get_parameter_or(

return self._parameters.get(name, alternative_value)

def get_parameters_by_prefix(self, prefix: str) -> Dict[str, Parameter]:
def get_parameters_by_prefix(self, prefix: str) -> List[Parameter]:
"""
Get parameters that have a given prefix in their names as a dictionary.
Expand Down Expand Up @@ -721,10 +721,12 @@ def _set_parameters_atomically(

if not result.successful:
return result
elif self._parameters_callback:
result = self._parameters_callback(parameter_list)
else:
result = SetParametersResult(successful=True)
elif self._parameters_callbacks:
for callback in self._parameters_callbacks:
result = callback(parameter_list)
if not result.successful:
return result
result = SetParametersResult(successful=True)

if result.successful:
parameter_event = ParameterEvent()
Expand Down Expand Up @@ -768,6 +770,33 @@ def _set_parameters_atomically(

return result

def add_on_set_parameters_callback(
self,
callback: Callable[[List[Parameter]], SetParametersResult]
) -> None:
"""
Add a callback in front to the list of callbacks.
Calling this function will add a callback in self._parameter_callbacks list.
:param callback: The function that is called whenever parameters are set for the node.
"""
self._parameters_callbacks.insert(0, callback)

def remove_on_set_parameters_callback(
self,
callback: Callable[[List[Parameter]], SetParametersResult]
) -> None:
"""
Remove a callback from list of callbacks.
Calling this function will remove the callback from self._parameter_callbacks list.
:param callback: The function that is called whenever parameters are set for the node.
:raises: ValueError if a callback is not present in the list of callbacks.
"""
self._parameters_callbacks.remove(callback)

def _apply_descriptors(
self,
parameter_list: List[Parameter],
Expand Down Expand Up @@ -1026,11 +1055,11 @@ def set_parameters_callback(
"""
Register a set parameters callback.
Calling this function with override any previously registered callback.
Calling this function will add a callback to the self._parameter_callbacks list.
:param callback: The function that is called whenever parameters are set for the node.
"""
self._parameters_callback = callback
self._parameters_callbacks = [callback]

def _validate_topic_or_service_name(self, topic_or_service_name, *, is_service=False):
name = self.get_name()
Expand Down
130 changes: 129 additions & 1 deletion rclpy/test/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,11 @@ def reject_parameter_callback(self, parameter_list):
rejected_parameters = (param for param in parameter_list if 'reject' in param.name)
return SetParametersResult(successful=(not any(rejected_parameters)))

def reject_parameter_callback_1(self, parameter_list):
rejected_parameters = (
param for param in parameter_list if 'refuse' in param.name)
return SetParametersResult(successful=(not any(rejected_parameters)))

def test_use_sim_time(self):
self.assertTrue(self.node.has_parameter(USE_SIM_TIME_NAME))
self.assertFalse(self.node.get_parameter(USE_SIM_TIME_NAME).value)
Expand Down Expand Up @@ -823,7 +828,6 @@ def test_node_set_parameters_rejection(self):
True,
ParameterDescriptor()
)

self.node.declare_parameter(*reject_parameter_tuple)
self.node.set_parameters_callback(self.reject_parameter_callback)
result = self.node.set_parameters(
Expand All @@ -838,6 +842,130 @@ def test_node_set_parameters_rejection(self):
self.assertIsInstance(result[0], SetParametersResult)
self.assertFalse(result[0].successful)

def test_node_set_parameters_rejection_list(self):
# Declare a new parameters and set a list of callbacks so that it's rejected when set.
reject_list_parameter_tuple = [
('reject', True, ParameterDescriptor()),
('accept', True, ParameterDescriptor()),
('accept', True, ParameterDescriptor())
]
self.node.declare_parameters('', reject_list_parameter_tuple)
self.node.add_on_set_parameters_callback(self.reject_parameter_callback)
self.node.add_on_set_parameters_callback(self.reject_parameter_callback_1)

result = self.node.set_parameters(
[
Parameter(
name=reject_list_parameter_tuple[0][0],
value=reject_list_parameter_tuple[0][1]
),
Parameter(
name=reject_list_parameter_tuple[1][0],
value=reject_list_parameter_tuple[1][1]
),
Parameter(
name=reject_list_parameter_tuple[2][0],
value=reject_list_parameter_tuple[2][1]
)
]
)
self.assertEqual(3, len(result))
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], SetParametersResult)
self.assertFalse(result[0].successful)
self.assertIsInstance(result[1], SetParametersResult)
self.assertTrue(result[1].successful)
self.assertIsInstance(result[2], SetParametersResult)
self.assertTrue(result[2].successful)

def test_node_add_on_set_parameter_callback(self):
# Add callbacks to the list of callbacks.
callbacks = [
self.reject_parameter_callback,
self.reject_parameter_callback_1
]
for callback in callbacks:
self.node.add_on_set_parameters_callback(callback)
for callback in callbacks:
self.assertTrue(callback in self.node._parameters_callbacks)
for callback in callbacks:
self.node.remove_on_set_parameters_callback(callback)

# Adding the parameters which will be accepted without any rejections.
non_reject_parameter_tuple = [
('accept_1', True, ParameterDescriptor()),
('accept_2', True, ParameterDescriptor())
]
self.node.declare_parameters('', non_reject_parameter_tuple)

for callback in callbacks:
self.node.add_on_set_parameters_callback(callback)

result = self.node.set_parameters(
[
Parameter(
name=non_reject_parameter_tuple[0][0],
value=non_reject_parameter_tuple[0][1]
),
Parameter(
name=non_reject_parameter_tuple[1][0],
value=non_reject_parameter_tuple[1][1]
)
]
)
self.assertEqual(2, len(result))
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], SetParametersResult)
self.assertTrue(result[0].successful)
self.assertIsInstance(result[1], SetParametersResult)
self.assertTrue(result[1].successful)

def test_node_remove_from_set_callback(self):
# Remove callbacks from list of callbacks.
parameter_tuple = (
'refuse', True, ParameterDescriptor()
)
self.node.declare_parameter(*parameter_tuple)

callbacks = [
self.reject_parameter_callback_1,
]
# Checking if the callbacks are not already present.
for callback in callbacks:
self.assertFalse(callback in self.node._parameters_callbacks)

for callback in callbacks:
self.node.add_on_set_parameters_callback(callback)

result = self.node.set_parameters(
[
Parameter(
name=parameter_tuple[0],
value=parameter_tuple[1]
)
]
)
self.assertEqual(1, len(result))
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], SetParametersResult)
self.assertFalse(result[0].successful)
# Removing the callback which is causing the rejection.
self.node.remove_on_set_parameters_callback(self.reject_parameter_callback_1)
self.assertFalse(self.reject_parameter_callback_1 in self.node._parameters_callbacks)
# Now the setting its value again.
result = self.node.set_parameters(
[
Parameter(
name=parameter_tuple[0],
value=parameter_tuple[1]
)
]
)
self.assertEqual(1, len(result))
self.assertIsInstance(result, list)
self.assertIsInstance(result[0], SetParametersResult)
self.assertTrue(result[0].successful)

def test_node_set_parameters_read_only(self):
integer_value = 42
string_value = 'hello'
Expand Down

0 comments on commit 822f194

Please sign in to comment.