00001
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 Support library for Python autogenerated message files. This defines
00035 the Message base class used by genpy as well as support
00036 libraries for type checking and retrieving message classes by type
00037 name.
00038 """
00039
00040 import math
00041 import itertools
00042 import struct
00043 import sys
00044
00045 import genmsg
00046
00047 from base import is_simple
00048 from rostime import Time, Duration, TVal
00049
00050
00051
00052
00053
00054 if sys.version > '3':
00055 long = int
00056
00057 struct_I = struct.Struct('<I')
00058
00059 def isstring(s):
00060 """Small helper version to check an object is a string in a way that works
00061 for both Python 2 and 3
00062 """
00063 try:
00064 return isinstance(s, basestring)
00065 except NameError:
00066 return isinstance(s, str)
00067
00068 class MessageException(Exception):
00069 """
00070 Base exception type for errors in genmsg routines
00071 """
00072 pass
00073
00074 class DeserializationError(MessageException):
00075 """Message deserialization error"""
00076 pass
00077
00078 class SerializationError(MessageException):
00079 """Message serialization error"""
00080 pass
00081
00082
00083
00084 def strify_message(val, indent='', time_offset=None, current_time=None, field_filter=None, fixed_numeric_width=None):
00085 """
00086 Convert value to string representation
00087 :param val: to convert to string representation. Most likely a Message. ``Value``
00088 :param indent: indentation. If indent is set, then the return value will have a leading \n, ``str``
00089 :param time_offset: if not None, time fields will be displayed
00090 as deltas from time_offset, ``Time``
00091
00092 :param current_time: currently not used. Only provided for API
00093 compatibility. current_time passes in the current time with
00094 respect to the message, ``Time``
00095 :param field_filter: filter the fields that are strified for Messages, ``fn(Message)->iter(str)``
00096 :returns: string (YAML) representation of message, ``str``
00097 """
00098
00099 type_ = type(val)
00100 if type_ in (int, long, float) and fixed_numeric_width is not None:
00101 if type_ is float:
00102 num_str = ('%.' + str(fixed_numeric_width) + 'f') % val
00103 return num_str[:max(num_str.find('.'), fixed_numeric_width)]
00104 else:
00105 return ('%' + str(fixed_numeric_width) + 'd') % val
00106 elif type_ in (int, long, float, bool):
00107 return str(val)
00108 elif isstring(val):
00109
00110 if not val:
00111 return "''"
00112 return val
00113 elif isinstance(val, TVal):
00114
00115 if time_offset is not None and isinstance(val, Time):
00116 val = val-time_offset
00117
00118 if fixed_numeric_width is not None:
00119 format_str = '%' + str(fixed_numeric_width) + 'd'
00120 sec_str = '\n%ssecs: ' % indent + (format_str % val.secs)
00121 nsec_str = '\n%snsecs: ' % indent + (format_str % val.nsecs)
00122 return sec_str + nsec_str
00123 else:
00124 return '\n%ssecs: %s\n%snsecs: %s'%(indent, val.secs, indent, val.nsecs)
00125
00126 elif type_ in (list, tuple):
00127 if len(val) == 0:
00128 return "[]"
00129 val0 = val[0]
00130 if type(val0) in (int, float) and fixed_numeric_width is not None:
00131 list_str = '[' + ''.join(strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) + ', ' for v in val).rstrip(', ') + ']'
00132 return list_str
00133 elif type(val0) in (int, float, str, bool):
00134
00135 return str(list(val))
00136 else:
00137 pref = indent + '- '
00138 indent = indent + ' '
00139 return '\n'+'\n'.join([pref+strify_message(v, indent, time_offset, current_time, field_filter, fixed_numeric_width) for v in val])
00140 elif isinstance(val, Message):
00141
00142 if field_filter is not None:
00143 fields = list(field_filter(val))
00144 else:
00145 fields = val.__slots__
00146
00147 p = '%s%%s: %%s'%(indent)
00148 ni = ' '+indent
00149 if sys.hexversion > 0x03000000:
00150 vals = '\n'.join([p%(f,
00151 strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f,t in zip(val.__slots__, val._slot_types) if f in fields])
00152 else:
00153 vals = '\n'.join([p%(f,
00154 strify_message(_convert_getattr(val, f, t), ni, time_offset, current_time, field_filter, fixed_numeric_width)) for f,t in itertools.izip(val.__slots__, val._slot_types) if f in fields])
00155 if indent:
00156 return '\n'+vals
00157 else:
00158 return vals
00159
00160 else:
00161 return str(val)
00162
00163 def _convert_getattr(val, f, t):
00164 """
00165 Convert atttribute types on the fly, if necessary. This is mainly
00166 to convert uint8[] fields back to an array type.
00167 """
00168 attr = getattr(val, f)
00169 if isstring(attr) and 'uint8[' in t:
00170 return [ord(x) for x in attr]
00171 else:
00172 return attr
00173
00174
00175
00176
00177
00178
00179
00180 _widths = {
00181 'byte': 8, 'char': 8, 'int8': 8, 'uint8': 8,
00182 'int16': 16, 'uint16': 16,
00183 'int32': 32, 'uint32': 32,
00184 'int64': 64, 'uint64': 64,
00185 }
00186
00187 def check_type(field_name, field_type, field_val):
00188 """
00189 Dynamic type checker that maps ROS .msg types to python types and
00190 verifies the python value. check_type() is not designed to be
00191 fast and is targeted at error diagnosis. This type checker is not
00192 designed to run fast and is meant only for error diagnosis.
00193
00194 :param field_name: ROS .msg field name, ``str``
00195 :param field_type: ROS .msg field type, ``str``
00196 :param field_val: field value, ``Any``
00197 :raises: :exc:`SerializationError` If typecheck fails
00198 """
00199 if is_simple(field_type):
00200
00201 if field_type in ['byte', 'int8', 'int16', 'int32', 'int64']:
00202 if type(field_val) not in [long, int]:
00203 raise SerializationError('field %s must be an integer type'%field_name)
00204 maxval = int(math.pow(2, _widths[field_type]-1))
00205 if field_val >= maxval or field_val <= -maxval:
00206 raise SerializationError('field %s exceeds specified width [%s]'%(field_name, field_type))
00207 elif field_type in ['char', 'uint8', 'uint16', 'uint32', 'uint64']:
00208 if type(field_val) not in [long, int] or field_val < 0:
00209 raise SerializationError('field %s must be unsigned integer type'%field_name)
00210 maxval = int(math.pow(2, _widths[field_type]))
00211 if field_val >= maxval:
00212 raise SerializationError('field %s exceeds specified width [%s]'%(field_name, field_type))
00213 elif field_type == 'bool':
00214 if field_val not in [True, False, 0, 1]:
00215 raise SerializationError('field %s is not a bool'%(field_name))
00216 elif field_type == 'string':
00217 if sys.hexversion > 0x03000000:
00218 if type(field_val) == str:
00219 try:
00220 field_val.encode('ascii')
00221 except UnicodeEncodeError:
00222 raise SerializationError('field %s is a non-ascii string'%field_name)
00223 elif not type(field_val) == bytes:
00224 raise SerializationError('field %s must be of type bytes or an ascii string'%field_name)
00225 else:
00226 if type(field_val) == unicode:
00227 raise SerializationError('field %s is a unicode string instead of an ascii string'%field_name)
00228 elif not isstring(field_val):
00229 raise SerializationError('field %s must be of type str'%field_name)
00230 elif field_type == 'time':
00231 if not isinstance(field_val, Time):
00232 raise SerializationError('field %s must be of type Time'%field_name)
00233 elif field_type == 'duration':
00234 if not isinstance(field_val, Duration):
00235 raise SerializationError('field %s must be of type Duration'%field_name)
00236
00237 elif field_type.endswith(']'):
00238
00239 base_type = field_type[:field_type.index('[')]
00240
00241 if type(field_val) == str:
00242 if not base_type in ['char', 'uint8']:
00243 raise SerializationError('field %s must be a list or tuple type. Only uint8[] can be a string' % field_name);
00244 else:
00245
00246
00247
00248 return
00249
00250 if not type(field_val) in [list, tuple]:
00251 raise SerializationError('field %s must be a list or tuple type'%field_name)
00252 for v in field_val:
00253 check_type(field_name+"[]", base_type, v)
00254 else:
00255 if isinstance(field_val, Message):
00256
00257 if field_val._type in ['std_msgs/Header', 'roslib/Header']:
00258 if field_type not in ['Header', 'std_msgs/Header', 'roslib/Header']:
00259 raise SerializationError("field %s must be a Header instead of a %s"%(field_name, field_val._type))
00260 elif field_val._type != field_type:
00261 raise SerializationError("field %s must be of type %s instead of %s"%(field_name, field_type, field_val._type))
00262 for n, t in zip(field_val.__slots__, field_val._get_types()):
00263 check_type("%s.%s"%(field_name,n), t, getattr(field_val, n))
00264 else:
00265 raise SerializationError("field %s must be of type [%s]"%(field_name, field_type))
00266
00267
00268
00269 class Message(object):
00270 """Base class of Message data classes auto-generated from msg files. """
00271
00272
00273
00274
00275
00276
00277 __slots__ = ['_connection_header']
00278
00279 def __init__(self, *args, **kwds):
00280 """
00281 Create a new Message instance. There are multiple ways of
00282 initializing Message instances, either using a 1-to-1
00283 correspondence between constructor arguments and message
00284 fields (*args), or using Python "keyword" arguments (**kwds) to initialize named field
00285 and leave the rest with default values.
00286 """
00287 if args and kwds:
00288 raise TypeError("Message constructor may only use args OR keywords, not both")
00289 if args:
00290 if len(args) != len(self.__slots__):
00291 raise TypeError("Invalid number of arguments, args should be %s"%str(self.__slots__)+" args are"+str(args))
00292 for i, k in enumerate(self.__slots__):
00293 setattr(self, k, args[i])
00294 else:
00295
00296 for k,v in kwds.items():
00297 if not k in self.__slots__:
00298 raise AttributeError("%s is not an attribute of %s"%(k, self.__class__.__name__))
00299
00300
00301
00302 for k in self.__slots__:
00303 if k in kwds:
00304 setattr(self, k, kwds[k])
00305 else:
00306 setattr(self, k, None)
00307
00308 def __getstate__(self):
00309 """
00310 support for Python pickling
00311 """
00312 return [getattr(self, x) for x in self.__slots__]
00313
00314 def __setstate__(self, state):
00315 """
00316 support for Python pickling
00317 """
00318 for x, val in zip(self.__slots__, state):
00319 setattr(self, x, val)
00320
00321 def _get_types(self):
00322 raise Exception("must be overriden")
00323 def _check_types(self, exc=None):
00324 """
00325 Perform dynamic type-checking of Message fields. This is performance intensive
00326 and is meant for post-error diagnosis
00327 :param exc: underlying exception that gave cause for type check, ``Exception``
00328 :raises: exc:`genpy.SerializationError` If typecheck fails
00329 """
00330 for n, t in zip(self.__slots__, self._get_types()):
00331 check_type(n, t, getattr(self, n))
00332 if exc:
00333 raise SerializationError(str(exc))
00334
00335 def serialize(self, buff):
00336 """
00337 Serialize data into buffer
00338 :param buff: buffer, ``StringIO``
00339 """
00340 pass
00341 def deserialize(self, str):
00342 """
00343 Deserialize data in str into this instance
00344 :param str: serialized data, ``str``
00345 """
00346 pass
00347 def __repr__(self):
00348 return strify_message(self)
00349 def __str__(self):
00350 return strify_message(self)
00351
00352 def __eq__(self, other):
00353 if not isinstance(other, self.__class__):
00354 return False
00355 for f in self.__slots__:
00356 try:
00357 v1 = getattr(self, f)
00358 v2 = getattr(other, f)
00359 if type(v1) in (list, tuple) and type(v2) in (list, tuple):
00360
00361 if tuple(v1) != tuple(v2):
00362 return False
00363 elif not v1 == v2:
00364 return False
00365 except AttributeError:
00366 return False
00367 return True
00368
00369
00370 def get_printable_message_args(msg, buff=None, prefix=''):
00371 """
00372 Get string representation of msg arguments
00373 :param msg: msg message to fill, ``Message``
00374 :param prefix: field name prefix (for verbose printing), ``str``
00375 :returns: printable representation of msg args, ``str``
00376 """
00377 try:
00378 from cStringIO import StringIO
00379 python3 = 0
00380 except ImportError:
00381 from io import BytesIO
00382 python3 = 1
00383
00384 if buff is None:
00385 if python3 == 1:
00386 buff = BytesIO()
00387 else:
00388 buff = StringIO()
00389 for f in msg.__slots__:
00390 if isinstance(getattr(msg, f), Message):
00391 get_printable_message_args(getattr(msg, f), buff=buff, prefix=(prefix+f+'.'))
00392 else:
00393 buff.write(prefix+f+' ')
00394 return buff.getvalue().rstrip()
00395
00396 def _fill_val(msg, f, v, keys, prefix):
00397 """
00398 Subroutine of L{_fill_message_args()}. Sets a particular field on a message
00399 :param f: field name, ``str``
00400 :param v: field value
00401 :param keys: keys to use as substitute values for messages and timestamps, ``dict``
00402 :raises: exc:`MessageException`
00403 """
00404 if not f in msg.__slots__:
00405 raise MessageException("No field name [%s%s]"%(prefix, f))
00406 def_val = getattr(msg, f)
00407 if isinstance(def_val, Message) or isinstance(def_val, TVal):
00408
00409 if type(v) == str:
00410 if v in keys:
00411 setattr(msg, f, keys[v])
00412 else:
00413 raise MessageException("No key named [%s]"%(v))
00414 elif isinstance(def_val, TVal) and type(v) in (int, long):
00415
00416
00417 if isinstance(def_val, Time):
00418 setattr(msg, f, Time.from_sec(v/1e9))
00419 elif isinstance(def_val, Duration):
00420 setattr(msg, f, Duration.from_sec(v/1e9))
00421 else:
00422 raise MessageException("Cannot create time values of type [%s]"%(type(def_val)))
00423 else:
00424 _fill_message_args(def_val, v, keys, prefix=(prefix+f+'.'))
00425 elif type(def_val) == list:
00426 if not type(v) in [list, tuple]:
00427 raise MessageException("Field [%s%s] must be a list or tuple instead of: %s"%(prefix, f, type(v).__name__))
00428
00429 idx = msg.__slots__.index(f)
00430 t = msg._slot_types[idx]
00431 base_type, is_array, length = genmsg.msgs.parse_type(t)
00432
00433
00434 if base_type in genmsg.msgs.PRIMITIVE_TYPES:
00435
00436 if length is not None and len(v) != length:
00437 raise MessageException("Field [%s%s] has incorrect number of elements: %s != %s"%(prefix, f, len(v), length))
00438 setattr(msg, f, v)
00439
00440
00441 else:
00442
00443 if length is not None and len(v) != length:
00444 raise MessageException("Field [%s%s] has incorrect number of elements: %s != %s"%(prefix, f, len(v), length))
00445 list_msg_class = get_message_class(base_type)
00446 if list_msg_class is None:
00447 raise MessageException("Cannot instantiate messages for field [%s%s] : cannot load class %s"%(prefix, f, base_type))
00448 del def_val[:]
00449 for el in v:
00450 inner_msg = list_msg_class()
00451 if isinstance(inner_msg, TVal) and type(el) in (int, long):
00452
00453
00454 if isinstance(inner_msg, Time):
00455 inner_msg = Time.from_sec(el/1e9)
00456 elif isinstance(inner_msg, Duration):
00457 inner_msg = Duration.from_sec(el/1e9)
00458 else:
00459 raise MessageException("Cannot create time values of type [%s]"%(type(inner_msg)))
00460 else:
00461 _fill_message_args(inner_msg, el, keys, prefix)
00462 def_val.append(inner_msg)
00463 else:
00464 setattr(msg, f, v)
00465
00466
00467 def _fill_message_args(msg, msg_args, keys, prefix=''):
00468 """
00469 Populate message with specified args.
00470
00471 :param msg: message to fill, ``Message``
00472 :param msg_args: list of arguments to set fields to, ``[args]``
00473 :param keys: keys to use as substitute values for messages and timestamps. ``dict``
00474 :param prefix: field name prefix (for verbose printing), ``str``
00475 :returns: unused/leftover message arguments. ``[args]``
00476 :raise :exc:`MessageException` If not enough message arguments to fill message
00477 :raises: :exc:`ValueError` If msg or msg_args is not of correct type
00478 """
00479 if not isinstance(msg, (Message, TVal)):
00480 raise ValueError("msg must be a Message instance: %s"%msg)
00481
00482 if type(msg_args) == dict:
00483
00484
00485
00486
00487 for f, v in msg_args.items():
00488
00489 if v == None:
00490 v = ''
00491 _fill_val(msg, f, v, keys, prefix)
00492 elif type(msg_args) == list:
00493
00494
00495
00496
00497 if len(msg_args) > len(msg.__slots__):
00498 raise MessageException("Too many arguments:\n * Given: %s\n * Expected: %s"%(msg_args, msg.__slots__))
00499 elif len(msg_args) < len(msg.__slots__):
00500 raise MessageException("Not enough arguments:\n * Given: %s\n * Expected: %s"%(msg_args, msg.__slots__))
00501
00502 for f, v in zip(msg.__slots__, msg_args):
00503 _fill_val(msg, f, v, keys, prefix)
00504 else:
00505 raise ValueError("invalid msg_args type: %s"%str(msg_args))
00506
00507 def fill_message_args(msg, msg_args, keys={}):
00508 """
00509 Populate message with specified args. Args are assumed to be a
00510 list of arguments from a command-line YAML parser. See
00511 http://www.ros.org/wiki/ROS/YAMLCommandLine for specification on
00512 how messages are filled.
00513
00514 fill_message_args also takes in an optional 'keys' dictionary
00515 which contain substitute values for message and time types. These
00516 values must be of the correct instance type, i.e. a Message, Time,
00517 or Duration. In a string key is encountered with these types, the
00518 value from the keys dictionary will be used instead. This is
00519 mainly used to provide values for the 'now' timestamp.
00520
00521 :param msg: message to fill, ``Message``
00522 :param msg_args: list of arguments to set fields to, or
00523 If None, msg_args will be made an empty list., ``[args]``
00524 :param keys: keys to use as substitute values for messages and timestamps, ``dict``
00525 :raises: :exc:`MessageException` If not enough/too many message arguments to fill message
00526 """
00527
00528
00529
00530
00531
00532 if msg_args is None:
00533 msg_args = []
00534
00535
00536
00537
00538
00539
00540 if len(msg_args) == 1 and type(msg_args[0]) == dict:
00541
00542
00543 _fill_message_args(msg, msg_args[0], keys, '')
00544 else:
00545 _fill_message_args(msg, msg_args, keys, '')
00546
00547 def _get_message_or_service_class(type_str, message_type, reload_on_error=False):
00548 """
00549 Utility for retrieving message/service class instances. Used by
00550 get_message_class and get_service_class.
00551 :param type_str: 'msg' or 'srv', ``str``
00552 :param message_type: type name of message/service, ``str``
00553 :returns: Message/Service for message/service type or None, ``class``
00554 :raises: :exc:`ValueError` If message_type is invalidly specified
00555 """
00556 if message_type == 'time':
00557 return Time
00558 if message_type == 'duration':
00559 return Duration
00560
00561 package, base_type = genmsg.package_resource_name(message_type)
00562 if not package:
00563 if base_type == 'Header':
00564 package = 'std_msgs'
00565 else:
00566 raise ValueError("message type is missing package name: %s"%str(message_type))
00567 pypkg = val = None
00568 try:
00569
00570 pypkg = __import__('%s.%s' % (package, type_str))
00571 except ImportError:
00572
00573 try:
00574 from roslib import load_manifest
00575 from rospkg import ResourceNotFound
00576 try:
00577 load_manifest(package)
00578 try:
00579 pypkg = __import__('%s.%s' % (package, type_str))
00580 except ImportError:
00581 pass
00582 except ResourceNotFound:
00583 pass
00584 except ImportError:
00585 pass
00586 if pypkg:
00587 try:
00588 val = getattr(getattr(pypkg, type_str), base_type)
00589 except AttributeError:
00590 pass
00591
00592
00593
00594 if val is None and reload_on_error:
00595 try:
00596 if pypkg:
00597 reload(pypkg)
00598 val = getattr(getattr(pypkg, type_str), base_type)
00599 except:
00600 val = None
00601 return val
00602
00603
00604 _message_class_cache = {}
00605
00606 def get_message_class(message_type, reload_on_error=False):
00607 """
00608 Get the message class. NOTE: this function maintains a
00609 local cache of results to improve performance.
00610 :param message_type: type name of message, ``str``
00611 :param reload_on_error: (optional). Attempt to reload the Python
00612 module if unable to load message the first time. Defaults to
00613 False. This is necessary if messages are built after the first load.
00614 :returns: Message class for message/service type, ``Message class``
00615 :raises :exc:`ValueError`: if message_type is invalidly specified
00616 """
00617 if message_type in _message_class_cache:
00618 return _message_class_cache[message_type]
00619 cls = _get_message_or_service_class('msg', message_type, reload_on_error=reload_on_error)
00620 if cls:
00621 _message_class_cache[message_type] = cls
00622 return cls
00623
00624
00625 _service_class_cache = {}
00626
00627 def get_service_class(service_type, reload_on_error=False):
00628 """
00629 Get the service class. NOTE: this function maintains a
00630 local cache of results to improve performance.
00631 :param service_type: type name of service, ``str``
00632 :param reload_on_error: (optional). Attempt to reload the Python
00633 module if unable to load message the first time. Defaults to
00634 False. This is necessary if messages are built after the first load.
00635 :returns: Service class for service type, ``Service class``
00636 :raises :exc:`Exception` If service_type is invalidly specified
00637 """
00638 if service_type in _service_class_cache:
00639 return _service_class_cache[service_type]
00640 cls = _get_message_or_service_class('srv', service_type, reload_on_error=reload_on_error)
00641 _service_class_cache[service_type] = cls
00642 return cls
00643