def test_example_requester_(self):
     """ Initialize ROCON scheduler node for example requester. """
     self.number_of_requests = 3  # number of requests desired
     self.queued_request = None
     rospy.init_node("test_example_requester")
     self.sch = Scheduler(self.callback)
     rospy.spin()  # spin in the main thread
Пример #2
0
    def __init__(self, node_name='simple_scheduler',
                 period=rospy.Duration(1.0)):
        """ Constructor. """
        rospy.init_node(node_name)
        self.pool = ResourcePool()
        self.pub_pool = rospy.Publisher('resource_pool', KnownResources,
                                        queue_size=1, latch=True)
        self.pub_pool.publish(self.pool.known_resources())
        self.sub_client = rospy.Subscriber('concert_client_changes',
                                           ConcertClients, self.track_clients,
                                           queue_size=1, tcp_nodelay=True)
        self.ready_queue = PriorityQueue()
        """ Queue of waiting requests. """
        self.blocked_queue = PriorityQueue()
        """ Queue of blocked requests. """
        self.period = period
        """ Duration between periodic rescheduling. """
        self.notification_set = set()
        """ Set of requester identifiers to notify. """
        self.timer = rospy.Timer(self.period, self.reschedule)

        try:
            topic_name = rospy.get_param('~topic_name')
            self.sch = Scheduler(self.callback, topic=topic_name)
        except KeyError:
            self.sch = Scheduler(self.callback)  # use the default

        rospy.spin()
class TestExampleRequester(unittest.TestCase):
    def test_example_requester_(self):
        """ Initialize ROCON scheduler node for example requester. """
        self.number_of_requests = 3  # number of requests desired
        self.queued_request = None
        rospy.init_node("test_example_requester")
        self.sch = Scheduler(self.callback)
        rospy.spin()  # spin in the main thread

    def allocate(self, event):
        """ Timer handler: simulated resource availability event.

        This method runs in a different thread from the scheduler
        callback, so acquire the Big Scheduler Lock before doing
        anything.
        """
        with self.sch.lock:
            if self.queued_request:
                self.queued_request.grant([TEST_RESOURCE])
                rospy.loginfo('Request granted: ' +
                              str(self.queued_request.uuid))
                if self.number_of_requests == 2:
                    # preempt this request immediately
                    self.queued_request.preempt()
                    rospy.loginfo('Request preempted: ' +
                                  str(self.queued_request.uuid))
                self.queued_request = None
                self.sch.notify(self.queued_requester)

    def queue(self, requester_id, request):
        """
        Queue timer request to simulate delayed resource availability.
        """
        # this test case only makes one request at a time:
        self.assertIsNone(self.queued_request)
        self.queued_requester = requester_id
        self.queued_request = request
        self.timer = rospy.Timer(rospy.Duration(1.0),
                                 self.allocate,
                                 oneshot=True)
        rospy.loginfo('Request queued: ' + str(request.uuid))

    def callback(self, rset):
        """ Scheduler request callback.

        Makes new requests wait, they will be granted after a second.

        """
        for rq in rset.values():
            if rq.msg.status == Request.NEW:
                rq.wait(reason=Request.BUSY)
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                rq.close()
                rospy.loginfo('Request canceled: ' + str(rq.uuid))
                self.number_of_requests -= 1
                if self.number_of_requests <= 0:
                    rospy.signal_shutdown('test completed.')
