def call_async(self, request: SrvTypeRequest) -> Future: """ Make a service request and asyncronously get the result. :param request: The service request. :return: A future that completes when the request does. :raises: TypeError if the type of the passed request isn't an instance of the Request type of the provided service when the client was constructed. """ if not isinstance(request, self.srv_type.Request): raise TypeError() with self._lock: with self.handle: sequence_number = self.__client.send_request(request) if sequence_number in self._pending_requests: raise RuntimeError( f'Sequence ({sequence_number}) conflicts with pending request' ) future = Future() self._pending_requests[sequence_number] = future future.add_done_callback(self.remove_pending_request) return future
def _get_result_async(self, goal_handle): """ Request the result for an active goal asynchronously. :param goal_handle: Handle to the goal to cancel. :type goal_handle: :class:`ClientGoalHandle` :return: a Future instance that completes when the get result request has been processed. :rtype: :class:`rclpy.task.Future` instance """ if not isinstance(goal_handle, ClientGoalHandle): raise TypeError( 'Expected type ClientGoalHandle but received {}'.format(type(goal_handle))) result_request = self._action_type.Impl.GetResultService.Request() result_request.goal_id = goal_handle.goal_id sequence_number = _rclpy_action.rclpy_action_send_result_request( self._client_handle, result_request) if sequence_number in self._pending_result_requests: raise RuntimeError( 'Sequence ({}) conflicts with pending result request'.format(sequence_number)) future = Future() self._pending_result_requests[sequence_number] = future future.add_done_callback(self._remove_pending_result_request) # Add future so executor is aware self.add_future(future) return future
def spin_until_future_complete(self, future: Future, timeout_sec: float = None) -> None: """Execute callbacks until a given future is done or a timeout occurs.""" # Make sure the future wakes this executor when it is done future.add_done_callback(lambda x: self.wake()) if timeout_sec is None or timeout_sec < 0: while self._context.ok( ) and not future.done() and not self._is_shutdown: self.spin_once_until_future_complete(future, timeout_sec) else: start = time.monotonic() end = start + timeout_sec timeout_left = timeout_sec while self._context.ok( ) and not future.done() and not self._is_shutdown: self.spin_once_until_future_complete(future, timeout_left) now = time.monotonic() if now >= end: return timeout_left = end - now
def _cancel_goal_async(self, goal_handle): """ Send a cancel request for an active goal and asynchronously get the result. :param goal_handle: Handle to the goal to cancel. :type goal_handle: :class:`ClientGoalHandle` :return: a Future instance that completes when the cancel request has been processed. :rtype: :class:`rclpy.task.Future` instance """ if not isinstance(goal_handle, ClientGoalHandle): raise TypeError( 'Expected type ClientGoalHandle but received {}'.format( type(goal_handle))) cancel_request = CancelGoal.Request() cancel_request.goal_info.goal_id = goal_handle.goal_id sequence_number = self._client_handle.send_cancel_request( cancel_request) if sequence_number in self._pending_cancel_requests: raise RuntimeError( 'Sequence ({}) conflicts with pending cancel request'.format( sequence_number)) future = Future() self._pending_cancel_requests[sequence_number] = future future.add_done_callback(self._remove_pending_cancel_request) # Add future so executor is aware self.add_future(future) return future
def test_add_done_callback_invokes_callback(self): called = False def cb(fut): nonlocal called called = True f = Future() f.set_result('Anything') f.add_done_callback(cb) assert called
def test_set_exception_invokes_callbacks(self): called = False def cb(fut): nonlocal called called = True f = Future() f.add_done_callback(cb) f.set_exception('Anything') assert called
def test_cancel_invokes_callbacks(self): called = False def cb(fut): nonlocal called called = True f = Future() f.add_done_callback(cb) f.cancel() assert called
def main(args=None): rclpy.init(args=args) node = SkibotNode() executor = MultiThreadedExecutor(num_threads=4) executor.add_node(node) future = Future() future.add_done_callback(lambda fut: print("Future is done")) executor.spin_until_future_complete(future) node.destroy_node() rclpy.shutdown()
def send_goal_async(self, goal, feedback_callback=None, goal_uuid=None): """ Send a goal and asynchronously get the result. The result of the returned Future is set to a ClientGoalHandle when receipt of the goal is acknowledged by an action server. :param goal: The goal request. :type goal: action_type.Goal :param feedback_callback: Callback function for feedback associated with the goal. :type feedback_callback: function :param goal_uuid: Universally unique identifier for the goal. If None, then a random UUID is generated. :type: unique_identifier_msgs.UUID :return: a Future instance to a goal handle that completes when the goal request has been accepted or rejected. :rtype: :class:`rclpy.task.Future` instance :raises: TypeError if the type of the passed goal isn't an instance of the Goal type of the provided action when the service was constructed. """ if not isinstance(goal, self._action_type.Goal): raise TypeError() request = self._action_type.Impl.SendGoalService.Request() request.goal_id = self._generate_random_uuid( ) if goal_uuid is None else goal_uuid request.goal = goal sequence_number = _rclpy_action.rclpy_action_send_goal_request( self._client_handle, request) if sequence_number in self._pending_goal_requests: raise RuntimeError( 'Sequence ({}) conflicts with pending goal request'.format( sequence_number)) if feedback_callback is not None: # TODO(jacobperron): Move conversion function to a general-use package goal_uuid = bytes(request.goal_id.uuid) self._feedback_callbacks[goal_uuid] = feedback_callback future = Future() self._pending_goal_requests[sequence_number] = future self._sequence_number_to_goal_id[sequence_number] = request.goal_id future.add_done_callback(self._remove_pending_goal_request) # Add future so executor is aware self.add_future(future) return future
def call_async(self, req): """ Make a service request and asyncronously get the result. :return: a Future instance that completes when the request does :rtype: :class:`rclpy.task.Future` instance """ sequence_number = _rclpy.rclpy_send_request(self.client_handle, req) if sequence_number in self._pending_requests: raise RuntimeError('Sequence (%r) conflicts with pending request' % sequence_number) future = Future() self._pending_requests[sequence_number] = future future.add_done_callback(self.remove_pending_request) return future
def call_async(self, request: SrvTypeRequest) -> Future: """ Make a service request and asyncronously get the result. :param request: The service request. :return: A future that completes when the request does. """ sequence_number = _rclpy.rclpy_send_request(self.client_handle, request) if sequence_number in self._pending_requests: raise RuntimeError('Sequence (%r) conflicts with pending request' % sequence_number) future = Future() self._pending_requests[sequence_number] = future future.add_done_callback(self.remove_pending_request) return future
def spin_once_until_future_complete(self, future: Future, timeout_sec: float = None) -> None: future.add_done_callback(lambda x: self.wake()) self._spin_once_impl(timeout_sec, future.done)
def test_set_exception_schedules_callbacks(self): executor = DummyExecutor() f = Future(executor=executor) f.add_done_callback(lambda f: None) f.set_exception('Anything') self.assertTrue(executor.done_callbacks)
def test_cancel_schedules_callbacks(self): executor = DummyExecutor() f = Future(executor=executor) f.add_done_callback(lambda f: None) f.cancel() self.assertTrue(executor.done_callbacks)