ros源码分析(3)—rosmaster 包分析

rosmaster包下的setup.py,

from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup

d = generate_distutils_setup(
    packages=['rosmaster'],
    package_dir={'': 'src'},
    scripts=['scripts/rosmaster'],
    requires=['roslib', 'rospkg']
)

setup(**d)

其中,命令行脚本为scripts/rosmaster(roslaunch会利用popen启动rosmaster进程,调用的就是该脚本,后续会分析)。

rosmaster仅执行了rosmaster_main()函数,

import rosmaster
rosmaster.rosmaster_main()

rosmaster_main()函数如下,

#ros_comm\tools\rosmaster\src\rosmaster\main.py
def rosmaster_main(argv=sys.argv, stdout=sys.stdout, env=os.environ):
    #①前面都是解析命令行参数
    parser = optparse.OptionParser(usage="usage: zenmaster [options]")
    parser.add_option("--core",
                      dest="core", action="store_true", default=False,
                      help="run as core")
    parser.add_option("-p", "--port", 
                      dest="port", default=0,
                      help="override port", metavar="PORT")
    parser.add_option("-w", "--numworkers",
                      dest="num_workers", default=NUM_WORKERS, type=int,
                      help="override number of worker threads", metavar="NUM_WORKERS")
    parser.add_option("-t", "--timeout",
                      dest="timeout",
                      help="override the socket connection timeout (in seconds).", metavar="TIMEOUT")
    options, args = parser.parse_args(argv[1:])

    # only arg that zenmaster supports is __log remapping of logfilename
    for arg in args:
        if not arg.startswith('__log:='):
            parser.error("unrecognized arg: %s"%arg)
    configure_logging()
# ②rosmaster进程默认监听端口       
#DEFAULT_MASTER_PORT=11311 #default port for master's to bind to   
    port = rosmaster.master.DEFAULT_MASTER_PORT
    if options.port:
        port = int(options.port)
.......

    try:
        logger.info("Starting ROS Master Node")
        #③ 创建Master对象,启动XmlRpcNode
        #NUM_WORKERS = 3 
        #number of threads we use to send publisher_update notifications
        #三个工作线程
        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()

上段代码最重要的是第③部分:
创建了个Master类对象,默认端口为11311 ,三个工作线程;
调用start()。

关于python xmlrpc,可以参考http://blog.csdn.net/lewif/article/details/75150722

class Master(object):

    def start(self):
        """
        Start the ROS Master.
        """
        self.handler = None
        self.master_node = None
        self.uri = None
        #① 创建一个class ROSMasterHandler(object)对象
        handler = rosmaster.master_api.ROSMasterHandler(self.num_workers)
        #② 创建一个XmlRpcNode对象
        master_node = rosgraph.xmlrpc.XmlRpcNode(self.port, handler)
        #③ 调用XmlRpcNode的start(),其实是新启动一个线程,线程函数为XmlRpcNode中的run()
        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

        logging.getLogger('rosmaster.master').info("Master initialized: port[%s], uri[%s]", self.port, self.uri)
# Master Implementation

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
        .......
#ros_comm\tools\rosgraph\src\rosgraph\xmlrpc.py
class XmlRpcNode(object):
    """
    Generic XML-RPC node. Handles the additional complexity of binding
    an XML-RPC server to an arbitrary port. 
    XmlRpcNode is initialized when the uri field has a value.
    """
     def __init__(self, port=0, rpc_handler=None, on_run_error=None):
        """
        XML RPC Node constructor
        :param port: port to use for starting XML-RPC API. Set to 0 or omit to bind to any available port, ``int``
        :param rpc_handler: XML-RPC API handler for node, `XmlRpcHandler`
        :param on_run_error: function to invoke if server.run() throws
          Exception. Server always terminates if run() throws, but this
          enables cleanup routines to be invoked if server goes down, as
          well as include additional debugging. ``fn(Exception)``
        """
        #调用父类构造函数
        super(XmlRpcNode, self).__init__()

        #①构造函数传进来的rpc_handler
        self.handler = rpc_handler
        ...
  def start(self):
        """
        Initiate a thread to run the XML RPC server. Uses thread.start_new_thread.
        """
        #② 启动新线程,线程函数为run()
        _thread.start_new_thread(self.run, ())
  def run(self):
        try:
            self._run()

 def _run_init(self):
        logger = logging.getLogger('xmlrpc')            
        try:
            log_requests = 0
            port = self.port or 0 #0 = any

            bind_address = rosgraph.network.get_bind_address()
            logger.info("XML-RPC server binding to %s:%d" % (bind_address, port))

            self.server = ThreadingXMLRPCServer((bind_address, port), log_requests)
            self.port = self.server.server_address[1] #set the port to whatever server bound to
            if not self.port:
                self.port = self.server.socket.getsockname()[1] #Python 2.4

            assert self.port, "Unable to retrieve local address binding"

            # #528: semi-complicated logic for determining XML-RPC URI
            # - if ROS_IP/ROS_HOSTNAME is set, use that address
            # - if the hostname returns a non-localhost value, use that
            # - use whatever rosgraph.network.get_local_address() returns
            uri = None
            override = rosgraph.network.get_address_override()
            if override:
                uri = 'http://%s:%s/'%(override, self.port)
            else:
                try:
                    hostname = socket.gethostname()
                    if hostname and not hostname == 'localhost' and not hostname.startswith('127.') and hostname != '::':
                        uri = 'http://%s:%s/'%(hostname, self.port)
                except:
                    pass
            if not uri:
                uri = 'http://%s:%s/'%(rosgraph.network.get_local_address(), self.port)
            self.set_uri(uri)
            # log打印Started XML-RPC server [http://lyf:11311/]
            logger.info("Started XML-RPC server [%s]", self.uri)
            # ③这里最主要的是下面两个函数,将handler注册到xml-rpc,
            # handler是个rosmaster.master_api.ROSMasterHandler对象
            self.server.register_multicall_functions()
            self.server.register_instance(self.handler)
    .......

    def _run(self):
        """
        Main processing thread body.
        :raises: :exc:`socket.error` If server cannot bind

        """
        self._run_init()
        while not self.is_shutdown:
            try:
                #④ 服务端开始监听运行
                self.server.serve_forever()

通过上面的代码分析可以看到rosmaster的整个执行流程:
rosmaster命令行脚本执行rosmaster_main();
启动了一个新的线程来启动xml-rpc server(rosmaster);
xml-rpc server注册了一个类为ROSMasterHandler,定义了rpc的方法。

接下来会继续分析roslaunch是如何调用这个rosmaster脚本,将roscore,roslaunch,rosmaster联系起来。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值