示例#1
0
    def initialize(self):
        """
        Initialize creating all necessary bridged clients and servers.
        """

        connected = False
        while not rospy.is_shutdown() and not connected:
            try:
                self.client = ROSBridgeClient(self.rosbridge_ip,
                                              self.rosbridge_port)
                connected = True
            except socket.error as e:
                rospy.logwarn(
                    'Error when opening websocket, is ROSBridge running?')
                rospy.logwarn(e)
                rospy.sleep(5.0)

        # We keep track of the instanced stuff in this dict
        self._instances = {'topics': [], 'services': []}
        for r_t in self.remote_topics:
            if len(r_t) == 2:
                topic_name, topic_type = r_t
                local_name = topic_name
                latch = False
            elif len(r_t) == 3:
                topic_name, topic_type, local_name = r_t
                latch = False
            elif len(r_t) == 4:
                topic_name, topic_type, local_name, latch = r_t

            msg = ROSDuctConnection()
            msg.conn_name = topic_name
            msg.conn_type = topic_type
            msg.alias_name = local_name
            msg.latch = latch if latch.__class__ == bool else latch.lower(
            ) == 'true'
            self.add_remote_topic(msg)

        for l_t in self.local_topics:
            if len(l_t) == 2:
                topic_name, topic_type = l_t
                remote_name = topic_name
                latch = False
            elif len(l_t) == 3:
                topic_name, topic_type, remote_name = l_t
                latch = False
            elif len(l_t) == 4:
                topic_name, topic_type, remote_name, latch = l_t

            msg = ROSDuctConnection()
            msg.conn_name = topic_name
            msg.conn_type = topic_type
            msg.alias_name = remote_name
            msg.latch = latch if latch.__class__ == bool else latch.lower(
            ) == 'true'
            self.add_local_topic(msg)

        # Services
        for r_s in self.remote_services:
            if len(r_s) == 2:
                service_name, service_type = r_s
                local_name = service_name
            elif len(r_s) == 3:
                service_name, service_type, local_name = r_s

            msg = ROSDuctConnection()
            msg.conn_name = service_name
            msg.conn_type = service_type
            msg.alias_name = local_name
            self.add_remote_service(msg)

        for l_s in self.local_services:
            if len(l_s) == 2:
                service_name, service_type = l_s
                remote_name = service_name
            elif len(l_s) == 3:
                service_name, service_type, remote_name = l_s

            msg = ROSDuctConnection()
            msg.conn_name = service_name
            msg.conn_type = service_type
            msg.alias_name = remote_name
            self.add_local_service(msg)

        # Get all params and store them for further updates
        for param in self.parameters:
            if type(param) == list:
                # remote param name is the first one
                param = param[0]
            self.last_params[param] = self.client.get_param(param)
