#!/usr/bin/env python from emanesh import manifest import os.path import re import textwrap class EmaneManifest2Model(object): class EmaneModel(object): class EmaneModelParameter(object): intfloat_regex = re.compile(r'^([0-9]+)\.(0*)$') indent = ' ' * 16 def __init__(self, name, apitype, default, caption, possible_values = ()): self.name = name self.apitype = apitype self.default = self.intfloat_regex.sub(r'\1.0', default) self.possible_values = possible_values self.caption = caption def __str__(self): return '''%s('%s', %s,\n%s '%s', '%s', '%s')''' % \ (self.indent, self.name, self.apitype, self.indent, self.default, ','.join(self.possible_values), self.caption) def __init__(self, name): self.name = name self.parameters = [] def add_parameter(self, name, apitype, default, caption, possible_values = ()): p = self.EmaneModelParameter(name, apitype, default, caption, possible_values) self.parameters.append(p) mac_xml_path = '/usr/share/emane/xml/models/mac' # map emane parameter types to CORE api data types core_api_type = { 'uint8': 'coreapi.CONF_DATA_TYPE_UINT8', 'uint16': 'coreapi.CONF_DATA_TYPE_UINT16', 'uint32': 'coreapi.CONF_DATA_TYPE_UINT32', 'uint64': 'coreapi.CONF_DATA_TYPE_UINT64', 'int8': 'coreapi.CONF_DATA_TYPE_INT8', 'int16': 'coreapi.CONF_DATA_TYPE_INT16', 'int32': 'coreapi.CONF_DATA_TYPE_INT32', 'int64': 'coreapi.CONF_DATA_TYPE_INT64', 'float': 'coreapi.CONF_DATA_TYPE_FLOAT', 'double': 'coreapi.CONF_DATA_TYPE_FLOAT', 'bool': 'coreapi.CONF_DATA_TYPE_BOOL', 'string': 'coreapi.CONF_DATA_TYPE_STRING', } parameter_regex = re.compile(r'^\^\(([\|\-\w]+)\)\$$') @classmethod def emane_model(cls, xmlfile): m = manifest.Manifest(xmlfile) model = cls.EmaneModel(m.getName()) for name in m.getAllConfiguration(): info = m.getConfigurationInfo(name) apitype = None for t in 'numeric', 'nonnumeric': if t in info: apitype = cls.core_api_type[info[t]['type']] break default = '' if info['default']: values = info['values'] if values: default = values[0] caption = name possible_values = [] if apitype == 'coreapi.CONF_DATA_TYPE_BOOL': possible_values = ['On,Off'] elif apitype == 'coreapi.CONF_DATA_TYPE_STRING': if name == 'pcrcurveuri': default = os.path.join(cls.mac_xml_path, model.name, model.name + 'pcr.xml') else: regex = info['regex'] if regex: match = cls.parameter_regex.match(regex) if match: possible_values = match.group(1).split('|') model.add_parameter(name, apitype, default, caption, possible_values) model.parameters.sort(key = lambda x: x.name) return model @classmethod def core_emane_model(cls, class_name, macmanifest_filename, phymanifest_filename): template = '''\ from core.emane.emane import EmaneModel from core.api import coreapi class BaseEmaneModel(EmaneModel): def __init__(self, session, objid = None, verbose = False): EmaneModel.__init__(self, session, objid, verbose) def buildnemxmlfiles(self, e, ifc): \'\'\'\\ Build the necessary nem, mac, and phy XMLs in the given path. If an individual NEM has a nonstandard config, we need to build that file also. Otherwise the WLAN-wide nXXemane_*nem.xml, nXXemane_*mac.xml, nXXemane_*phy.xml are used. \'\'\' values = e.getifcconfig(self.objid, self._name, self.getdefaultvalues(), ifc) if values is None: return nemdoc = e.xmldoc('nem') nem = nemdoc.getElementsByTagName('nem').pop() e.appendtransporttonem(nemdoc, nem, self.objid, ifc) def append_definition(tag, name, xmlname, doc): el = doc.createElement(name) el.setAttribute('definition', xmlname) tag.appendChild(el) append_definition(nem, 'mac', self.macxmlname(ifc), nemdoc) append_definition(nem, 'phy', self.phyxmlname(ifc), nemdoc) e.xmlwrite(nemdoc, self.nemxmlname(ifc)) names = list(self.getnames()) def append_options(tag, optnames, doc): for name in optnames: value = self.valueof(name, values).strip() if value: tag.appendChild(e.xmlparam(doc, name, value)) macdoc = e.xmldoc('mac') mac = macdoc.getElementsByTagName('mac').pop() mac.setAttribute('library', '%(modelLibrary)s') # append MAC options to macdoc append_options(mac, names[:len(self._confmatrix_mac)], macdoc) e.xmlwrite(macdoc, self.macxmlname(ifc)) phydoc = e.xmldoc('phy') phy = phydoc.getElementsByTagName('phy').pop() # append PHY options to phydoc append_options(phy, names[len(self._confmatrix_mac):], phydoc) e.xmlwrite(phydoc, self.phyxmlname(ifc)) class %(modelClass)s(BaseEmaneModel): # model name _name = 'emane_%(modelName)s' # configuration parameters are # ( 'name', 'type', 'default', 'possible-value-list', 'caption') # MAC parameters _confmatrix_mac = [\n%(confMatrixMac)s ] # PHY parameters _confmatrix_phy = [\n%(confMatrixPhy)s ] _confmatrix = _confmatrix_mac + _confmatrix_phy # value groupings _confgroups = 'MAC Parameters:1-%%s|PHY Parameters:%%s-%%s' %% \\ (len(_confmatrix_mac), \\ len(_confmatrix_mac) + 1, len(_confmatrix)) ''' macmodel = cls.emane_model(macmanifest_filename) phymodel = cls.emane_model(phymanifest_filename) d = { 'modelClass': 'Emane%sModel' % (class_name), 'modelName': macmodel.name, 'confMatrixMac': ',\n'.join(map(str, macmodel.parameters)) + ',', 'confMatrixPhy': ',\n'.join(map(str, phymodel.parameters)) + ',', 'modelLibrary': macmodel.name, } return textwrap.dedent(template % d) def main(): import argparse import sys parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description = 'Create skeleton CORE bindings from ' \ 'EMANE model manifest files.', epilog = 'example:\n' \ ' %(prog)s -c RadioX \\\n' \ ' -m /usr/share/emane/manifest/radiox.xml \\\n' \ ' -p /usr/share/emane/manifest/emanephy.xml') parser.add_argument('-c', '--class-name', dest = 'classname', required = True, help = 'corresponding python ' 'class name: RadioX -> EmaneRadioXModel') parser.add_argument('-m', '--mac-xmlfile', dest = 'macxmlfilename', required = True, help = 'MAC model manifest XML filename') parser.add_argument('-p', '--phy-xmlfile', dest = 'phyxmlfilename', required = True, help = 'PHY model manifest XML filename') args = parser.parse_args() model = EmaneManifest2Model.core_emane_model(args.classname, args.macxmlfilename, args.phyxmlfilename) sys.stdout.write(model) if __name__ == "__main__": main()