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的一个封装,用户一般不直接对他操作

posted @ 2016-06-10 17:03  逍遥客33  阅读(5186)  评论(0编辑  收藏  举报