• ros的源码阅读


    测试代码,使用xmlrpc与roscore通信

    ros的框架是使用rpc与server端通信,server维护topic的publisher,subscriber,param server,serviceServer

    import xmlrpclib
    
    server = xmlrpclib.ServerProxy("http://localhost:11311/",verbose=False)
    
    print server.getSystemState('/rosout')
    
    ##返回的是 [topic node ] [topic node ] [topic node ] [topic node ]
    

    server

    rosmaster/main.py

    try:
        logger.info("Starting ROS Master Node")
        #定义master的端口,工作线程,启动
        master = rosmaster.master.Master(port, options.num_workers)
        master.start()
    
        import time
        while master.ok():
            time.sleep(.1)
    except KeyboardInterrupt:
        logger.info("keyboard interrupt, will exit")
    finally:
        logger.info("stopping master...")
        master.stop()
    

    rosmaster/master.py

    class Master(object):
        
        def __init__(self, port=DEFAULT_MASTER_PORT, num_workers=rosmaster.master_api.NUM_WORKERS):
            self.port = port
            self.num_workers = num_workers
            
        def start(self):
            """
            Start the ROS Master.
            """
            self.handler = None
            self.master_node = None
            self.uri = None
    
    # self.server.register_instance(self.handler)
    # 注册handler到xmlrpc,以后每次远程调用的时候,寻找ROSMasterHandler类同名称的函数进行处理返回
            handler = rosmaster.master_api.ROSMasterHandler(self.num_workers)
            master_node = rosgraph.xmlrpc.XmlRpcNode(self.port, handler)
            master_node.start()
    
            # poll for initialization
            while not master_node.uri:
                time.sleep(0.0001) 
    
            # save fields
            self.handler = handler
            self.master_node = master_node
            self.uri = master_node.uri
    

    rosmaster/master_api.py

    class ROSMasterHandler(object):
        """
        XML-RPC handler for ROS master APIs.
        API routines for the ROS Master Node. The Master Node is a
        superset of the Slave Node and contains additional API methods for
        creating and monitoring a graph of slave nodes.
    
        By convention, ROS nodes take in caller_id as the first parameter
        of any API call.  The setting of this parameter is rarely done by
        client code as ros::msproxy::MasterProxy automatically inserts
        this parameter (see ros::client::getMaster()).
        """
        
        def __init__(self, num_workers=NUM_WORKERS):
            """ctor."""
    
            self.uri = None
            self.done = False
    
            self.thread_pool = rosmaster.threadpool.MarkedThreadPool(num_workers)
            # pub/sub/providers: dict { topicName : [publishers/subscribers names] }
            self.ps_lock = threading.Condition(threading.Lock())
    
            self.reg_manager = RegistrationManager(self.thread_pool)
    
            # maintain refs to reg_manager fields
            self.publishers  = self.reg_manager.publishers
            self.subscribers = self.reg_manager.subscribers
            self.services = self.reg_manager.services
            self.param_subscribers = self.reg_manager.param_subscribers
            
            self.topics_types = {} #dict { topicName : type }
    
            # parameter server dictionary
            self.param_server = rosmaster.paramserver.ParamDictionary(self.reg_manager)
    

    ROSMasterHandler定义的所有函数,也就是xmlrpc服务器处理的函数

    rosmaster/registrations.py

    class RegistrationManager(object):
        """
        Stores registrations for Master.
        
        RegistrationManager is not threadsafe, so access must be externally locked as appropriate
        """
    
        def __init__(self, thread_pool):
            """
            ctor.
            @param thread_pool: thread pool for queueing tasks
            @type  thread_pool: ThreadPool
            """
            self.nodes = {}
            self.thread_pool = thread_pool
    
            self.publishers  = Registrations(Registrations.TOPIC_PUBLICATIONS)
            self.subscribers = Registrations(Registrations.TOPIC_SUBSCRIPTIONS)
            self.services = Registrations(Registrations.SERVICE)
            self.param_subscribers = Registrations(Registrations.PARAM_SUBSCRIPTIONS) 
    
       def _register(self, r, key, caller_id, caller_api, service_api=None):
            # update node information
            node_ref, changed = self._register_node_api(caller_id, caller_api)
            node_ref.add(r.type, key)
            # update pub/sub/service indicies
            if changed:
                self.publishers.unregister_all(caller_id)
                self.subscribers.unregister_all(caller_id)
                self.services.unregister_all(caller_id)
                self.param_subscribers.unregister_all(caller_id)
            r.register(key, caller_id, caller_api, service_api)
            
        def _unregister(self, r, key, caller_id, caller_api, service_api=None):
            node_ref = self.nodes.get(caller_id, None)
            if node_ref != None:
                retval = r.unregister(key, caller_id, caller_api, service_api)
                # check num removed field, if 1, unregister is valid
                if retval[2] == 1:
                    node_ref.remove(r.type, key)
                if node_ref.is_empty():
                    del self.nodes[caller_id]
            else:
                retval = 1, "[%s] is not a registered node"%caller_id, 0
            return retval
    

    上面的publishers,subscribers,services都是由 Registrations这个类进行管理

    def registerPublisher(self, caller_id, topic, topic_type, caller_api):
        """
        Register the caller as a publisher the topic.
        @param caller_id: ROS caller id
        @type  caller_id: str
        @param topic: Fully-qualified name of topic to register.
        @type  topic: str
        @param topic_type: Datatype for topic. Must be a
        package-resource name, i.e. the .msg name.
        @type  topic_type: str
        @param caller_api str: ROS caller XML-RPC API URI
        @type  caller_api: str
        @return: (code, statusMessage, subscriberApis).
        List of current subscribers of topic in the form of XMLRPC URIs.
        @rtype: (int, str, [str])
        """
        #NOTE: we need topic_type for getPublishedTopics.
        try:
            self.ps_lock.acquire()
            self.reg_manager.register_publisher(topic, caller_id, caller_api)
            # don't let '*' type squash valid typing
            if topic_type != rosgraph.names.ANYTYPE or not topic in self.topics_types:
                self.topics_types[topic] = topic_type
            pub_uris = self.publishers.get_apis(topic)
            sub_uris = self.subscribers.get_apis(topic)
    #通知订阅topic的所有subscriber,回调函数
            self._notify_topic_subscribers(topic, pub_uris, sub_uris)
            mloginfo("+PUB [%s] %s %s",topic, caller_id, caller_api)
            sub_uris = self.subscribers.get_apis(topic)            
        finally:
            self.ps_lock.release()
        return 1, "Registered [%s] as publisher of [%s]"%(caller_id, topic), sub_uris
    

    caller_id 是节点,caller_api http://localhost:53749/

      def _notify(self, registrations, task, key, value, node_apis):
            """
            Generic implementation of callback notification
            @param registrations: Registrations
            @type  registrations: L{Registrations}
            @param task: task to queue
            @type  task: fn
            @param key: registration key
            @type  key: str
            @param value: value to pass to task
            @type  value: Any
            """
            # cache thread_pool for thread safety
            thread_pool = self.thread_pool
            if not thread_pool:
                return
            
            try:            
                for node_api in node_apis:
                    # use the api as a marker so that we limit one thread per subscriber
                    thread_pool.queue_task(node_api, task, (node_api, key, value))
            except KeyError:
                _logger.warn('subscriber data stale (key [%s], listener [%s]): node API unknown'%(key, s))
            
        def _notify_param_subscribers(self, updates):
            """
            Notify parameter subscribers of new parameter value
            @param updates [([str], str, any)*]: [(subscribers, param_key, param_value)*]
            @param param_value str: parameter value
            """
            # cache thread_pool for thread safety
            thread_pool = self.thread_pool
            if not thread_pool:
                return
    
            for subscribers, key, value in updates:
                # use the api as a marker so that we limit one thread per subscriber
                for caller_id, caller_api in subscribers:
                    self.thread_pool.queue_task(caller_api, self.param_update_task, (caller_id, caller_api, key, value))
    
    

    client

    def init_node(name, argv=None, anonymous=False, log_level=None, disable_rostime=False, disable_rosout=False, disable_signals=False, xmlrpc_port=0, tcpros_port=0):
    
    class MasterProxy(object):
        """
        Convenience wrapper for ROS master API and XML-RPC
        implementation. The Master API methods can be invoked on this
        object and will be forwarded appropriately. Names in arguments
        will be remapped according to current node settings. Provides
        dictionary-like access to parameter server, e.g.::
        
          master[key] = value
    
        All methods are thread-safe.
        """
    

    rospy 是python的客户端的实现
    roscpp 是c++的客户端的实现

    ros::NodeHandler构造函数执行会调用ros::start(),接下来ros的框架就起来了。

    //init.cpp    
    namespace ros{
        void start()
        {
    
          PollManager::instance()->addPollThreadListener(checkForShutdown);
          XMLRPCManager::instance()->bind("shutdown", shutdownCallback);
    
          initInternalTimerManager();
    
          TopicManager::instance()->start();
          ServiceManager::instance()->start();
          ConnectionManager::instance()->start();
          PollManager::instance()->start();
          XMLRPCManager::instance()->start();
        }
    };
    

    gdb调试一个listener, talker,service ,client,可以看到每个node都起了好多线程,有专门的log thread, xmlrpc select线程,poll线程

    rosgraph 是对底层的master的一个封装,用户一般不直接对他操作

  • 相关阅读:
    bash 教程 shell 基础语法
    使用 Flutter 开发 Windows 桌面应用 [MD]
    小tips:使用babelupgrade从babel6升级babel7
    JS的可选链操作符(?.)与双问号(??),你用到了吗?
    JS处理html的编码(encode)与解码(decode)
    pdf A3 到 A4
    grub4dos 制作U盘启动盘
    amixer的用法
    一个tomcat设置多个端口
    PostgreSQL 配置内存参数
  • 原文地址:https://www.cnblogs.com/shhu1993/p/5573925.html
Copyright © 2020-2023  润新知