Package roslib_electric :: Module msgs
[frames] | no frames]

Source Code for Module roslib_electric.msgs

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2008, Willow Garage, Inc. 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Willow Garage, Inc. nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32  # 
 33  # Revision $Id: msgs.py 14304 2011-07-13 08:00:40Z kwc $ 
 34  # $Author: kwc $ 
 35   
 36  from __future__ import print_function 
 37   
 38  """ 
 39  ROS msg library for Python 
 40   
 41  Implements: U{http://ros.org/wiki/msg} 
 42  """ 
 43   
 44  try: 
 45      from cStringIO import StringIO # Python 2.x 
 46  except ImportError: 
 47      from io import StringIO # Python 3.x 
 48   
 49  import os 
 50  import itertools 
 51  import sys 
 52  import re 
 53  import string 
 54   
 55  import roslib_electric.exceptions 
 56  import roslib_electric.manifest 
 57  import roslib_electric.packages 
 58  import roslib_electric.names 
 59  import roslib_electric.resources 
 60  import roslib_electric.rospack 
 61   
 62  VERBOSE = False 
 63   
 64  ## @return: True if msg-related scripts should print verbose output 
65 -def is_verbose():
66 return VERBOSE
67 68 ## set whether msg-related scripts should print verbose output
69 -def set_verbose(v):
70 global VERBOSE 71 VERBOSE = v
72 73 EXT = roslib_electric.names.MSG_EXT #alias 74 SEP = roslib_electric.names.PRN_SEPARATOR #e.g. std_msgs/String 75 ## character that designates a constant assignment rather than a field 76 CONSTCHAR = '=' 77 COMMENTCHAR = '#' 78
79 -class MsgSpecException(roslib_electric.exceptions.ROSLibException): pass
80 81 #TODOXXX: unit test
82 -def base_msg_type(type_):
83 """ 84 Compute the base data type, e.g. for arrays, get the underlying array item type 85 @param type_: ROS msg type (e.g. 'std_msgs/String') 86 @type type_: str 87 @return: base type 88 @rtype: str 89 """ 90 if type_ is None: 91 return None 92 if '[' in type_: 93 return type_[:type_.find('[')] 94 return type_
95
96 -def resolve_type(type_, package_context):
97 """ 98 Resolve type name based on current package context. 99 100 NOTE: in ROS Diamondback, 'Header' resolves to 101 'std_msgs/Header'. In previous releases, it resolves to 102 'roslib/Header' (REP 100). 103 104 e.g.:: 105 resolve_type('String', 'std_msgs') -> 'std_msgs/String' 106 resolve_type('String[]', 'std_msgs') -> 'std_msgs/String[]' 107 resolve_type('std_msgs/String', 'foo') -> 'std_msgs/String' 108 resolve_type('uint16', 'std_msgs') -> 'uint16' 109 resolve_type('uint16[]', 'std_msgs') -> 'uint16[]' 110 """ 111 bt = base_msg_type(type_) 112 if bt in BUILTIN_TYPES: 113 return type_ 114 elif bt == 'Header': 115 return 'std_msgs/Header' 116 elif SEP in type_: 117 return type_ 118 else: 119 return "%s%s%s"%(package_context, SEP, type_)
120 121 #NOTE: this assumes that we aren't going to support multi-dimensional 122
123 -def parse_type(type_):
124 """ 125 Parse ROS message field type 126 @param type_: ROS field type 127 @type type_: str 128 @return: base_type, is_array, array_length 129 @rtype: str, bool, int 130 @raise MsgSpecException: if type_ cannot be parsed 131 """ 132 if not type_: 133 raise MsgSpecException("Invalid empty type") 134 if '[' in type_: 135 var_length = type_.endswith('[]') 136 splits = type_.split('[') 137 if len(splits) > 2: 138 raise MsgSpecException("Currently only support 1-dimensional array types: %s"%type_) 139 if var_length: 140 return type_[:-2], True, None 141 else: 142 try: 143 length = int(splits[1][:-1]) 144 return splits[0], True, length 145 except ValueError: 146 raise MsgSpecException("Invalid array dimension: [%s]"%splits[1][:-1]) 147 else: 148 return type_, False, None
149 150 ################################################################################ 151 # name validation 152
153 -def is_valid_msg_type(x):
154 """ 155 @return: True if the name is a syntatically legal message type name 156 @rtype: bool 157 """ 158 if not x or len(x) != len(x.strip()): 159 return False 160 base = base_msg_type(x) 161 if not roslib_electric.names.is_legal_resource_name(base): 162 return False 163 #parse array indicies 164 x = x[len(base):] 165 state = 0 166 i = 0 167 for c in x: 168 if state == 0: 169 if c != '[': 170 return False 171 state = 1 #open 172 elif state == 1: 173 if c == ']': 174 state = 0 #closed 175 else: 176 try: 177 string.atoi(c) 178 except: 179 return False 180 return state == 0
181
182 -def is_valid_constant_type(x):
183 """ 184 @return: True if the name is a legal constant type. Only simple types are allowed. 185 @rtype: bool 186 """ 187 return x in PRIMITIVE_TYPES
188
189 -def is_valid_msg_field_name(x):
190 """ 191 @return: True if the name is a syntatically legal message field name 192 @rtype: bool 193 """ 194 return roslib_electric.names.is_legal_resource_base_name(x)
195 196 # msg spec representation ########################################## 197
198 -class Constant(object):
199 """ 200 Container class for holding a Constant declaration 201 """ 202 __slots__ = ['type', 'name', 'val', 'val_text'] 203
204 - def __init__(self, type_, name, val, val_text):
205 """ 206 @param type_: constant type 207 @type type_: str 208 @param name: constant name 209 @type name: str 210 @param val: constant value 211 @type val: str 212 @param val_text: Original text definition of \a val 213 @type val_text: str 214 """ 215 if type is None or name is None or val is None or val_text is None: 216 raise ValueError('Constant must have non-None parameters') 217 self.type = type_ 218 self.name = name.strip() #names are always stripped of whitespace 219 self.val = val 220 self.val_text = val_text
221
222 - def __eq__(self, other):
223 if not isinstance(other, Constant): 224 return False 225 return self.type == other.type and self.name == other.name and self.val == other.val
226
227 - def __repr__(self):
228 return "%s %s=%s"%(self.type, self.name, self.val)
229
230 - def __str__(self):
231 return "%s %s=%s"%(self.type, self.name, self.val)
232
233 -def _strify_spec(spec, buff=None, indent=''):
234 """ 235 Convert spec into a string representation. Helper routine for MsgSpec. 236 @param indent: internal use only 237 @type indent: str 238 @param buff: internal use only 239 @type buff: StringIO 240 @return: string representation of spec 241 @rtype: str 242 """ 243 if buff is None: 244 buff = StringIO() 245 for c in spec.constants: 246 buff.write("%s%s %s=%s\n"%(indent, c.type, c.name, c.val_text)) 247 for type_, name in zip(spec.types, spec.names): 248 buff.write("%s%s %s\n"%(indent, type_, name)) 249 base_type = base_msg_type(type_) 250 if not base_type in BUILTIN_TYPES: 251 subspec = get_registered(base_type) 252 _strify_spec(subspec, buff, indent + ' ') 253 return buff.getvalue()
254
255 -class Field(object):
256 """ 257 Container class for storing information about a single field in a MsgSpec 258 259 Contains: 260 name 261 type 262 base_type 263 is_array 264 array_len 265 is_builtin 266 is_header 267 """ 268
269 - def __init__(self, name, type):
270 self.name = name 271 self.type = type 272 (self.base_type, self.is_array, self.array_len) = parse_type(type) 273 self.is_header = is_header_type(self.base_type) 274 self.is_builtin = is_builtin(self.base_type)
275
276 - def __repr__(self):
277 return "[%s, %s, %s, %s, %s]"%(self.name, self.type, self.base_type, self.is_array, self.array_len)
278
279 -class MsgSpec(object):
280 """ 281 Container class for storing loaded msg description files. Field 282 types and names are stored in separate lists with 1-to-1 283 correspondence. MsgSpec can also return an md5 of the source text. 284 """ 285
286 - def __init__(self, types, names, constants, text, full_name = '', short_name = '', package = ''):
287 """ 288 @param types: list of field types, in order of declaration 289 @type types: [str] 290 @param names: list of field names, in order of declaration 291 @type names: [str] 292 @param constants: Constant declarations 293 @type constants: [L{Constant}] 294 @param text: text of declaration 295 @type text: str 296 @raise MsgSpecException: if spec is invalid (e.g. fields with the same name) 297 """ 298 self.types = types 299 if len(set(names)) != len(names): 300 raise MsgSpecException("Duplicate field names in message: %s"%names) 301 self.names = names 302 self.constants = constants 303 assert len(self.types) == len(self.names), "len(%s) != len(%s)"%(self.types, self.names) 304 #Header.msg support 305 if (len(self.types)): 306 self.header_present = self.types[0] == HEADER and self.names[0] == 'header' 307 else: 308 self.header_present = False 309 self.text = text 310 self.full_name = full_name 311 self.short_name = short_name 312 self.package = package 313 self._parsed_fields = [Field(name, type) for (name, type) in zip(self.names, self.types)]
314
315 - def fields(self):
316 """ 317 @return: zip list of types and names (e.g. [('int32', 'x'), ('int32', 'y')] 318 @rtype: [(str,str),] 319 """ 320 return list(zip(self.types, self.names)) #py3k
321
322 - def parsed_fields(self):
323 """ 324 @return: list of Field classes 325 @rtype: [Field,] 326 """ 327 return self._parsed_fields
328
329 - def has_header(self):
330 """ 331 @return: True if msg decription contains a 'Header header' 332 declaration at the beginning 333 @rtype: bool 334 """ 335 return self.header_present
336 - def __eq__(self, other):
337 if not other or not isinstance(other, MsgSpec): 338 return False 339 return self.types == other.types and self.names == other.names and \ 340 self.constants == other.constants and self.text == other.text
341 - def __ne__(self, other):
342 if not other or not isinstance(other, MsgSpec): 343 return True 344 return not self.__eq__(other)
345
346 - def __repr__(self):
347 if self.constants: 348 return "MsgSpec[%s, %s, %s]"%(repr(self.constants), repr(self.types), repr(self.names)) 349 else: 350 return "MsgSpec[%s, %s]"%(repr(self.types), repr(self.names))
351
352 - def __str__(self):
353 return _strify_spec(self)
354 355 # msg spec loading utilities ########################################## 356
357 -def reinit():
358 """ 359 Reinitialize roslib_electric.msgs. This API is for message generators 360 (e.g. genpy) that need to re-initialize the registration table. 361 """ 362 global _initialized , _loaded_packages 363 # unset the initialized state and unregister everything 364 _initialized = False 365 del _loaded_packages[:] 366 REGISTERED_TYPES.clear() 367 _init()
368 369 _initialized = False
370 -def _init():
371 #lazy-init 372 global _initialized 373 if _initialized: 374 return 375 376 fname = '%s%s'%(HEADER, EXT) 377 std_msgs_dir = roslib_electric.packages.get_pkg_dir('std_msgs') 378 if std_msgs_dir is None: 379 raise MsgSpecException("Unable to locate roslib: %s files cannot be loaded"%EXT) 380 381 header = os.path.join(std_msgs_dir, roslib_electric.packages.MSG_DIR, fname) 382 if not os.path.isfile(header): 383 sys.stderr.write("ERROR: cannot locate %s. Expected to find it at '%s'\n"%(fname, header)) 384 return False 385 386 # register Header under both contexted and de-contexted name 387 _, spec = load_from_file(header, '') 388 register(HEADER, spec) 389 register('std_msgs/'+HEADER, spec) 390 # backwards compat, REP 100 391 register('roslib/'+HEADER, spec) 392 for k, spec in EXTENDED_BUILTINS.items(): 393 register(k, spec) 394 395 _initialized = True
396 397 # .msg file routines ############################################################## 398
399 -def _msg_filter(f):
400 """ 401 Predicate for filtering directory list. matches message files 402 @param f: filename 403 @type f: str 404 """ 405 return os.path.isfile(f) and f.endswith(EXT)
406 407 # also used by doxymaker
408 -def list_msg_types(package, include_depends):
409 """ 410 List all messages in the specified package 411 @param package str: name of package to search 412 @param include_depends bool: if True, will also list messages in package dependencies 413 @return [str]: message type names 414 """ 415 types = roslib_electric.resources.list_package_resources(package, include_depends, roslib_electric.packages.MSG_DIR, _msg_filter) 416 return [x[:-len(EXT)] for x in types]
417
418 -def msg_file(package, type_):
419 """ 420 Determine the file system path for the specified .msg 421 resource. .msg resource does not have to exist. 422 423 @param package: name of package .msg file is in 424 @type package: str 425 @param type_: type name of message, e.g. 'Point2DFloat32' 426 @type type_: str 427 @return: file path of .msg file in specified package 428 @rtype: str 429 """ 430 return roslib_electric.packages.resource_file(package, roslib_electric.packages.MSG_DIR, type_+EXT)
431
432 -def get_pkg_msg_specs(package):
433 """ 434 List all messages that a package contains. 435 436 @param package: package to load messages from 437 @type package: str 438 @return: list of message type names and specs for package, as well as a list 439 of message names that could not be processed. 440 @rtype: [(str, L{MsgSpec}), [str]] 441 """ 442 _init() 443 types = list_msg_types(package, False) 444 specs = [] #no fancy list comprehension as we want to show errors 445 failures = [] 446 for t in types: 447 try: 448 typespec = load_from_file(msg_file(package, t), package) 449 specs.append(typespec) 450 except Exception as e: 451 failures.append(t) 452 print("ERROR: unable to load %s"%t) 453 return specs, failures
454
455 -def load_package_dependencies(package, load_recursive=False):
456 """ 457 Register all messages that the specified package depends on. 458 459 @param load_recursive: (optional) if True, load all dependencies, 460 not just direct dependencies. By default, this is false to 461 prevent packages from incorrectly inheriting dependencies. 462 @type load_recursive: bool 463 """ 464 global _loaded_packages 465 _init() 466 if VERBOSE: 467 print("Load dependencies for package", package) 468 469 if not load_recursive: 470 manifest_file = roslib_electric.manifest.manifest_file(package, True) 471 m = roslib_electric.manifest.parse_file(manifest_file) 472 depends = [d.package for d in m.depends] # #391 473 else: 474 depends = roslib_electric.rospack.rospack_depends(package) 475 476 msgs = [] 477 failures = [] 478 for d in depends: 479 if VERBOSE: 480 print("Load dependency", d) 481 #check if already loaded 482 # - we are dependent on manifest.getAll returning first-order dependencies first 483 if d in _loaded_packages or d == package: 484 continue 485 _loaded_packages.append(d) 486 specs, failed = get_pkg_msg_specs(d) 487 msgs.extend(specs) 488 failures.extend(failed) 489 for key, spec in msgs: 490 register(key, spec)
491
492 -def load_package(package):
493 """ 494 Load package into the local registered namespace. All messages found 495 in the package will be registered if they are successfully 496 loaded. This should only be done with one package (i.e. the 'main' 497 package) per Python instance. 498 499 @param package: package name 500 @type package: str 501 """ 502 global _loaded_packages 503 _init() 504 if VERBOSE: 505 print("Load package", package) 506 507 #check if already loaded 508 # - we are dependent on manifest.getAll returning first-order dependencies first 509 if package in _loaded_packages: 510 if VERBOSE: 511 print("Package %s is already loaded"%package) 512 return 513 514 _loaded_packages.append(package) 515 specs, failed = get_pkg_msg_specs(package) 516 if VERBOSE: 517 print("Package contains the following messages: %s"%specs) 518 for key, spec in specs: 519 #register spec under both local and fully-qualified key 520 register(key, spec) 521 register(package + roslib_electric.names.PRN_SEPARATOR + key, spec)
522
523 -def _convert_val(type_, val):
524 """ 525 Convert constant value declaration to python value. Does not do 526 type-checking, so ValueError or other exceptions may be raised. 527 528 @param type_: ROS field type 529 @type type_: str 530 @param val: string representation of constant 531 @type val: str: 532 @raise ValueError: if unable to convert to python representation 533 @raise MsgSpecException: if value exceeds specified integer width 534 """ 535 if type_ in ['float32','float64']: 536 return float(val) 537 elif type_ in ['string']: 538 return val.strip() #string constants are always stripped 539 elif type_ in ['int8', 'uint8', 'int16','uint16','int32','uint32','int64','uint64', 'char', 'byte']: 540 # bounds checking 541 bits = [('int8', 8), ('uint8', 8), ('int16', 16),('uint16', 16),\ 542 ('int32', 32),('uint32', 32), ('int64', 64),('uint64', 64),\ 543 ('byte', 8), ('char', 8)] 544 b = [b for t, b in bits if t == type_][0] 545 import math 546 if type_[0] == 'u' or type_ == 'char': 547 lower = 0 548 upper = int(math.pow(2, b)-1) 549 else: 550 upper = int(math.pow(2, b-1)-1) 551 lower = -upper - 1 #two's complement min 552 val = int(val) #python will autocast to long if necessary 553 if val > upper or val < lower: 554 raise MsgSpecException("cannot coerce [%s] to %s (out of bounds)"%(val, type_)) 555 return val 556 elif type_ == 'bool': 557 # TODO: need to nail down constant spec for bool 558 return True if eval(val) else False 559 raise MsgSpecException("invalid constant type: [%s]"%type_)
560
561 -def load_by_type(msgtype, package_context=''):
562 """ 563 Load message specification for specified type 564 565 @param package_context: package name to use for the type name or 566 '' to use the local (relative) naming convention. 567 @type package_context: str 568 @return: Message type name and message specification 569 @rtype: (str, L{MsgSpec}) 570 """ 571 pkg, basetype = roslib_electric.names.package_resource_name(msgtype) 572 pkg = pkg or package_context # convert '' -> local package 573 try: 574 m_f = msg_file(pkg, basetype) 575 except roslib_electric.packages.InvalidROSPkgException: 576 raise MsgSpecException("Cannot locate message type [%s], package [%s] does not exist"%(msgtype, pkg)) 577 return load_from_file(m_f, pkg)
578
579 -def load_from_string(text, package_context='', full_name='', short_name=''):
580 """ 581 Load message specification from a string. 582 @param text: .msg text 583 @type text: str 584 @param package_context: package name to use for the type name or 585 '' to use the local (relative) naming convention. 586 @type package_context: str 587 @return: Message specification 588 @rtype: L{MsgSpec} 589 @raise MsgSpecException: if syntax errors or other problems are detected in file 590 """ 591 types = [] 592 names = [] 593 constants = [] 594 for orig_line in text.split('\n'): 595 l = orig_line.split(COMMENTCHAR)[0].strip() #strip comments 596 if not l: 597 continue #ignore empty lines 598 splits = [s for s in [x.strip() for x in l.split(" ")] if s] #split type/name, filter out empties 599 type_ = splits[0] 600 if not is_valid_msg_type(type_): 601 raise MsgSpecException("%s is not a legal message type"%type_) 602 if CONSTCHAR in l: 603 if not is_valid_constant_type(type_): 604 raise MsgSpecException("%s is not a legal constant type"%type_) 605 if type_ == 'string': 606 # strings contain anything to the right of the equals sign, there are no comments allowed 607 idx = orig_line.find(CONSTCHAR) 608 name = orig_line[orig_line.find(' ')+1:idx] 609 val = orig_line[idx+1:] 610 else: 611 splits = [x.strip() for x in ' '.join(splits[1:]).split(CONSTCHAR)] #resplit on '=' 612 if len(splits) != 2: 613 raise MsgSpecException("Invalid declaration: %s"%l) 614 name = splits[0] 615 val = splits[1] 616 try: 617 val_converted = _convert_val(type_, val) 618 except Exception as e: 619 raise MsgSpecException("Invalid declaration: %s"%e) 620 constants.append(Constant(type_, name, val_converted, val.strip())) 621 else: 622 if len(splits) != 2: 623 raise MsgSpecException("Invalid declaration: %s"%l) 624 name = splits[1] 625 if not is_valid_msg_field_name(name): 626 raise MsgSpecException("%s is not a legal message field name"%name) 627 if package_context and not SEP in type_: 628 if not base_msg_type(type_) in RESERVED_TYPES: 629 #print "rewrite", type_, "to", "%s/%s"%(package_context, type_) 630 type_ = "%s/%s"%(package_context, type_) 631 types.append(type_) 632 names.append(name) 633 return MsgSpec(types, names, constants, text, full_name, short_name, package_context)
634
635 -def load_from_file(file_path, package_context=''):
636 """ 637 Convert the .msg representation in the file to a MsgSpec instance. 638 This does *not* register the object. 639 @param file_path: path of file to load from 640 @type file_path: str: 641 @param package_context: package name to prepend to type name or 642 '' to use local (relative) naming convention. 643 @type package_context: str 644 @return: Message type name and message specification 645 @rtype: (str, L{MsgSpec}) 646 @raise MsgSpecException: if syntax errors or other problems are detected in file 647 """ 648 if VERBOSE: 649 if package_context: 650 print("Load spec from", file_path, "into package [%s]"%package_context) 651 else: 652 print("Load spec from", file_path) 653 654 file_name = os.path.basename(file_path) 655 type_ = file_name[:-len(EXT)] 656 base_type_ = type_ 657 # determine the type name 658 if package_context: 659 while package_context.endswith(SEP): 660 package_context = package_context[:-1] #strip message separators 661 type_ = "%s%s%s"%(package_context, SEP, type_) 662 if not roslib_electric.names.is_legal_resource_name(type_): 663 raise MsgSpecException("%s: [%s] is not a legal type name"%(file_path, type_)) 664 665 f = open(file_path, 'r') 666 try: 667 try: 668 text = f.read() 669 return (type_, load_from_string(text, package_context, type_, base_type_)) 670 except MsgSpecException as e: 671 raise MsgSpecException('%s: %s'%(file_name, e)) 672 finally: 673 f.close()
674 675 # data structures and builtins specification ########################### 676 677 # adjustable constants, in case we change our minds 678 HEADER = 'Header' 679 TIME = 'time' 680 DURATION = 'duration' 681
682 -def is_header_type(type_):
683 """ 684 @param type_: message type name 685 @type type_: str 686 @return: True if \a type_ refers to the ROS Header type 687 @rtype: bool 688 """ 689 # for backwards compatibility, include roslib/Header. REP 100 690 return type_ in [HEADER, 'std_msgs/Header', 'roslib/Header']
691 692 # time and duration types are represented as aggregate data structures 693 # for the purposes of serialization from the perspective of 694 # roslib_electric.msgs. genmsg_py will do additional special handling is required 695 # to convert them into rospy.msg.Time/Duration instances. 696 697 ## time as msg spec. time is unsigned 698 TIME_MSG = "uint32 secs\nuint32 nsecs" 699 ## duration as msg spec. duration is just like time except signed 700 DURATION_MSG = "int32 secs\nint32 nsecs" 701 702 ## primitive types are those for which we allow constants, i.e. have primitive representation 703 PRIMITIVE_TYPES = ['int8','uint8','int16','uint16','int32','uint32','int64','uint64','float32','float64', 704 'string', 705 'bool', 706 # deprecated: 707 'char','byte'] 708 BUILTIN_TYPES = PRIMITIVE_TYPES + [TIME, DURATION] 709
710 -def is_builtin(msg_type_name):
711 """ 712 @param msg_type_name: name of message type 713 @type msg_type_name: str 714 @return: True if msg_type_name is a builtin/primitive type 715 @rtype: bool 716 """ 717 return msg_type_name in BUILTIN_TYPES
718 719 ## extended builtins are builtin types that can be represented as MsgSpec instances 720 EXTENDED_BUILTINS = { TIME : load_from_string(TIME_MSG), DURATION: load_from_string(DURATION_MSG) } 721 722 RESERVED_TYPES = BUILTIN_TYPES + [HEADER] 723 724 REGISTERED_TYPES = { } 725 _loaded_packages = [] #keep track of packages so that we only load once (note: bug #59) 726
727 -def is_registered(msg_type_name):
728 """ 729 @param msg_type_name: name of message type 730 @type msg_type_name: str 731 @return: True if msg spec for specified msg type name is 732 registered. NOTE: builtin types are not registered. 733 @rtype: bool 734 """ 735 return msg_type_name in REGISTERED_TYPES
736
737 -def get_registered(msg_type_name, default_package=None):
738 """ 739 @param msg_type_name: name of message type 740 @type msg_type_name: str 741 @return: msg spec for msg type name 742 @rtype: L{MsgSpec} 743 """ 744 if msg_type_name in REGISTERED_TYPES: 745 return REGISTERED_TYPES[msg_type_name] 746 elif default_package: 747 # if msg_type_name has no package specifier, try with default package resolution 748 p, n = roslib_electric.names.package_resource_name(msg_type_name) 749 if not p: 750 return REGISTERED_TYPES[roslib_electric.names.resource_name(default_package, msg_type_name)] 751 raise KeyError(msg_type_name)
752
753 -def register(msg_type_name, msg_spec):
754 """ 755 Load MsgSpec into the type dictionary 756 757 @param msg_type_name: name of message type 758 @type msg_type_name: str 759 @param msg_spec: spec to load 760 @type msg_spec: L{MsgSpec} 761 """ 762 if VERBOSE: 763 print("Register msg %s"%msg_type_name) 764 REGISTERED_TYPES[msg_type_name] = msg_spec
765