class ExampleScheduler:

    def __init__(self):
        rospy.init_node("example_scheduler")
        # simplifying assumptions: all requests want a single robot,
        # and any of these will do:
        self.avail = deque([            # FIFO queue of available robots
            Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
            Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')])
        self.ready_queue = deque()      # FIFO queue of waiting requests
        self.sch = Scheduler(self.callback)
        rospy.spin()

    def callback(self, rset):
        """ Scheduler request callback. """
        rospy.logdebug('scheduler callback:')
        for rq in rset.values():
            rospy.logdebug('  ' + str(rq))
            if rq.msg.status == Request.NEW:
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                self.free(rset.requester_id, rq)

    def dispatch(self):
        """ Grant any available resources to waiting requests. """
        while len(self.ready_queue) > 0:
            if len(self.avail) == 0:    # no resources available?
                return
            resource = self.avail.popleft()
            requester_id, rq = self.ready_queue.popleft()
            try:                        # grant request & notify requester
                rq.grant([resource])
                self.sch.notify(requester_id)
                rospy.loginfo('Request granted: ' + str(rq.uuid))
            except (TransitionError, KeyError):
                # request no longer active or requester missing?
                # Put resource back at the front of the queue.
                self.avail.appendleft(resource)

    def free(self, requester_id, rq):
        """ Free all resources allocated for this request. """
        self.avail.extend(rq.allocations)
        rospy.loginfo('Request canceled: ' + str(rq.uuid))
        rq.close()
        self.dispatch()                 # grant waiting requests

    def queue(self, requester_id, rq):
        """ Add request to ready queue, making it wait. """
        try:
            rq.wait(reason=Request.BUSY)
        except TransitionError:         # request no longer active?
            return
        self.ready_queue.append((requester_id, rq))
        rospy.loginfo('Request queued: ' + str(rq.uuid))
        self.dispatch()
class ExampleScheduler:
    def __init__(self):
        rospy.init_node("example_scheduler")
        # simplifying assumptions: all requests want a single robot,
        # and any of these will do:
        self.avail = deque([  # FIFO queue of available robots
            Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
            Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')
        ])
        self.ready_queue = deque()  # FIFO queue of waiting requests
        self.sch = Scheduler(self.callback)
        rospy.spin()

    def callback(self, rset):
        """ Scheduler request callback. """
        rospy.logdebug('scheduler callback:')
        for rq in rset.values():
            rospy.logdebug('  ' + str(rq))
            if rq.msg.status == Request.NEW:
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                self.free(rset.requester_id, rq)

    def dispatch(self):
        """ Grant any available resources to waiting requests. """
        while len(self.ready_queue) > 0:
            if len(self.avail) == 0:  # no resources available?
                return
            resource = self.avail.popleft()
            requester_id, rq = self.ready_queue.popleft()
            try:  # grant request & notify requester
                rq.grant([resource])
                self.sch.notify(requester_id)
                rospy.loginfo('Request granted: ' + str(rq.uuid))
            except (TransitionError, KeyError):
                # request no longer active or requester missing?
                # Put resource back at the front of the queue.
                self.avail.appendleft(resource)

    def free(self, requester_id, rq):
        """ Free all resources allocated for this request. """
        self.avail.extend(rq.allocations)
        rospy.loginfo('Request canceled: ' + str(rq.uuid))
        rq.close()
        self.dispatch()  # grant waiting requests

    def queue(self, requester_id, rq):
        """ Add request to ready queue, making it wait. """
        try:
            rq.wait(reason=Request.BUSY)
        except TransitionError:  # request no longer active?
            return
        self.ready_queue.append((requester_id, rq))
        rospy.loginfo('Request queued: ' + str(rq.uuid))
        self.dispatch()
 def __init__(self):
     rospy.init_node("example_scheduler")
     # simplifying assumptions: all requests want a single robot,
     # and any of these will do:
     self.avail = deque([  # FIFO queue of available robots
         Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
         Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')
     ])
     self.ready_queue = deque()  # FIFO queue of waiting requests
     self.sch = Scheduler(self.callback)
     rospy.spin()
class TestExampleRequester(unittest.TestCase):
    def test_example_requester_(self):
        """ Initialize ROCON scheduler node for example requester. """
        self.number_of_requests = 3  # number of requests desired
        self.queued_request = None
        rospy.init_node("test_example_requester")
        self.sch = Scheduler(self.callback)
        rospy.spin()  # spin in the main thread

    def allocate(self, event):
        """ Timer handler: simulated resource availability event.

        This method runs in a different thread from the scheduler
        callback, so acquire the Big Scheduler Lock before doing
        anything.
        """
        with self.sch.lock:
            if self.queued_request:
                self.queued_request.grant([TEST_RESOURCE])
                rospy.loginfo("Request granted: " + str(self.queued_request.uuid))
                if self.number_of_requests == 2:
                    # preempt this request immediately
                    self.queued_request.preempt()
                    rospy.loginfo("Request preempted: " + str(self.queued_request.uuid))
                self.queued_request = None
                self.sch.notify(self.queued_requester)

    def queue(self, requester_id, request):
        """
        Queue timer request to simulate delayed resource availability.
        """
        # this test case only makes one request at a time:
        self.assertIsNone(self.queued_request)
        self.queued_requester = requester_id
        self.queued_request = request
        self.timer = rospy.Timer(rospy.Duration(1.0), self.allocate, oneshot=True)
        rospy.loginfo("Request queued: " + str(request.uuid))

    def callback(self, rset):
        """ Scheduler request callback.

        Makes new requests wait, they will be granted after a second.

        """
        for rq in rset.values():
            if rq.msg.status == Request.NEW:
                rq.wait(reason=Request.BUSY)
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                rq.close()
                rospy.loginfo("Request canceled: " + str(rq.uuid))
                self.number_of_requests -= 1
                if self.number_of_requests <= 0:
                    rospy.signal_shutdown("test completed.")
 def test_timeout_scheduler(self):
     """ Initialize ROCON scheduler node for timeout test. """
     rospy.init_node("timeout_scheduler")
     # simplifying assumptions: all requests want a single robot,
     # and any of these will do:
     self.avail = deque(  # FIFO queue of available robots
         [
             Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
             Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')
         ])
     self.ready_queue = deque()  # FIFO queue of waiting requests
     self.seen_requester = False
     self.timer = rospy.Timer(rospy.Duration(2.0), self.check_finished)
     self.sch = Scheduler(self.callback, frequency=1.0)
     rospy.spin()
 def test_example_requester_(self):
     """ Initialize ROCON scheduler node for example requester. """
     self.number_of_requests = 3  # number of requests desired
     self.queued_request = None
     rospy.init_node("test_example_requester")
     self.sch = Scheduler(self.callback)
     rospy.spin()  # spin in the main thread
 def __init__(self):
     rospy.init_node("example_scheduler")
     # simplifying assumptions: all requests want a single robot,
     # and any of these will do:
     self.avail = deque([            # FIFO queue of available robots
         Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
         Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')])
     self.ready_queue = deque()      # FIFO queue of waiting requests
     self.sch = Scheduler(self.callback)
     rospy.spin()
 def test_timeout_scheduler(self):
     """ Initialize ROCON scheduler node for timeout test. """
     rospy.init_node("timeout_scheduler")
     # simplifying assumptions: all requests want a single robot,
     # and any of these will do:
     self.avail = deque(           # FIFO queue of available robots
         [Resource(
             rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
          Resource(
             rapp='example_rapp', uri='rocon:/turtlebot/marvin')])
     self.ready_queue = deque()      # FIFO queue of waiting requests
     self.seen_requester = False
     self.timer = rospy.Timer(rospy.Duration(2.0), self.check_finished)
     self.sch = Scheduler(self.callback, frequency=1.0)
     rospy.spin()
class TestTimeoutScheduler(unittest.TestCase):

    def test_timeout_scheduler(self):
        """ Initialize ROCON scheduler node for timeout test. """
        rospy.init_node("timeout_scheduler")
        # simplifying assumptions: all requests want a single robot,
        # and any of these will do:
        self.avail = deque(           # FIFO queue of available robots
            [Resource(
                rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
             Resource(
                rapp='example_rapp', uri='rocon:/turtlebot/marvin')])
        self.ready_queue = deque()      # FIFO queue of waiting requests
        self.seen_requester = False
        self.timer = rospy.Timer(rospy.Duration(2.0), self.check_finished)
        self.sch = Scheduler(self.callback, frequency=1.0)
        rospy.spin()

    def callback(self, rset):
        """ Scheduler request callback. """
        rospy.logdebug('scheduler callback:')
        for rq in rset.values():
            rospy.logdebug('  ' + str(rq))
            if rq.msg.status == Request.NEW:
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                self.free(rset.requester_id, rq)

    def check_finished(self, event):
        """ Timer event handler:

        Stops test after at least one requester has come and gone.
        """
        if self.seen_requester:
            if len(self.sch.requesters) == 0:
                rospy.loginfo('requester connection lost')
                rospy.signal_shutdown('test completed.')
        else:
            if len(self.sch.requesters) > 0:
                self.seen_requester = True

    def dispatch(self):
        """ Grant any available resources to waiting requests. """
        while len(self.ready_queue) > 0:
            if len(self.avail) == 0:    # no resources available?
                return
            resource = self.avail.popleft()
            requester_id, rq = self.ready_queue.popleft()
            try:                        # grant request & notify requester
                rq.grant([resource])
                self.sch.notify(requester_id)
                rospy.loginfo('Request granted: ' + str(rq.uuid))
            except (TransitionError, KeyError):
                # request no longer active or requester missing?
                # Put resource back at the front of the queue.
                self.avail.appendleft(resource)

    def free(self, requester_id, rq):
        """ Free all resources allocated for this request. """
        self.avail.extend(rq.allocations)
        rospy.loginfo('Request canceled: ' + str(rq.uuid))
        rq.close()
        self.dispatch()                 # grant waiting requests

    def queue(self, requester_id, rq):
        """ Add request to ready queue, making it wait. """
        try:
            rq.wait(reason=Request.BUSY)
        except TransitionError:         # request no longer active?
            return
        self.ready_queue.append((requester_id, rq))
        rospy.loginfo('Request queued: ' + str(rq.uuid))
        self.dispatch()
Пример #13
0
class SimpleSchedulerNode(object):
    """ Simple scheduler node.

    :param node_name: (str) Default name of scheduler node.
    :param period: (:class:`rospy.Duration`) Rescheduling time quantum.

    Derived versions of this class can implement different scheduling
    policies.
    """
    def __init__(self, node_name='simple_scheduler',
                 period=rospy.Duration(1.0)):
        """ Constructor. """
        rospy.init_node(node_name)
        self.pool = ResourcePool()
        self.pub_pool = rospy.Publisher('resource_pool', KnownResources,
                                        queue_size=1, latch=True)
        self.pub_pool.publish(self.pool.known_resources())
        self.sub_client = rospy.Subscriber('concert_client_changes',
                                           ConcertClients, self.track_clients,
                                           queue_size=1, tcp_nodelay=True)
        self.ready_queue = PriorityQueue()
        """ Queue of waiting requests. """
        self.blocked_queue = PriorityQueue()
        """ Queue of blocked requests. """
        self.period = period
        """ Duration between periodic rescheduling. """
        self.notification_set = set()
        """ Set of requester identifiers to notify. """
        self.timer = rospy.Timer(self.period, self.reschedule)

        try:
            topic_name = rospy.get_param('~topic_name')
            self.sch = Scheduler(self.callback, topic=topic_name)
        except KeyError:
            self.sch = Scheduler(self.callback)  # use the default

        rospy.spin()

    def callback(self, rset):
        """ Scheduler request callback.

        Called in the scheduler callback thread holding the Big
        Scheduler Lock.

        See: :class:`.rocon_scheduler_requests.Scheduler` documentation.
        """
        rospy.logdebug('scheduler callback:')
        for rq in rset.values():
            rospy.logdebug('  ' + str(rq))
            if rq.msg.status == Request.NEW:
                self.queue(rq, rset.requester_id)
            elif rq.msg.status == Request.CANCELING:
                self.free(rq, rset.requester_id)
        self.dispatch()                 # try to allocate ready requests

    def dispatch(self):
        """ Grant any available resources to ready requests.

        Notifies all affected requesters.
        """
        while len(self.ready_queue) > 0:
            # Try to allocate top element in the ready queue.
            elem = self.ready_queue.pop()
            resources = []
            try:
                resources = self.pool.allocate(elem.request)
            except InvalidRequestError as ex:
                self.reject_request(elem, ex)
                continue                # skip to next queue element

            if not resources:           # top request cannot be satisfied?
                # Return it to head of queue.
                self.ready_queue.add(elem)
                break                   # stop looking

            try:
                elem.request.grant(resources)
                rospy.loginfo(
                    'Request granted: ' + str(elem.request.uuid))
            except TransitionError:     # request no longer active?
                # Return allocated resources to the pool.
                self.pool.release_resources(resources)
            self.notification_set.add(elem.requester_id)

        # notify all affected requesters
        self.notify_requesters()

        # update resource_pool topic, if anything changed
        if self.pool.changed:
            self.pub_pool.publish(self.pool.known_resources())

    def free(self, request, requester_id):
        """ Free all resources allocated for this *request*.

        :param request: (:class:`.ActiveRequest`)
        :param requester_id: (:class:`uuid.UUID`) Unique requester identifier.
        """
        self.pool.release_request(request)
        rospy.loginfo('Request canceled: ' + str(request.uuid))
        request.close()
        # remove request from any queues
        request_id = request.uuid
        for queue in [self.ready_queue, self.blocked_queue]:
            if request_id in queue:
                queue.remove(request_id)
                break                   # should not be in any other queue
        self.notification_set.add(requester_id)

    def notify_requesters(self):
        """ Notify affected requesters.

        :pre: self.notification_set contains requesters to notify.
        :post: self.notification_set is empty.
        """
        for requester_id in self.notification_set:
            try:
                self.sch.notify(requester_id)
            except KeyError:            # requester now missing?
                # shut down this requester
                self.shutdown_requester(requester_id)
        self.notification_set.clear()

    def queue(self, request, requester_id):
        """ Add *request* to ready queue, making it wait.

        :param request: resource request to be queued.
        :type request: :class:`.ActiveRequest`
        :param requester_id: Unique requester identifier.
        :type requester_id: :class:`uuid.UUID`
        """
        try:
            request.wait(reason=Request.BUSY)
        except TransitionError:         # request no longer active?
            return
        self.ready_queue.add(QueueElement(request, requester_id))
        rospy.loginfo('Request queued: ' + str(request.uuid))
        self.notification_set.add(requester_id)

    def reject_request(self, element, exception):
        """ Reject an invalid queue *element*.

        :param element: Queue element to reject.
        :type element: :class:`.QueueElement`
        :param exception: Associated exception object.
        """
        rospy.logwarn(str(exception))
        if hasattr(Request, "INVALID"):  # new reason code defined?
            element.request.cancel(Request.INVALID)
        else:
            element.request.cancel(Request.UNAVAILABLE)
        self.notification_set.add(element.requester_id)

    def reschedule(self, event):
        """ Periodic rescheduling thread.

        Moves requests that cannot be satisfied with
        currently-available resources to the blocked queue.

        Uses the Big Scheduler Lock to serialize changes with
        operations done within the scheduler callback thread.
        """
        with self.sch.lock:
            while len(self.ready_queue) > 0:
                # see if head of ready queue can be scheduled
                elem = self.ready_queue.pop()

                # see if all available or allocated resources would suffice
                criteria = {CurrentStatus.AVAILABLE,
                            CurrentStatus.ALLOCATED}
                if self.pool.match_list(elem.request.msg.resources, criteria):
                    # request not blocked
                    self.ready_queue.add(elem)
                    break               # done rescheduling

                # move elem to blocked_queue
                rospy.loginfo('Request blocked: '
                              + str(elem.request.uuid))
                elem.request.wait(reason=Request.UNAVAILABLE)
                self.blocked_queue.add(elem)
                self.notification_set.add(elem.requester_id)

            # try to allocate any remaining ready requests
            self.dispatch()

    def shutdown_requester(self, requester_id):
        """ Shut down this requester, recovering all resources assigned. """
        for queue in [self.ready_queue, self.blocked_queue]:
            for rq in queue:
                if rq.requester_id == requester_id:
                    self.free(rq, requester_id)

    def track_clients(self, msg):
        """ Concert clients message callback.

        Updates the resource pool based on client changes published by
        the concert conductor.

        Uses the Big Scheduler Lock to serialize changes with
        operations done within the scheduler callback thread.
        """
        with self.sch.lock:
            self.pool.update(msg.clients)
class TestTimeoutScheduler(unittest.TestCase):
    def test_timeout_scheduler(self):
        """ Initialize ROCON scheduler node for timeout test. """
        rospy.init_node("timeout_scheduler")
        # simplifying assumptions: all requests want a single robot,
        # and any of these will do:
        self.avail = deque(  # FIFO queue of available robots
            [
                Resource(rapp='example_rapp', uri='rocon:/turtlebot/roberto'),
                Resource(rapp='example_rapp', uri='rocon:/turtlebot/marvin')
            ])
        self.ready_queue = deque()  # FIFO queue of waiting requests
        self.seen_requester = False
        self.timer = rospy.Timer(rospy.Duration(2.0), self.check_finished)
        self.sch = Scheduler(self.callback, frequency=1.0)
        rospy.spin()

    def callback(self, rset):
        """ Scheduler request callback. """
        rospy.logdebug('scheduler callback:')
        for rq in rset.values():
            rospy.logdebug('  ' + str(rq))
            if rq.msg.status == Request.NEW:
                self.queue(rset.requester_id, rq)
            elif rq.msg.status == Request.CANCELING:
                self.free(rset.requester_id, rq)

    def check_finished(self, event):
        """ Timer event handler:

        Stops test after at least one requester has come and gone.
        """
        if self.seen_requester:
            if len(self.sch.requesters) == 0:
                rospy.loginfo('requester connection lost')
                rospy.signal_shutdown('test completed.')
        else:
            if len(self.sch.requesters) > 0:
                self.seen_requester = True

    def dispatch(self):
        """ Grant any available resources to waiting requests. """
        while len(self.ready_queue) > 0:
            if len(self.avail) == 0:  # no resources available?
                return
            resource = self.avail.popleft()
            requester_id, rq = self.ready_queue.popleft()
            try:  # grant request & notify requester
                rq.grant([resource])
                self.sch.notify(requester_id)
                rospy.loginfo('Request granted: ' + str(rq.uuid))
            except (TransitionError, KeyError):
                # request no longer active or requester missing?
                # Put resource back at the front of the queue.
                self.avail.appendleft(resource)

    def free(self, requester_id, rq):
        """ Free all resources allocated for this request. """
        self.avail.extend(rq.allocations)
        rospy.loginfo('Request canceled: ' + str(rq.uuid))
        rq.close()
        self.dispatch()  # grant waiting requests

    def queue(self, requester_id, rq):
        """ Add request to ready queue, making it wait. """
        try:
            rq.wait(reason=Request.BUSY)
        except TransitionError:  # request no longer active?
            return
        self.ready_queue.append((requester_id, rq))
        rospy.loginfo('Request queued: ' + str(rq.uuid))
        self.dispatch()