示例#2
0
class ROSduct(object):
    def __init__(self):
        # ROSbridge
        self.rosbridge_ip = rospy.get_param('~rosbridge_ip', None)
        if self.rosbridge_ip is None:
            rospy.logerr('No rosbridge_ip given.')
            raise Exception('No rosbridge_ip given.')
        self.rosbridge_port = rospy.get_param('~rosbridge_port', 9090)
        rospy.loginfo("Will connect to ROSBridge websocket: ws://{}:{}".format(
            self.rosbridge_ip, self.rosbridge_port))

        # Topics
        # TODO: check if topic types are installed, if not, give a warning
        self.remote_topics = rospy.get_param('~remote_topics', [])
        #rospy.loginfo("Remote topics: " + str(self.remote_topics))
        self.local_topics = rospy.get_param('~local_topics', [])
        #rospy.loginfo("Local topics: " + str(self.local_topics))

        # Services
        # TODO: check if service types are installed
        self.remote_services = rospy.get_param('~remote_services', [])
        #rospy.loginfo("Remote services: " + str(self.remote_services))
        self.local_services = rospy.get_param('~local_services', [])
        #rospy.loginfo("Local services: " + str(self.local_services))

        # Parameters
        self.rate_hz = rospy.get_param('~parameter_polling_hz', 1)
        self.parameters = rospy.get_param('~parameters', [])
        #rospy.loginfo("Parameters: " + str(self.parameters))
        self.last_params = {}

        self.check_if_msgs_are_installed()

        self.initialize()
        self.expose_local_topic = rospy.Service('~expose_local_topic',
                                                ROSDuctConnection,
                                                self.add_local_topic)
        self.close_local_topic = rospy.Service('~close_local_topic',
                                               ROSDuctConnection,
                                               self.remove_local_topic)
        self.expose_local_service = rospy.Service('~expose_local_service',
                                                  ROSDuctConnection,
                                                  self.add_local_service)
        self.close_local_service = rospy.Service('~close_local_service',
                                                 ROSDuctConnection,
                                                 self.remove_local_service)
        self.expose_remote_topic = rospy.Service('~expose_remote_topic',
                                                 ROSDuctConnection,
                                                 self.add_remote_topic)
        self.close_remote_topic = rospy.Service('~close_remote_topic',
                                                ROSDuctConnection,
                                                self.remove_remote_topic)
        self.expose_remote_service = rospy.Service('~expose_remote_service',
                                                   ROSDuctConnection,
                                                   self.add_remote_service)
        self.close_remote_service = rospy.Service('~close_remote_service',
                                                  ROSDuctConnection,
                                                  self.remove_remote_service)

    def initialize(self):
        """
        Initialize creating all necessary bridged clients and servers.
        """

        connected = False
        while not rospy.is_shutdown() and not connected:
            try:
                self.client = ROSBridgeClient(self.rosbridge_ip,
                                              self.rosbridge_port)
                connected = True
            except socket.error as e:
                rospy.logwarn(
                    'Error when opening websocket, is ROSBridge running?')
                rospy.logwarn(e)
                rospy.sleep(5.0)

        # We keep track of the instanced stuff in this dict
        self._instances = {'topics': [], 'services': []}
        for r_t in self.remote_topics:
            if len(r_t) == 2:
                topic_name, topic_type = r_t
                local_name = topic_name
                latch = False
            elif len(r_t) == 3:
                topic_name, topic_type, local_name = r_t
                latch = False
            elif len(r_t) == 4:
                topic_name, topic_type, local_name, latch = r_t

            msg = ROSDuctConnection()
            msg.conn_name = topic_name
            msg.conn_type = topic_type
            msg.alias_name = local_name
            msg.latch = latch if latch.__class__ == bool else latch.lower(
            ) == 'true'
            self.add_remote_topic(msg)

        for l_t in self.local_topics:
            if len(l_t) == 2:
                topic_name, topic_type = l_t
                remote_name = topic_name
                latch = False
            elif len(l_t) == 3:
                topic_name, topic_type, remote_name = l_t
                latch = False
            elif len(l_t) == 4:
                topic_name, topic_type, remote_name, latch = l_t

            msg = ROSDuctConnection()
            msg.conn_name = topic_name
            msg.conn_type = topic_type
            msg.alias_name = remote_name
            msg.latch = latch if latch.__class__ == bool else latch.lower(
            ) == 'true'
            self.add_local_topic(msg)

        # Services
        for r_s in self.remote_services:
            if len(r_s) == 2:
                service_name, service_type = r_s
                local_name = service_name
            elif len(r_s) == 3:
                service_name, service_type, local_name = r_s

            msg = ROSDuctConnection()
            msg.conn_name = service_name
            msg.conn_type = service_type
            msg.alias_name = local_name
            self.add_remote_service(msg)

        for l_s in self.local_services:
            if len(l_s) == 2:
                service_name, service_type = l_s
                remote_name = service_name
            elif len(l_s) == 3:
                service_name, service_type, remote_name = l_s

            msg = ROSDuctConnection()
            msg.conn_name = service_name
            msg.conn_type = service_type
            msg.alias_name = remote_name
            self.add_local_service(msg)

        # Get all params and store them for further updates
        for param in self.parameters:
            if type(param) == list:
                # remote param name is the first one
                param = param[0]
            self.last_params[param] = self.client.get_param(param)

    def add_local_topic(self, msg):
        bridgepub = self.client.publisher(msg.alias_name,
                                          msg.conn_type,
                                          latch=msg.latch)

        cb_l_to_r = self.create_callback_from_local_to_remote(
            msg.conn_name, msg.conn_type, bridgepub)

        rossub = rospy.Subscriber(msg.conn_name, get_ROS_class(msg.conn_type),
                                  cb_l_to_r)
        self._instances['topics'].append(
            {msg.conn_name: {
                'rossub': rossub,
                'bridgepub': bridgepub
            }})
        return ROSDuctConnectionResponse()

    def remove_local_topic(self, msg):
        for i, topic in enumerate(self._instances['topics']):
            if msg.conn_name in topic:
                topic[msg.conn_name]['rossub'].unregister()
                topic[msg.conn_name]['bridgepub'].unregister()
                del self._instances['topics'][i]
                break
        return ROSDuctConnectionResponse()

    def add_remote_topic(self, msg):
        rospub = rospy.Publisher(
            msg.alias_name,
            get_ROS_class(msg.conn_type),
            # SubscribeListener added later
            queue_size=1,
            latch=msg.latch)

        cb_r_to_l = self.create_callback_from_remote_to_local(
            msg.conn_name, msg.conn_type, rospub)
        subl = self.create_subscribe_listener(msg.conn_name, msg.conn_type,
                                              cb_r_to_l)
        rospub.impl.add_subscriber_listener(subl)
        self._instances['topics'].append(
            {msg.conn_name: {
                'rospub': rospub,
                'bridgesub': None
            }})
        return ROSDuctConnectionResponse()

    def remove_remote_topic(self, msg):
        for i, topic in enumerate(self._instances['topics']):
            if msg.conn_name in topic:
                topic[msg.conn_name]['rospub'].unregister()
                del self._instances['topics'][i]
                break
        return ROSDuctConnectionResponse()

    def add_local_service(self, msg):
        rosservprox = rospy.ServiceProxy(
            msg.conn_name, get_ROS_class(msg.conn_type, srv=True))
        l_to_r_srv_cv = self.create_callback_from_local_to_remote_srv(
            msg.conn_name, msg.conn_type, rosservprox)
        remote_service_server = self.client.service_server(
            msg.alias_name, msg.conn_type, l_to_r_srv_cv)
        self._instances['services'].append({
            msg.conn_name: {
                'rosservprox': rosservprox,
                'bridgeservserver': remote_service_server
            }
        })
        return ROSDuctConnectionResponse()

    def remove_local_service(self, msg):
        for i, service in enumerate(self._instances['services']):
            if msg.conn_name in service:
                service[msg.conn_name]['rosservprox'].unregister()
                service[msg.conn_name]['bridgeservserver'].unregister()
                del self._instances['services'][i]
                break
        return ROSDuctConnectionResponse()

    def add_remote_service(self, msg):
        remote_service_client = self.client.service_client(
            msg.conn_name, msg.conn_type)
        r_to_l_serv_cb = self.create_callback_from_remote_to_local_srv(
            remote_service_client, msg.conn_name, msg.conn_type)
        rosserv = rospy.Service(msg.alias_name,
                                get_ROS_class(msg.conn_type, srv=True),
                                r_to_l_serv_cb)

        self._instances['services'].append({
            msg.conn_name: {
                'rosserv': rosserv,
                'bridgeservclient': remote_service_client
            }
        })
        return ROSDuctConnectionResponse()

    def remove_remote_service(self, msg):
        for i, service in enumerate(self._instances['services']):
            if msg.conn_name in service:
                service[msg.conn_name]['bridgeservserver'].unregister()
                del self._instances['services'][i]
                break
        return ROSDuctConnectionResponse()

    def create_callback_from_remote_to_local(self, topic_name, topic_type,
                                             rospub):
        # Note: argument MUST be named 'message' as
        # that's the keyword given to pydispatch
        def callback_remote_to_local(message):
            rospy.logdebug("Remote ROSBridge subscriber from topic " +
                           topic_name + ' of type ' + topic_type +
                           ' got data: ' + str(message) +
                           ' which is republished locally.')
            # Only convert and publish with subscribers
            if rospub.get_num_connections() >= 1:
                msg = from_dict_to_ROS(message, topic_type)
                rospub.publish(msg)

        return callback_remote_to_local

    def create_callback_from_local_to_remote(self, topic_name, topic_type,
                                             bridgepub):
        def callback_local_to_remote(message):
            rospy.logdebug("Local subscriber from topic " + topic_name +
                           ' of type ' + topic_type + ' got data: ' +
                           str(message) + ' which is republished remotely.')
            dict_msg = from_ROS_to_dict(message)
            if not self.client.terminated:
                bridgepub.publish(dict_msg)

        return callback_local_to_remote

    def create_subscribe_listener(self, topic_name, topic_type, cb_r_to_l):
        # We create a SubscribeListener that will
        # create a rosbridge subscriber on demand
        # and also unregister it if no one is listening
        class CustomSubscribeListener(rospy.SubscribeListener):
            def __init__(this):
                super(CustomSubscribeListener, this).__init__()
                this.bridgesub = None

            def peer_subscribe(this, tn, tp, pp):
                # Only make a new subscriber if there wasn't one
                if this.bridgesub is None:
                    rospy.logdebug("We have a first subscriber to: " +
                                   topic_name)
                    this.bridgesub = self.client.subscriber(
                        topic_name, topic_type, cb_r_to_l)
                    for idx, topic_d in enumerate(self._instances['topics']):
                        if topic_d.get(topic_name):
                            self._instances['topics'][idx][topic_name][
                                'bridgesub'] = this.bridgesub
                            break

            def peer_unsubscribe(this, tn, num_peers):
                # Unsubscribe if there isnt anyone left
                if num_peers < 1:
                    rospy.logdebug("There are no more subscribers to: " +
                                   topic_name)
                    self.client.unsubscribe(this.bridgesub)
                    this.bridgesub = None
                    # May be redundant if it's actually a reference to this.bridgesub already
                    for idx, topic_d in enumerate(self._instances['topics']):
                        if topic_d.get(topic_name):
                            self._instances['topics'][idx][topic_name][
                                'bridgesub'] = None
                            break

        return CustomSubscribeListener()

    def create_callback_from_remote_to_local_srv(self, remote_service_client,
                                                 service_name, service_type):
        def callback_from_local_srv_call(request):
            rospy.loginfo("Got a SRV call to " + service_name + " of type " +
                          service_type)
            req_dict = from_ROS_to_dict(request)

            result = {'responded': False, 'response': None}

            def cb(success, response):
                result['responded'] = True
                if success:
                    result['response'] = response

            remote_service_client.request(req_dict, cb)
            while not rospy.is_shutdown() and not result['responded']:
                rospy.sleep(0.1)
            if result['response'] is None:
                rospy.logerr('Service call didnt succeed (' +
                             str(service_name) + ' of type ' +
                             str(service_type))
                return None
            return from_dict_to_ROS(result['response'],
                                    service_type + 'Response',
                                    srv=True)

        return callback_from_local_srv_call

    def create_callback_from_local_to_remote_srv(self, service_name,
                                                 service_type, rosservprox):
        def callback_from_remote_service_call(request):
            ros_req = from_dict_to_ROS(request,
                                       service_type + 'Request',
                                       srv=True)
            rospy.loginfo("Waiting for server " + service_name + "...")
            rospy.wait_for_service(service_name)
            # TODO: error handling in services...
            try:
                resp = rosservprox.call(ros_req)
                resp_dict = from_ROS_to_dict(resp)
                return_val = True
            except rospy.ServiceException as exc:
                resp_dict = dict()
                return_val = False
                rospy.logerr("Service did not process request: " + str(exc))

            return return_val, resp_dict

        return callback_from_remote_service_call

    def check_if_msgs_are_installed(self):
        """
        Check if the provided message types are installed.
        """
        for rt in self.remote_topics:
            if len(rt) >= 2:
                topic_type = rt[1]

            if not is_ros_message_installed(topic_type):
                rospy.logwarn(
                    "{} could not be found in the system.".format(topic_type))

        for lt in self.local_topics:
            if len(lt) >= 2:
                topic_type = lt[1]

            if not is_ros_message_installed(topic_type):
                rospy.logwarn(
                    "{} could not be found in the system.".format(topic_type))

        for rs in self.remote_services:
            if len(rs) >= 2:
                service_type = rs[1]

            if not is_ros_service_installed(service_type):
                rospy.logwarn("{} could not be found in the system.".format(
                    service_type))

        for ls in self.local_services:
            if len(ls) >= 2:
                service_type = ls[1]

            if not is_ros_service_installed(service_type):
                rospy.logwarn("{} could not be found in the system.".format(
                    service_type))

    def sync_params(self):
        """
        Sync parameter server in between
        external and local roscore (local changes
        are not forwarded).
        """
        for param in self.parameters:
            if type(param) == list:
                local_param = param[1]
                param = param[0]
            else:
                local_param = param
            # Get remote param
            remote_param = self.client.get_param(param)
            if remote_param != self.last_params[param]:
                rospy.set_param(local_param, remote_param)
                self.last_params[param] = remote_param

    def spin(self):
        """
        Run the node, needed to update the parameter server.
        """
        r = rospy.Rate(self.rate_hz)
        while not rospy.is_shutdown():
            if self.client.terminated:  # we've lost the connection
                rospy.logerr(
                    "Unexpected disconnect from server, shutting down...")
                rospy.signal_shutdown("We've lost the connection!")
                #del self.client # will this remove all the pub/sub objects?
                #self.client.reconnect()
                #self.initialize()
            self.sync_params()
            r.sleep()
