00001 import time
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 import roslib
00036 import rospy
00037
00038 from threading import Lock
00039
00040 """ ros_loader contains methods for dynamically loading ROS message classes at
00041 runtime. It's achieved by using roslib to load the manifest files for the
00042 package that the respective class is contained in.
00043
00044 Methods typically return the requested class or instance, or None if not found
00045 """
00046
00047
00048 _loaded_msgs = {}
00049 _loaded_srvs = {}
00050 _msgs_lock = Lock()
00051 _srvs_lock = Lock()
00052 _manifest_lock = Lock()
00053
00054
00055 class InvalidTypeStringException(Exception):
00056 def __init__(self, typestring):
00057 Exception.__init__(self, "%s is not a valid type string" % typestring)
00058
00059
00060 class InvalidPackageException(Exception):
00061 def __init__(self, package, original_exception):
00062 Exception.__init__(self,
00063 "Unable to load the manifest for package %s. Caused by: %s"
00064 % (package, original_exception.message)
00065 )
00066
00067
00068 class InvalidModuleException(Exception):
00069 def __init__(self, modname, subname, original_exception):
00070 Exception.__init__(self,
00071 "Unable to import %s.%s from package %s. Caused by: %s"
00072 % (modname, subname, modname, str(original_exception))
00073 )
00074
00075
00076 class InvalidClassException(Exception):
00077 def __init__(self, modname, subname, classname, original_exception):
00078 Exception.__init__(self,
00079 "Unable to import %s class %s from package %s. Caused by %s"
00080 % (subname, classname, modname, str(original_exception))
00081 )
00082
00083
00084 def get_message_class(typestring):
00085 """ Loads the message type specified.
00086
00087 Returns the loaded class, or throws exceptions on failure """
00088 return _get_msg_class(typestring)
00089
00090
00091 def get_service_class(typestring):
00092 """ Loads the service type specified.
00093
00094 Returns the loaded class, or None on failure """
00095 return _get_srv_class(typestring)
00096
00097
00098 def get_message_instance(typestring):
00099 """ If not loaded, loads the specified type.
00100 Then returns an instance of it, or None. """
00101 cls = get_message_class(typestring)
00102 return cls()
00103
00104
00105 def get_service_instance(typestring):
00106 """ If not loaded, loads the specified type.
00107 Then returns an instance of it, or None. """
00108 cls = get_service_class(typestring)
00109 return cls()
00110
00111
00112 def get_service_request_instance(typestring):
00113 cls = get_service_class(typestring)
00114 return cls._request_class()
00115
00116
00117 def get_service_response_instance(typestring):
00118 cls = get_service_class(typestring)
00119 return cls._response_class()
00120
00121
00122 def _get_msg_class(typestring):
00123 """ If not loaded, loads the specified msg class then returns an instance
00124 of it
00125
00126 Throws various exceptions if loading the msg class fails """
00127 global _loaded_msgs, _msgs_lock
00128 return _get_class(typestring, "msg", _loaded_msgs, _msgs_lock)
00129
00130
00131 def _get_srv_class(typestring):
00132 """ If not loaded, loads the specified srv class then returns an instance
00133 of it
00134
00135 Throws various exceptions if loading the srv class fails """
00136 global _loaded_srvs, _srvs_lock
00137 return _get_class(typestring, "srv", _loaded_srvs, _srvs_lock)
00138
00139
00140 def _get_class(typestring, subname, cache, lock):
00141 """ If not loaded, loads the specified class then returns an instance
00142 of it.
00143
00144 Loaded classes are cached in the provided cache dict
00145
00146 Throws various exceptions if loading the msg class fails """
00147
00148
00149 cls = _get_from_cache(cache, lock, typestring)
00150 if cls is not None:
00151 return cls
00152
00153
00154 modname, classname = _splittype(typestring)
00155 norm_typestring = modname + "/" + classname
00156
00157
00158 cls = _get_from_cache(cache, lock, norm_typestring)
00159 if cls is not None:
00160 return cls
00161
00162
00163 cls = _load_class(modname, subname, classname)
00164
00165
00166 _add_to_cache(cache, lock, typestring, cls)
00167 _add_to_cache(cache, lock, norm_typestring, cls)
00168
00169 return cls
00170
00171
00172 def _load_class(modname, subname, classname):
00173 """ Loads the manifest and imports the module that contains the specified
00174 type.
00175
00176 Logic is similar to that of roslib.message.get_message_class, but we want
00177 more expressive exceptions.
00178
00179 Returns the loaded module, or None on failure """
00180 global loaded_modules
00181
00182 try:
00183 with _manifest_lock:
00184
00185 roslib.launcher.load_manifest(modname)
00186 except Exception as exc:
00187 raise InvalidPackageException(modname, exc)
00188
00189 try:
00190 pypkg = __import__('%s.%s' % (modname, subname))
00191 except Exception as exc:
00192 raise InvalidModuleException(modname, subname, exc)
00193
00194 try:
00195 return getattr(getattr(pypkg, subname), classname)
00196 except Exception as exc:
00197 raise InvalidClassException(modname, subname, classname, exc)
00198
00199
00200 def _splittype(typestring):
00201 """ Split the string the / delimiter and strip out empty strings
00202
00203 Performs similar logic to roslib.names.package_resource_name but is a bit
00204 more forgiving about excess slashes
00205 """
00206 splits = [x for x in typestring.split("/") if x]
00207 if len(splits) == 2:
00208 return splits
00209 raise InvalidTypeStringException(typestring)
00210
00211
00212 def _add_to_cache(cache, lock, key, value):
00213 lock.acquire()
00214 cache[key] = value
00215 lock.release()
00216
00217
00218 def _get_from_cache(cache, lock, key):
00219 """ Returns the value for the specified key from the cache.
00220 Locks the lock before doing anything. Returns None if key not in cache """
00221 lock.acquire()
00222 ret = None
00223 if key in cache:
00224 ret = cache[key]
00225 lock.release()
00226 return ret