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

来源:互联网 发布:淘宝子账号登录密码 编辑:程序博客网 时间:2024/05/22 14:02

rosmaster包下的setup.py,

from distutils.core import setupfrom catkin_pkg.python_setup import generate_distutils_setupd = 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 rosmasterrosmaster.rosmaster_main()

rosmaster_main()函数如下,

#ros_comm\tools\rosmaster\src\rosmaster\main.pydef 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 Implementationclass 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.pyclass 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联系起来。

原创粉丝点击