示例#1
0
    def __init__(self, host = "localhost", service_port = 4000):
        """ Creates an instance of the MORSE simulator proxy.
        
        This is the main object you need to instanciate to communicate with the simulator.
        
        :param host: the simulator host (default: localhost)
        :param port: the port of the simulator socket interface (default: 4000)
        """

        self._services_in_queue = Queue() # where we put the service result to read from MORSE
        self._services_out_queue = Queue() # where we put the request to send to MORSE

        self.com = ComChannel(host)

        # First, connect to the service port
        self.com.connect(service_port,
                         self._services_in_queue,
                         self._services_out_queue)

        self.executor = MorseExecutor(max_workers = 10, morse = self)

        self.initapi()
示例#2
0
class Morse():

    id = 0

    def __init__(self, host = "localhost", service_port = 4000):
        """ Creates an instance of the MORSE simulator proxy.
        
        This is the main object you need to instanciate to communicate with the simulator.
        
        :param host: the simulator host (default: localhost)
        :param port: the port of the simulator socket interface (default: 4000)
        """

        self._services_in_queue = Queue() # where we put the service result to read from MORSE
        self._services_out_queue = Queue() # where we put the request to send to MORSE

        self.com = ComChannel(host)

        # First, connect to the service port
        self.com.connect(service_port,
                         self._services_in_queue,
                         self._services_out_queue)

        self.executor = MorseExecutor(max_workers = 10, morse = self)

        self.initapi()

    def _normalize_name(self, name):
        """
        Normalize Blender names to get valid Python identifiers
        """
        new = name
        for c in ".-~":
            new = new.replace(c, "_")
        return new

    def initapi(self):
        """ This method asks MORSE for the scene structure, and
        dynamically creates corresponding objects in 'self'.
        """
        simu = self.rpc("simulation", "details")

        self.robots = []
        for r in simu["robots"]:
            name = self._normalize_name(r["name"])
            self.robots.append(name)
            setattr(self, name, Robot())
            robot = getattr(self, name)

            for c in sorted(r["components"].keys()): # important to sort the list of components to ensure parents are created before children
                self._add_component(robot, c, r["components"][c])

    def _add_component(self, robot, fqn, details):
        stream = details.get('stream', None)
        if stream:
            port = self.get_stream_port(fqn)
        else:
            port = None

        services = details.get('services', [])

        name = fqn.split('.')[1:] # the first token is always the robot name. Remove it

        cmpt = Component(self,
                        name[-1],
                        fqn,
                        stream,
                        port,
                        services)

        if len(name) == 1: # this component belongs to the robot directly.
            robot[name[0]] = cmpt
        else:
            subcmpt = robot[name[0]]
            for sub in name[1:-1]:
                subcmpt = getattr(subcmpt, sub)

            if hasattr(subcmpt, name[-1]): # pathologic cmpt name!
                raise RuntimeError("Sub-component name <%s> conflicts with <%s.%s> member. To use pymorse with this scenario, please change the name of the sub-component." % (name[-1], subcmpt.name, name[-1]))
            setattr(subcmpt, name[-1], cmpt)


    def cancel(self, id):
        """ Send a cancelation request for an existing (running) service.

        If the service is not running or does not exist, the request is
        ignored.
        """
        req = {'id': id,
               'special': 'cancel'}

        self._services_out_queue.put(req)
        self.com.process()


    def rpc(self, component, service, *args):
        """ Calls a service from the simulator.
        
        The call will block until a response from the simulator is received.
        
        :param component: the component that expose the service (like a robot name)
        :param service: the name of the service
        :param args...: (variadic) each service parameter, as a separate argument
        """
        return self._execute_rpc(self._make_request(component, service, *args))
        
    def _make_request(self, component, service, *args):
        req = {'id': str(self.id),
               'component': component,
               'service': service,
               'args': args}
        self.id += 1
        return req

    def _execute_rpc(self, req):

        self._services_out_queue.put(req)
        self.com.process()

        while True:
            res = self._services_in_queue.get() #block until we get an answer
            if res['id'] != req['id']:
                self._services_in_queue.put(res)
                time.sleep(0) # try to switch to another
            else:
                break

        if res['status'] == SUCCESS:
            if not res['result']:
                return None
            return res['result']
            
        elif res['status'] == FAILURE:
            msg = res['result']

            if msg and "wrong # of parameters" in msg:
                raise TypeError(msg)
            
            raise MorseServiceFailed(res['result'])

        elif res['status'] == PREEMPTED:

            msg = res['result']

            raise MorseServicePreempted(res['result'])
       
        else:
            raise MorseServerError("Got an unexpected message status from MORSE: " + \
            res['status'])

    def close(self, cancel_async_services = False):

        if cancel_async_services:
            pymorselogger.info('Cancelling all running asynchronous requests...')
            self.executor.cancel_all()
        else:
            pymorselogger.info('Waiting for all asynchronous requests to complete...')
        self.executor.shutdown(wait = True)
        self.com.close()
        pymorselogger.info('Done. Bye bye!')

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not exc_type:
            self.close()
        else:
            self.close(True)
            return False # re-raise exception

    #####################################################################
    ###### Predefined methods to interact with the simulator

    def quit(self):
        self.rpc("simulation", "quit")
        self.close()

    def reset(self):
       return self.rpc("simulation", "reset")

    def streams(self):
       return self.rpc("simulation", "list_streams")

    def get_stream_port(self, stream):
       return self.rpc("simulation", "get_stream_port", stream)