def generate_firos_messages(self, package, data, outdir, OUTPUT, search_path): """ :returns: return code, ``int`` """ if not genmsg.is_legal_resource_base_name(package): raise MsgGenerationException("\nERROR: package name '%s' is illegal and cannot be used in message generation.\nPlease see http://ros.org/wiki/Names"%(package)) # package/src/package/msg for messages, packages/src/package/srv for services msg_context = MsgContext.create_default() retcode = 0 try: # you can't just check first... race condition outdir = outdir + OUTPUT os.makedirs(outdir) f = open(os.path.join(outdir, "__init__.py"), 'w') f.close() except OSError as e: if e.errno != 17: # file exists raise for robotName in data: try: robot = data[robotName] for topic_name in robot['topics']: topic = robot['topics'][topic_name] full_type = str(robotName) + '/' + str(topic_name) if type(topic['msg']) is dict: self.firos_generate(msg_context, full_type, topic, outdir, search_path) #actual generation except Exception as e: if not isinstance(e, MsgGenerationException) and not isinstance(e, genmsg.msgs.InvalidMsgSpec): traceback.print_exc() print("\nERROR: Unable to generate %s for package '%s': %s\n"%(self.what, package, e), file=sys.stderr) retcode = 1 #flag error return retcode
def generate_messages(self, package, package_files, outdir, search_path): """ :returns: return code, ``int`` """ if not genmsg.is_legal_resource_base_name(package): raise MsgGenerationException( "\nERROR: package name '%s' is illegal and cannot be used in message generation.\nPlease see http://ros.org/wiki/Names" % (package)) # package/src/package/msg for messages, packages/src/package/srv for services msg_context = MsgContext.create_default() retcode = 0 for f in package_files: try: f = os.path.abspath(f) infile_name = os.path.basename(f) full_type = genmsg.gentools.compute_full_type_name( package, infile_name) outfile = self.generate(msg_context, full_type, f, outdir, search_path) #actual generation except Exception as e: if not isinstance(e, MsgGenerationException) and not isinstance( e, genmsg.msgs.InvalidMsgSpec): traceback.print_exc() print( "\nERROR: Unable to generate %s for package '%s': while processing '%s': %s\n" % (self.what, package, f, e), file=sys.stderr) retcode = 1 #flag error return retcode
def generator_fn(self, msg_context, spec, search_path): try: genmsg.msg_loader.load_depends(msg_context, spec, search_path) except InvalidMsgSpec as e: raise MsgGenerationException("Cannot generate .msg for %s: %s" % (spec.full_name, str(e))) self.add_msg(MsgGeneratorContex(msg_context, spec, search_path))
def get_registered_ex(msg_context, type_): """ wrapper for get_registered that wraps unknown types with a MsgGenerationException :param type_: ROS message type, ``str`` """ try: return msg_context.get_registered(type_) except: raise MsgGenerationException("Unknown type [%s]. Please check that the manifest.xml correctly declares dependencies."%type_)
def gen_serializable_xml(spec, pkg): # TODO(mereweth) spec.constants # TODO(mereweth) - is there ever a difference between spec full and short names? root = etree.Element("serializable", name=spec.full_name, namespace="%s::%s"%(BASE_NAMESPACE, pkg)) members = etree.SubElement(root, "members") if len(spec.parsed_fields()) == 0: # NOTE(mereweth) Empty.msg, possibly others member = etree.SubElement(members, "member", name="unused", type='U8') # TODO(mereweth) - what is field.type vs field.base_type? for field in spec.parsed_fields(): # TODO(mereweth) - decide how to handle arrays besides just capping size type_, size_, header_includes, serial_imports, countName, countType = compute_type(pkg, field) for header in header_includes: include_header = etree.SubElement(root, "include_header") include_header.text = header for serial in serial_imports: import_serial = etree.SubElement(root, "import_serializable_type") import_serial.text = serial if field.is_array: if size_ is None: raise MsgGenerationException("No calc size for array field: %s, msg: %s, pkg: %s"%( field.name, spec.full_name, pkg)) member = etree.SubElement(members, "member", name=field.name, type=type_, size=str(size_)) if countName is not None: member = etree.SubElement(members, "member", name=countName, type=countType) else: member = etree.SubElement(members, "member", name=field.name, type=type_) return root.getroottree()
def compute_pkg_type(package, type_): """ :param package: package that type is being imported into, ``str`` :param type: message type (package resource name), ``str`` :returns: python package and type name, ``(str, str)`` """ splits = type_.split(genmsg.SEP) if len(splits) == 1: return package, splits[0] elif len(splits) == 2: return tuple(splits) else: raise MsgGenerationException("illegal message type: %s"%type_)
def _write_modmk(outdir, generated_xml): if not os.path.exists(outdir): os.makedirs(outdir) elif not os.path.isdir(outdir): raise MsgGenerationException("file preventing the creating of Fprime directory: %s"%dir) p = os.path.join(outdir, 'mod.mk') with open(p, 'w') as f: f.write('SRC = \\\n') if len(generated_xml) != 0: for xml in generated_xml[:-1]: f.write('%s \\\n'%xml) f.write('%s\n'%generated_xml[-1]) return 0
def _generate_dynamic_specs(msg_context, specs, dep_msg): """ :param dep_msg: text of dependent .msg definition, ``str`` :returns: type name, message spec, ``str, MsgSpec`` :raises: MsgGenerationException If dep_msg is improperly formatted """ line1 = dep_msg.find('\n') msg_line = dep_msg[:line1] if not msg_line.startswith("MSG: "): raise MsgGenerationException("invalid input to generate_dynamic: dependent type is missing 'MSG:' type declaration header") dep_type = msg_line[5:].strip() dep_pkg, dep_base_type = genmsg.package_resource_name(dep_type) dep_spec = genmsg.msg_loader.load_msg_from_string(msg_context, dep_msg[line1+1:], dep_type) return dep_type, dep_spec
def complex_serializer_generator(msg_context, package, type_, name, serialize, is_numpy): # noqa: D401 """ Generator for serializing complex type. :param serialize: if True, generate serialization code. Otherwise, deserialization code. ``bool`` :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists, ``bool`` :raises: MsgGenerationException If type is not a valid """ # ordering of these statements is important as we mutate the type # string we are checking throughout. parse_type strips array # brackets, then we check for the 'complex' builtin types (string, # time, duration, Header), then we canonicalize it to an embedded # message type. _, is_array, _ = genmsg.msgs.parse_type(type_) # Array if is_array: for y in array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy): yield y # Embedded Message elif type_ == 'string': for y in string_serializer_generator(package, type_, name, serialize): yield y else: if not is_special(type_): # canonicalize type pkg, base_type = compute_pkg_type(package, type_) type_ = '%s/%s' % (pkg, base_type) if msg_context.is_registered(type_): # descend data structure #################### ctx_var = next_var() yield '%s = %s' % (ctx_var, _serial_context + name) push_context(ctx_var + '.') # unoptimized code # push_context(_serial_context+name+'.') for y in serializer_generator( msg_context, make_python_safe(get_registered_ex(msg_context, type_)), serialize, is_numpy): yield y # recurs on subtype pop_context() else: # Invalid raise MsgGenerationException( 'Unknown type: %s. Package context is %s' % (type_, package))
def serializer_generator(msg_context, spec, serialize, is_numpy): """ Python generator that yields un-indented python code for (de)serializing MsgSpec. The code this yields is meant to be included in a class method and cannot be used standalone. serialize_fn_generator and deserialize_fn_generator wrap method to provide appropriate class field initializations. :param serialize: if True, yield serialization code. Otherwise, yield deserialization code. ``bool`` :param is_numpy: if True, generate serializer code for numpy datatypes instead of Python lists. ``bool`` """ # Break spec into chunks of simple (primitives) vs. complex (arrays, etc...) # Simple types are batch serialized using the python struct module. # Complex types are individually serialized if spec is None: raise MsgGenerationException("spec is none") names, types = spec.names, spec.types if serialize and not len(names): #Empty yield "pass" return _max_chunk = 255 # iterate through types. whenever we encounter a non-simple type, # yield serializer for any simple types we've encountered until # then, then yield the complex type serializer curr = 0 for (i, full_type) in enumerate(types): if not is_simple(full_type): if i != curr: #yield chunk of simples for _start in range(curr, i, _max_chunk): _end = min(_start + _max_chunk, i) for y in simple_serializer_generator( msg_context, spec, _start, _end, serialize): yield y curr = i + 1 for y in complex_serializer_generator(msg_context, spec.package, full_type, names[i], serialize, is_numpy): yield y if curr < len(types): #yield rest of simples for _start in range(curr, len(types), _max_chunk): _end = min(_start + _max_chunk, len(types)) for y in simple_serializer_generator(msg_context, spec, _start, _end, serialize): yield y
def write_module(basedir, generated_modules): """ Create a module file to mark directory for python :param base_dir: path to package, ``str`` :param package: name of package to write module for, ``str`` :param generated_modules: list of generated message modules, i.e. the names of the .py files that were generated for each .msg file. ``[str]`` """ if not os.path.exists(basedir): os.makedirs(basedir) elif not os.path.isdir(basedir): raise MsgGenerationException("file preventing the creating of module directory: %s"%dir) p = os.path.join(basedir, '__init__.py') with open(p, 'w') as f: for mod in generated_modules: f.write('from %s import *\n'%mod) parent_init = os.path.dirname(basedir)
def generate_dynamic(core_type, msg_cat): """ Dymamically generate message classes from msg_cat .msg text gendeps dump. This method modifies sys.path to include a temp file directory. :param core_type str: top-level ROS message type of concatenated .msg text :param msg_cat str: concatenation of full message text (output of gendeps --cat) :raises: MsgGenerationException If dep_msg is improperly formatted """ msg_context = MsgContext.create_default() core_pkg, core_base_type = genmsg.package_resource_name(core_type) # REP 100: pretty gross hack to deal with the fact that we moved # Header. Header is 'special' because it can be used w/o a package # name, so the lookup rules end up failing. We are committed to # never changing std_msgs/Header, so this is generally fine. msg_cat = msg_cat.replace('roslib/Header', 'std_msgs/Header') # separate msg_cat into the core message and dependencies splits = msg_cat.split('\n' + '=' * 80 + '\n') core_msg = splits[0] deps_msgs = splits[1:] # create MsgSpec representations of .msg text specs = { core_type: msg_loader.load_msg_from_string(msg_context, core_msg, core_type) } # - dependencies for dep_msg in deps_msgs: # dependencies require more handling to determine type name dep_type, dep_spec = _generate_dynamic_specs(msg_context, specs, dep_msg) specs[dep_type] = dep_spec # clear the message registration table and register loaded # types. The types have to be registered globally in order for # message generation of dependents to work correctly. msg_context = msg_loader.MsgContext.create_default() search_path = {} # no ability to dynamically load for t, spec in specs.items(): msg_context.register(t, spec) # process actual MsgSpecs: we accumulate them into a single file, # rewriting the generated text as needed buff = StringIO() for t, spec in specs.items(): pkg, s_type = genmsg.package_resource_name(t) # dynamically generate python message code for line in msg_generator(msg_context, spec, search_path): line = _gen_dyn_modify_references(line, t, list(specs.keys())) buff.write(line + '\n') full_text = buff.getvalue() # Create a temporary directory tmp_dir = tempfile.mkdtemp(prefix='genpy_') # Afterwards, we are going to remove the directory so that the .pyc file gets cleaned up if it's still around atexit.register(shutil.rmtree, tmp_dir) # write the entire text to a file and import it (it will get deleted when tmp_dir goes - above) tmp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.py', dir=tmp_dir, delete=False) tmp_file.file.write(full_text) tmp_file.file.close() # import our temporary file as a python module, which requires modifying sys.path sys.path.append(os.path.dirname(tmp_file.name)) # - strip the prefix to turn it into the python module name try: mod = __import__(os.path.basename(tmp_file.name)[:-3]) except Exception: # TODOXXX:REMOVE with open(tmp_file.name) as f: text = f.read() with open('/tmp/foo', 'w') as f2: f2.write(text) raise # finally, retrieve the message classes from the dynamic module messages = {} for t in specs.keys(): pkg, s_type = genmsg.package_resource_name(t) try: messages[t] = getattr(mod, _gen_dyn_name(pkg, s_type)) except AttributeError: raise MsgGenerationException( 'cannot retrieve message class for %s/%s: %s' % (pkg, s_type, _gen_dyn_name(pkg, s_type))) messages[t]._spec = specs[t] return messages
def msg_generator(msg_context, spec, search_path): """ Python code generator for .msg files. Generates a Python from a :class:`genmsg.MsgSpec`. :param spec: parsed .msg :class:`genmsg.MsgSpec` instance :param search_path: dictionary mapping message namespaces to a directory locations """ # #2990: have to compute md5sum before any calls to make_python_safe # generate dependencies dictionary. omit files calculation as we # rely on in-memory MsgSpecs instead so that we can generate code # for older versions of msg files try: genmsg.msg_loader.load_depends(msg_context, spec, search_path) except InvalidMsgSpec as e: raise MsgGenerationException("Cannot generate .msg for %s/%s: %s"%(package, name, str(e))) md5sum = genmsg.compute_md5(msg_context, spec) # remap spec names to be Python-safe spec = make_python_safe(spec) spec_names = spec.names # #1807 : this will be much cleaner when msggenerator library is # rewritten to not use globals clear_patterns() yield '# This Python file uses the following encoding: utf-8' yield '"""autogenerated by genpy from %s.msg. Do not edit."""'%spec.full_name yield 'import sys' yield 'python3 = True if sys.hexversion > 0x03000000 else False' yield 'import genpy\nimport struct\n' import_strs = [] for t in spec.types: import_strs.extend(compute_import(msg_context, spec.package, t)) import_strs = set(import_strs) for i in import_strs: if i: yield i yield '' fulltype = spec.full_name name = spec.short_name #Yield data class first, e.g. Point2D yield 'class %s(genpy.Message):'%spec.short_name yield ' _md5sum = "%s"'%(md5sum) yield ' _type = "%s"'%(fulltype) yield ' _has_header = %s #flag to mark the presence of a Header object'%spec.has_header() full_text = compute_full_text_escaped(msg_context, spec) # escape trailing double-quote, unless already escaped, before wrapping in """ if full_text.endswith('"') and not full_text.endswith(r'\"'): full_text = full_text[:-1] + r'\"' yield ' _full_text = """%s"""'%full_text if spec.constants: yield ' # Pseudo-constants' for c in spec.constants: if c.type == 'string': val = c.val if '"' in val and "'" in val: # crude escaping of \ and " escaped = c.val.replace('\\', '\\\\') escaped = escaped.replace('\"', '\\"') yield ' %s = "%s"'%(c.name, escaped) elif '"' in val: #use raw encoding for prettiness yield " %s = r'%s'"%(c.name, val) elif "'" in val: #use raw encoding for prettiness yield ' %s = r"%s"'%(c.name, val) else: yield " %s = '%s'"%(c.name, val) else: yield ' %s = %s'%(c.name, c.val) yield '' if len(spec_names): yield " __slots__ = ['"+"','".join(spec_names)+"']" yield " _slot_types = ['"+"','".join(spec.types)+"']" else: yield " __slots__ = []" yield " _slot_types = []" yield """ def __init__(self, *args, **kwds): \"\"\" Constructor. Any message fields that are implicitly/explicitly set to None will be assigned a default value. The recommend use is keyword arguments as this is more robust to future message changes. You cannot mix in-order arguments and keyword arguments. The available fields are: %s :param args: complete set of field values, in .msg order :param kwds: use keyword arguments corresponding to message field names to set specific fields. \"\"\" if args or kwds: super(%s, self).__init__(*args, **kwds)"""%(','.join(spec_names), name) if len(spec_names): yield " #message fields cannot be None, assign default values for those that are" for (t, s) in zip(spec.types, spec_names): yield " if self.%s is None:"%s yield " self.%s = %s"%(s, default_value(msg_context, t, spec.package)) if len(spec_names) > 0: yield " else:" for (t, s) in zip(spec.types, spec_names): yield " self.%s = %s"%(s, default_value(msg_context, t, spec.package)) yield """ def _get_types(self): \"\"\" internal API method \"\"\" return self._slot_types def serialize(self, buff): \"\"\" serialize message into buffer :param buff: buffer, ``StringIO`` \"\"\"""" for y in serialize_fn_generator(msg_context, spec): yield " "+ y yield """ def deserialize(self, str): \"\"\" unpack serialized message in str into this message instance :param str: byte array of serialized message, ``str`` \"\"\"""" for y in deserialize_fn_generator(msg_context, spec): yield " " + y yield "" yield """ def serialize_numpy(self, buff, numpy): \"\"\" serialize message with numpy array types into buffer :param buff: buffer, ``StringIO`` :param numpy: numpy python module \"\"\"""" for y in serialize_fn_generator(msg_context, spec, is_numpy=True): yield " "+ y yield """ def deserialize_numpy(self, str, numpy): \"\"\" unpack serialized message in str into this message instance using numpy for array types :param str: byte array of serialized message, ``str`` :param numpy: numpy python module \"\"\"""" for y in deserialize_fn_generator(msg_context, spec, is_numpy=True): yield " " + y yield "" # #1807 : this will be much cleaner when msggenerator library is # rewritten to not use globals yield '_struct_I = genpy.struct_I' yield 'def _get_struct_I():' yield ' global _struct_I' yield ' return _struct_I' patterns = get_patterns() for p in set(patterns): # I patterns are already optimized if p == 'I': continue var_name = '_struct_%s'%(p.replace('<','')) yield '%s = None' % var_name yield 'def _get%s():' % var_name yield ' global %s' % var_name yield ' if %s is None:' % var_name yield ' %s = struct.Struct("<%s")' % (var_name, p) yield ' return %s' % var_name clear_patterns()
def array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy): """ Generator for array types :raises: :exc:`MsgGenerationException` If array spec is invalid """ base_type, is_array, array_len = genmsg.msgs.parse_type(type_) if not is_array: raise MsgGenerationException("Invalid array spec: %s"%type_) var_length = array_len is None # handle fixed-size byte arrays could be slightly more efficient # as we recalculated the length in the generated code. if base_type in ['char', 'uint8']: #treat unsigned int8 arrays as string type for y in string_serializer_generator(package, type_, name, serialize): yield y return var = _serial_context+name # yield length serialization, if necessary if var_length: for y in len_serializer_generator(var, False, serialize): yield y #serialize array length length = None else: length = array_len #optimization for simple arrays if is_simple(base_type): if var_length: pattern = compute_struct_pattern([base_type]) yield "pattern = '<%%s%s'%%length"%pattern if serialize: if is_numpy: yield pack_numpy(var) else: yield pack2('pattern', "*"+var) else: yield "start = end" yield "end += struct.calcsize(pattern)" if is_numpy: dtype = NUMPY_DTYPE[base_type] yield unpack_numpy(var, 'length', dtype, 'str[start:end]') else: yield unpack2(var, 'pattern', 'str[start:end]') else: pattern = "%s%s"%(length, compute_struct_pattern([base_type])) if serialize: if is_numpy: yield pack_numpy(var) else: yield pack(pattern, "*"+var) else: yield "start = end" yield "end += %s"%struct.calcsize('<%s'%pattern) if is_numpy: dtype = NUMPY_DTYPE[base_type] yield unpack_numpy(var, length, dtype, 'str[start:end]') else: yield unpack(var, pattern, 'str[start:end]') if not serialize and base_type == 'bool': # convert uint8 to bool if base_type == 'bool': yield "%s = map(bool, %s)"%(var, var) else: #generic recursive serializer #NOTE: this is functionally equivalent to the is_registered branch of complex_serializer_generator # choose a unique temporary variable for iterating loop_var = 'val%s'%len(_context_stack) # compute the variable context and factory to use if base_type == 'string': push_context('') factory = string_serializer_generator(package, base_type, loop_var, serialize) else: push_context('%s.'%loop_var) factory = serializer_generator(msg_context, make_python_safe(get_registered_ex(msg_context, base_type)), serialize, is_numpy) if serialize: if array_len is not None: yield 'if len(%s) != %s:'%(var, array_len) yield INDENT + "self._check_types(ValueError(\"Expecting %%s items but found %%s when writing '%%s'\" %% (%s, len(%s), '%s')))"%(array_len, var, var) yield 'for %s in %s:'%(loop_var, var) else: yield '%s = []'%var if var_length: yield 'for i in range(0, length):' else: yield 'for i in range(0, %s):'%length if base_type != 'string': yield INDENT + '%s = %s'%(loop_var, compute_constructor(msg_context, package, base_type)) for y in factory: yield INDENT + y if not serialize: yield INDENT + '%s.append(%s)'%(var, loop_var) pop_context()
def array_serializer_generator(msg_context, package, type_, name, serialize, is_numpy): """ Generator for array types @raise MsgGenerationException: if array spec is invalid """ base_type, is_array, array_len = genmsg.msgs.parse_type(type_) if not is_array: raise MsgGenerationException("Invalid array spec: %s" % type_) var_length = array_len is None # handle fixed-size byte arrays could be slightly more efficient # as we recalculated the length in the generated code. if base_type in ['char', 'uint8']: #treat unsigned int8 arrays as string type for y in string_serializer_generator(package, type_, name, serialize): yield y return var = _serial_context + name try: # yield length serialization, if necessary if var_length: for y in len_serializer_generator(var, False, serialize): yield y #serialize array length length = None else: length = array_len #optimization for simple arrays if is_simple(base_type): if var_length: pattern = compute_struct_pattern([base_type]) yield 'pattern = "%s#{length}"' % convert_to_ruby_pattern( pattern) if serialize: if is_numpy: yield pack_numpy(var) else: yield pack2('pattern', "*" + var) else: yield "start = end_point" yield 'end_point += ROS::Struct::calc_size("#{pattern}")' if is_numpy: dtype = _NUMPY_DTYPE[base_type] yield unpack_numpy(var, 'length', dtype, 'str[start..(end_point-1)]') else: yield unpack2(var, 'pattern', 'str[start..(end_point-1)]') else: pattern = "%s%s" % (length, compute_struct_pattern([base_type ])) if serialize: if is_numpy: yield pack_numpy(var) else: yield pack(pattern, "*" + var) else: yield "start = end_point" yield "end_point += ROS::Struct::calc_size('%s')" % convert_to_ruby_pattern( pattern) if is_numpy: dtype = _NUMPY_DTYPE[base_type] yield unpack_numpy(var, length, dtype, 'str[start..(end_point-1)]') else: yield unpack(var, pattern, 'str[start..(end_point-1)]') if not serialize and base_type == 'bool': # convert uint8 to bool if base_type == 'bool': yield "%s = map(bool, %s)" % (var, var) else: #generic recursive serializer #NOTE: this is functionally equivalent to the is_registered branch of complex_serializer_generator # choose a unique temporary variable for iterating loop_var = 'val%s' % len(_context_stack) # compute the variable context and factory to use if base_type == 'string': push_context('') factory = string_serializer_generator(package, base_type, loop_var, serialize) else: push_context('%s.' % loop_var) factory = serializer_generator( msg_context, get_registered_ex(msg_context, base_type), serialize, is_numpy) if serialize: yield 'for %s in %s' % (loop_var, var) else: yield '%s = []' % var if var_length: yield 'length.times do' else: yield '%s.times do' % length if base_type != 'string': yield INDENT + '%s = %s' % ( loop_var, compute_constructor(msg_context, package, base_type)) for y in factory: yield INDENT + y if not serialize: yield INDENT + '%s.push(%s)' % (var, loop_var) pop_context() yield 'end' except MsgGenerationException: raise #re-raise except Exception as e: raise MsgGenerationException(e) #wrap
def msg_generator_internal(msg_context, spec, search_path): """ Python code generator for .msg files. Takes in a package name, message name, and message specification and generates a Python message class. @param package: name of package for message @type package: str @param name: base type name of message, e.g. 'Empty', 'String' @type name: str @param spec: parsed .msg specification @type spec: L{MsgSpec} """ # #2990: have to compute md5sum before any calls to make_python_safe # generate dependencies dictionary. omit files calculation as we # rely on in-memory MsgSpecs instead so that we can generate code # for older versions of msg files try: genmsg.msg_loader.load_depends(msg_context, spec, search_path) except InvalidMsgSpec as e: raise MsgGenerationException("Cannot generate .msg for %s/%s: %s" % (package, name, str(e))) md5sum = genmsg.compute_md5(msg_context, spec) # remap spec names to be Python-safe spec = make_ruby_safe(spec) spec_names = spec.names # for not capital class like tfMessages name = spec.short_name capitalized_name = name[0].upper() + name[1:] # #1807 : this will be much cleaner when msggenerator library is # rewritten to not use globals clear_patterns() yield '# autogenerated by genrb from %s.msg. Do not edit.' % name yield "require 'ros/message'\n" import_strs = [] for t in spec.types: import_strs.extend(compute_import(msg_context, spec.package, t)) import_strs = set(import_strs) for i in import_strs: if i: yield i yield '' yield "module %s\n" % spec.package.capitalize() fulltype = '%s%s%s' % (spec.package, genmsg.SEP, name) #Yield data class first, e.g. Point2D yield 'class %s <::ROS::Message' % capitalized_name yield """ def self.md5sum \"%s\" end """ % (md5sum) yield """ def self.type \"%s\" end """ % (fulltype) if spec.has_header(): bool_val = 'true' else: bool_val = 'false' yield """ def has_header? %s end """ % bool_val # note: we introduce an extra newline to protect the escaping from quotes in the message yield """ def message_definition \"%s\n\" end""" % compute_full_text_escaped(msg_context, spec) if spec.constants: yield ' # Pseudo-constants' for c in spec.constants: if c.type == 'string': val = c.val if '"' in val and "'" in val: # crude escaping of \ and " escaped = c.val.replace('\\', '\\\\') escaped = escaped.replace('\"', '\\"') yield ' %s = "%s"' % (c.name, escaped) elif '"' in val: #use raw encoding for prettiness yield " %s = r'%s'" % (c.name, val) elif "'" in val: #use raw encoding for prettiness yield ' %s = r"%s"' % (c.name, val) else: yield " %s = '%s'" % (c.name, val) else: yield ' %s = %s' % (c.name, c.val) yield '' yield " attr_accessor " + ", ".join([":" + x for x in spec_names]) + "\n" yield '_REPLACE_FOR_STRUCT_' if len(spec_names): yield " @@struct_L = ::ROS::Struct.new(\"L\")" yield " @@slot_types = ['" + "','".join(spec.types) + "']" else: yield " @@struct_L = ::ROS::Struct.new(\"L\")" yield " @@slot_types = []" yield """ # Constructor. You can set the default values using keyword operators. # # @param [Hash] args keyword for initializing values""" for (t, s) in zip(spec.types, spec_names): yield " # @option args [%s] :%s initialize value" % (t, s) yield " def initialize(args={})" if len(spec_names): yield " # message fields cannot be None, assign default values for those that are" if len(spec_names) > 0: for (t, s) in zip(spec.types, spec_names): yield """ if args[:%s] @%s = args[:%s] else""" % (s, s, s) yield " @%s = %s" % ( s, default_value(msg_context, t, spec.package)) yield " end" yield " end" # end of initialize yield """ # internal API method # @return [String] Message type string. def _get_types @slot_types end # serialize message into buffer # @param [IO] buff buffer def serialize(buff)""" for y in serialize_fn_generator(msg_context, spec): yield " " + y yield " end" yield """ # unpack serialized message in str into this message instance # @param [String] str: byte array of serialized message def deserialize(str) """ for y in deserialize_fn_generator(msg_context, spec): yield " " + y yield " end" yield "end # end of class" yield "end # end of module"