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