示例#3
0
    def initialize(self):
        """
        Initialize creating all necessary bridged clients and servers.
        """
        connected = False
        while not rospy.is_shutdown() and not connected:
            try:
                self.client = ROSBridgeClient(self.rosbridge_ip,
                                              self.rosbridge_port)
                connected = True
            except socket.error as e:
                rospy.logwarn(
                    'Error when opening websocket, is ROSBridge running?')
                rospy.logwarn(e)
                rospy.sleep(5.0)

        # We keep track of the instanced stuff in this dict
        self._instances = {'topics': [], 'services': []}
        for r_t in self.remote_topics:
            if len(r_t) == 2:
                topic_name, topic_type = r_t
                local_name = topic_name
            elif len(r_t) == 3:
                topic_name, topic_type, local_name = r_t
            rospub = rospy.Publisher(
                local_name,
                get_ROS_class(topic_type),
                # SubscribeListener added later
                queue_size=1)

            cb_r_to_l = self.create_callback_from_remote_to_local(
                topic_name, topic_type, rospub)
            subl = self.create_subscribe_listener(topic_name, topic_type,
                                                  cb_r_to_l)
            rospub.impl.add_subscriber_listener(subl)
            self._instances['topics'].append(
                {topic_name: {
                    'rospub': rospub,
                    'bridgesub': None
                }})

        for l_t in self.local_topics:
            if len(l_t) == 2:
                topic_name, topic_type = l_t
                remote_name = topic_name
            elif len(l_t) == 3:
                topic_name, topic_type, remote_name = l_t

            bridgepub = self.client.publisher(remote_name, topic_type)

            cb_l_to_r = self.create_callback_from_local_to_remote(
                topic_name, topic_type, bridgepub)

            rossub = rospy.Subscriber(topic_name, get_ROS_class(topic_type),
                                      cb_l_to_r)
            self._instances['topics'].append(
                {topic_name: {
                    'rossub': rossub,
                    'bridgepub': bridgepub
                }})

        # Services
        for r_s in self.remote_services:
            if len(r_s) == 2:
                service_name, service_type = r_s
                local_name = service_name
            elif len(r_s) == 3:
                service_name, service_type, local_name = r_s
            remote_service_client = self.client.service_client(
                service_name, service_type)
            r_to_l_serv_cb = self.create_callback_from_remote_to_local_srv(
                remote_service_client, service_name, service_type)
            rosserv = rospy.Service(local_name,
                                    get_ROS_class(service_type, srv=True),
                                    r_to_l_serv_cb)

            self._instances['services'].append({
                service_name: {
                    'rosserv': rosserv,
                    'bridgeservclient': remote_service_client
                }
            })

        for l_s in self.local_services:
            if len(l_s) == 2:
                service_name, service_type = l_s
                remote_name = service_name
            elif len(l_s) == 3:
                service_name, service_type, remote_name = l_s
            rosservprox = rospy.ServiceProxy(
                service_name, get_ROS_class(service_type, srv=True))
            l_to_r_srv_cv = self.create_callback_from_local_to_remote_srv(
                service_name, service_type, rosservprox)
            remote_service_server = self.client.service_server(
                remote_name, service_type, l_to_r_srv_cv)
            self._instances['services'].append({
                service_name: {
                    'rosservprox': rosservprox,
                    'bridgeservserver': remote_service_server
                }
            })

        # Get all params and store them for further updates
        for param in self.parameters:
            if type(param) == list:
                # remote param name is the first one
                param = param[0]
            self.last_params[param] = self.client.get_param(param)