initial import (Boeing r1752, NRL r878)
This commit is contained in:
commit
f8f46d28be
394 changed files with 99738 additions and 0 deletions
0
daemon/core/emane/__init__.py
Normal file
0
daemon/core/emane/__init__.py
Normal file
65
daemon/core/emane/bypass.py
Normal file
65
daemon/core/emane/bypass.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2011-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
bypass.py: EMANE Bypass model for CORE
|
||||
'''
|
||||
|
||||
import sys
|
||||
import string
|
||||
from core.api import coreapi
|
||||
|
||||
from core.constants import *
|
||||
from emane import EmaneModel
|
||||
|
||||
class EmaneBypassModel(EmaneModel):
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
_name = "emane_bypass"
|
||||
_confmatrix = [
|
||||
("none",coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'True,False','There are no parameters for the bypass model.'),
|
||||
]
|
||||
|
||||
# value groupings
|
||||
_confgroups = "Bypass Parameters:1-1"
|
||||
|
||||
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_bypassnem.xml,
|
||||
nXXemane_bypassmac.xml, nXXemane_bypassphy.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()
|
||||
nem.setAttribute("name", "BYPASS NEM")
|
||||
mactag = nemdoc.createElement("mac")
|
||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||
nem.appendChild(mactag)
|
||||
phytag = nemdoc.createElement("phy")
|
||||
phytag.setAttribute("definition", self.phyxmlname(ifc))
|
||||
nem.appendChild(phytag)
|
||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||
|
||||
macdoc = e.xmldoc("mac")
|
||||
mac = macdoc.getElementsByTagName("mac").pop()
|
||||
mac.setAttribute("name", "BYPASS MAC")
|
||||
mac.setAttribute("library", "bypassmaclayer")
|
||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||
|
||||
phydoc = e.xmldoc("phy")
|
||||
phy = phydoc.getElementsByTagName("phy").pop()
|
||||
phy.setAttribute("name", "BYPASS PHY")
|
||||
phy.setAttribute("library", "bypassphylayer")
|
||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||
|
||||
|
124
daemon/core/emane/commeffect.py
Executable file
124
daemon/core/emane/commeffect.py
Executable file
|
@ -0,0 +1,124 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# authors: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
# Randy Charland <rcharland@ll.mit.edu>
|
||||
#
|
||||
'''
|
||||
commeffect.py: EMANE CommEffect model for CORE
|
||||
'''
|
||||
|
||||
import sys
|
||||
import string
|
||||
from core.api import coreapi
|
||||
|
||||
from core.constants import *
|
||||
from emane import EmaneModel
|
||||
|
||||
try:
|
||||
import emaneeventservice
|
||||
import emaneeventcommeffect
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
def z(x):
|
||||
''' Helper to use 0 for None values. '''
|
||||
if x is None:
|
||||
return 0
|
||||
else:
|
||||
return x
|
||||
|
||||
class EmaneCommEffectModel(EmaneModel):
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
# model name
|
||||
_name = "emane_commeffect"
|
||||
# CommEffect parameters
|
||||
_confmatrix_shim = [
|
||||
("defaultconnectivity", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'defaultconnectivity'),
|
||||
("filterfile", coreapi.CONF_DATA_TYPE_STRING, '',
|
||||
'', 'filter file'),
|
||||
("groupid", coreapi.CONF_DATA_TYPE_UINT32, '0',
|
||||
'', 'NEM Group ID'),
|
||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable promiscuous mode'),
|
||||
("enabletighttimingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable tight timing mode'),
|
||||
("receivebufferperiod", coreapi.CONF_DATA_TYPE_FLOAT, '1.0',
|
||||
'', 'receivebufferperiod'),
|
||||
]
|
||||
|
||||
_confmatrix = _confmatrix_shim
|
||||
# value groupings
|
||||
_confgroups = "CommEffect SHIM Parameters:1-%d" \
|
||||
% len(_confmatrix_shim)
|
||||
|
||||
def buildnemxmlfiles(self, e, ifc):
|
||||
''' Build the necessary nem and commeffect 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_commeffectnem.xml, nXXemane_commeffectshim.xml are used.
|
||||
'''
|
||||
values = e.getifcconfig(self.objid, self._name,
|
||||
self.getdefaultvalues(), ifc)
|
||||
if values is None:
|
||||
return
|
||||
shimdoc = e.xmldoc("shim")
|
||||
shim = shimdoc.getElementsByTagName("shim").pop()
|
||||
shim.setAttribute("name", "commeffect SHIM")
|
||||
shim.setAttribute("library", "commeffectshim")
|
||||
|
||||
names = self.getnames()
|
||||
shimnames = list(names[:len(self._confmatrix_shim)])
|
||||
shimnames.remove("filterfile")
|
||||
|
||||
# append all shim options (except filterfile) to shimdoc
|
||||
map( lambda n: shim.appendChild(e.xmlparam(shimdoc, n, \
|
||||
self.valueof(n, values))), shimnames)
|
||||
# empty filterfile is not allowed
|
||||
ff = self.valueof("filterfile", values)
|
||||
if ff.strip() != '':
|
||||
shim.appendChild(e.xmlparam(shimdoc, "filterfile", ff))
|
||||
e.xmlwrite(shimdoc, self.shimxmlname(ifc))
|
||||
|
||||
nemdoc = e.xmldoc("nem")
|
||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||
nem.setAttribute("name", "commeffect NEM")
|
||||
nem.setAttribute("type", "unstructured")
|
||||
nem.appendChild(e.xmlshimdefinition(nemdoc, self.shimxmlname(ifc)))
|
||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||
|
||||
def linkconfig(self, netif, bw = None, delay = None,
|
||||
loss = None, duplicate = None, jitter = None, netif2 = None):
|
||||
''' Generate CommEffect events when a Link Message is received having
|
||||
link parameters.
|
||||
'''
|
||||
service = self.session.emane.service
|
||||
if service is None:
|
||||
self.session.warn("%s: EMANE event service unavailable" % \
|
||||
self._name)
|
||||
return
|
||||
if netif is None or netif2 is None:
|
||||
self.session.warn("%s: missing NEM information" % self._name)
|
||||
return
|
||||
# TODO: batch these into multiple events per transmission
|
||||
event = emaneeventcommeffect.EventCommEffect(1)
|
||||
index = 0
|
||||
e = self.session.obj(self.objid)
|
||||
nemid = e.getnemid(netif)
|
||||
nemid2 = e.getnemid(netif2)
|
||||
mbw = bw
|
||||
|
||||
event.set(index, nemid, 0, z(delay), 0, z(jitter), z(loss),
|
||||
z(duplicate), long(z(bw)), long(z(mbw)))
|
||||
service.publish(emaneeventcommeffect.EVENT_ID,
|
||||
emaneeventservice.PLATFORMID_ANY,
|
||||
nemid2, emaneeventservice.COMPONENTID_ANY,
|
||||
event.export())
|
||||
|
||||
|
||||
|
844
daemon/core/emane/emane.py
Normal file
844
daemon/core/emane/emane.py
Normal file
|
@ -0,0 +1,844 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
emane.py: definition of an Emane class for implementing configuration
|
||||
control of an EMANE emulation.
|
||||
'''
|
||||
|
||||
import sys, os, threading, subprocess, time, string
|
||||
from xml.dom.minidom import parseString, Document
|
||||
from core.constants import *
|
||||
from core.api import coreapi
|
||||
from core.misc.ipaddr import MacAddr
|
||||
from core.conf import ConfigurableManager, Configurable
|
||||
from core.mobility import WirelessModel
|
||||
from core.emane.nodes import EmaneNode
|
||||
try:
|
||||
import emaneeventservice
|
||||
import emaneeventlocation
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
class Emane(ConfigurableManager):
|
||||
''' EMANE controller object. Lives in a Session instance and is used for
|
||||
building EMANE config files from all of the EmaneNode objects in this
|
||||
emulation, and for controlling the EMANE daemons.
|
||||
'''
|
||||
_name = "emane"
|
||||
_type = coreapi.CORE_TLV_REG_EMULSRV
|
||||
_hwaddr_prefix = "02:02"
|
||||
|
||||
def __init__(self, session):
|
||||
ConfigurableManager.__init__(self, session)
|
||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
||||
self._objs = {}
|
||||
self._objslock = threading.Lock()
|
||||
self._ifccounts = {}
|
||||
self._ifccountslock = threading.Lock()
|
||||
self._modelclsmap = {}
|
||||
# Port numbers are allocated from these counters
|
||||
self.platformport = self.session.getcfgitemint('emane_platform_port',
|
||||
8100)
|
||||
self.transformport = self.session.getcfgitemint('emane_transform_port',
|
||||
8200)
|
||||
# model for global EMANE configuration options
|
||||
self.emane_config = EmaneGlobalModel(session, None, self.verbose)
|
||||
session.broker.handlers += (self.handledistributed, )
|
||||
self.loadmodels()
|
||||
# this allows the event service Python bindings to be absent
|
||||
try:
|
||||
self.service = emaneeventservice.EventService()
|
||||
except:
|
||||
self.service = None
|
||||
self.doeventloop = False
|
||||
self.eventmonthread = None
|
||||
# EMANE 0.7.4 support -- to be removed when 0.7.4 support is deprecated
|
||||
self.emane074 = False
|
||||
try:
|
||||
tmp = emaneeventlocation.EventLocation(1)
|
||||
# check if yaw parameter is supported by Location Events
|
||||
# if so, we have EMANE 0.8.1+; if not, we have EMANE 0.7.4/earlier
|
||||
tmp.set(0, 1, 2, 2, 2, 3)
|
||||
except TypeError:
|
||||
self.emane074 = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def loadmodels(self):
|
||||
''' dynamically load EMANE models that were specified in the config file
|
||||
'''
|
||||
self._modelclsmap.clear()
|
||||
self._modelclsmap[self.emane_config._name] = self.emane_config
|
||||
emane_models = self.session.getcfgitem('emane_models')
|
||||
if emane_models is None:
|
||||
return
|
||||
emane_models = emane_models.split(',')
|
||||
for model in emane_models:
|
||||
model = model.strip()
|
||||
try:
|
||||
modelfile = "%s" % model.lower()
|
||||
clsname = "Emane%sModel" % model
|
||||
importcmd = "from %s import %s" % (modelfile, clsname)
|
||||
exec(importcmd)
|
||||
except Exception, e:
|
||||
warntxt = "unable to load the EMANE model '%s'" % modelfile
|
||||
warntxt += " specified in the config file (%s)" % e
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_WARNING, "emane",
|
||||
None, warntxt)
|
||||
self.warn(warntxt)
|
||||
continue
|
||||
# record the model name to class name mapping
|
||||
# this should match clsname._name
|
||||
confname = "emane_%s" % model.lower()
|
||||
self._modelclsmap[confname] = eval(clsname)
|
||||
# each EmaneModel must have ModelName.configure() defined
|
||||
confmethod = eval("%s.configure_emane" % clsname)
|
||||
self.session.addconfobj(confname, coreapi.CORE_TLV_REG_WIRELESS,
|
||||
confmethod)
|
||||
|
||||
def addobj(self, obj):
|
||||
''' add a new EmaneNode object to this Emane controller object
|
||||
'''
|
||||
self._objslock.acquire()
|
||||
if obj.objid in self._objs:
|
||||
self._objslock.release()
|
||||
raise KeyError, "non-unique EMANE object id %s for %s" % \
|
||||
(obj.objid, obj)
|
||||
self._objs[obj.objid] = obj
|
||||
self._objslock.release()
|
||||
|
||||
def getmodels(self, n):
|
||||
''' Used with XML export; see ConfigurableManager.getmodels()
|
||||
'''
|
||||
r = ConfigurableManager.getmodels(self, n)
|
||||
# EMANE global params are stored with first EMANE node (if non-default
|
||||
# values are configured)
|
||||
sorted_ids = sorted(self.configs.keys())
|
||||
if None in self.configs and len(sorted_ids) > 1 and \
|
||||
n.objid == sorted_ids[1]:
|
||||
v = self.configs[None]
|
||||
for model in v:
|
||||
cls = self._modelclsmap[model[0]]
|
||||
vals = model[1]
|
||||
r.append((cls, vals))
|
||||
return r
|
||||
|
||||
def getifcconfig(self, nodenum, conftype, defaultvalues, ifc):
|
||||
# use the network-wide config values or interface(NEM)-specific values?
|
||||
if ifc is None:
|
||||
return self.getconfig(nodenum, conftype, defaultvalues)[1]
|
||||
else:
|
||||
# don't use default values when interface config is the same as net
|
||||
# note here that using ifc.node.objid as key allows for only one type
|
||||
# of each model per node; TODO: use both node and interface as key
|
||||
return self.getconfig(ifc.node.objid, conftype, None)[1]
|
||||
|
||||
def setup(self):
|
||||
''' Populate self._objs with EmaneNodes; perform distributed setup;
|
||||
associate models with EmaneNodes from self.config.
|
||||
'''
|
||||
with self.session._objslock:
|
||||
for obj in self.session.objs():
|
||||
if isinstance(obj, EmaneNode):
|
||||
self.addobj(obj)
|
||||
if len(self._objs) == 0:
|
||||
return False
|
||||
if self.checkdistributed():
|
||||
# we are slave, but haven't received a platformid yet
|
||||
cfgval = self.getconfig(None, self.emane_config._name,
|
||||
self.emane_config.getdefaultvalues())[1]
|
||||
i = self.emane_config.getnames().index('platform_id_start')
|
||||
if cfgval[i] == self.emane_config.getdefaultvalues()[i]:
|
||||
return False
|
||||
self.setnodemodels()
|
||||
return True
|
||||
|
||||
def startup(self):
|
||||
''' after all the EmaneNode objects have been added, build XML files
|
||||
and start the daemons
|
||||
'''
|
||||
self.reset()
|
||||
if not self.setup():
|
||||
return
|
||||
with self._objslock:
|
||||
self.buildxml()
|
||||
self.starteventmonitor()
|
||||
if self.numnems() > 0:
|
||||
self.startdaemons()
|
||||
self.installnetifs()
|
||||
|
||||
def poststartup(self):
|
||||
''' Retransmit location events now that all NEMs are active.
|
||||
'''
|
||||
if self.doeventmonitor():
|
||||
return
|
||||
with self._objslock:
|
||||
for n in sorted(self._objs.keys()):
|
||||
e = self._objs[n]
|
||||
for netif in e.netifs():
|
||||
(x, y, z) = netif.node.position.get()
|
||||
e.setnemposition(netif, x, y, z)
|
||||
|
||||
def reset(self):
|
||||
''' remove all EmaneNode objects from the dictionary,
|
||||
reset port numbers and nem id counters
|
||||
'''
|
||||
with self._objslock:
|
||||
self._objs.clear()
|
||||
# don't clear self._ifccounts here; NEM counts are needed for buildxml
|
||||
self.platformport = self.session.getcfgitemint('emane_platform_port',
|
||||
8100)
|
||||
self.transformport = self.session.getcfgitemint('emane_transform_port',
|
||||
8200)
|
||||
|
||||
def shutdown(self):
|
||||
''' stop all EMANE daemons
|
||||
'''
|
||||
self._ifccountslock.acquire()
|
||||
self._ifccounts.clear()
|
||||
self._ifccountslock.release()
|
||||
self._objslock.acquire()
|
||||
if len(self._objs) == 0:
|
||||
self._objslock.release()
|
||||
return
|
||||
self.info("Stopping EMANE daemons.")
|
||||
self.deinstallnetifs()
|
||||
self.stopdaemons()
|
||||
self.stopeventmonitor()
|
||||
self._objslock.release()
|
||||
|
||||
def handledistributed(self, msg):
|
||||
''' Broker handler for processing CORE API messages as they are
|
||||
received. This is used to snoop the Link add messages to get NEM
|
||||
counts of NEMs that exist on other servers.
|
||||
'''
|
||||
if msg.msgtype == coreapi.CORE_API_LINK_MSG and \
|
||||
msg.flags & coreapi.CORE_API_ADD_FLAG:
|
||||
nn = msg.nodenumbers()
|
||||
# first node is always link layer node in Link add message
|
||||
if nn[0] in self.session.broker.nets:
|
||||
serverlist = self.session.broker.getserversbynode(nn[1])
|
||||
for server in serverlist:
|
||||
self._ifccountslock.acquire()
|
||||
if server not in self._ifccounts:
|
||||
self._ifccounts[server] = 1
|
||||
else:
|
||||
self._ifccounts[server] += 1
|
||||
self._ifccountslock.release()
|
||||
|
||||
def checkdistributed(self):
|
||||
''' Check for EMANE nodes that exist on multiple emulation servers and
|
||||
coordinate the NEM id and port number space.
|
||||
If we are the master EMANE node, return False so initialization will
|
||||
proceed as normal; otherwise slaves return True here and
|
||||
initialization is deferred.
|
||||
'''
|
||||
# check with the session if we are the "master" Emane object?
|
||||
master = False
|
||||
self._objslock.acquire()
|
||||
if len(self._objs) > 0:
|
||||
master = self.session.master
|
||||
self.info("Setup EMANE with master=%s." % master)
|
||||
self._objslock.release()
|
||||
|
||||
# we are not the master Emane object, wait for nem id and ports
|
||||
if not master:
|
||||
return True
|
||||
|
||||
cfgval = self.getconfig(None, self.emane_config._name,
|
||||
self.emane_config.getdefaultvalues())[1]
|
||||
values = list(cfgval)
|
||||
|
||||
nemcount = 0
|
||||
self._objslock.acquire()
|
||||
for n in self._objs:
|
||||
emanenode = self._objs[n]
|
||||
nemcount += emanenode.numnetif()
|
||||
nemid = int(self.emane_config.valueof("nem_id_start", values))
|
||||
nemid += nemcount
|
||||
platformid = int(self.emane_config.valueof("platform_id_start", values))
|
||||
names = list(self.emane_config.getnames())
|
||||
|
||||
# build an ordered list of servers so platform ID is deterministic
|
||||
servers = []
|
||||
for n in sorted(self._objs):
|
||||
for s in self.session.broker.getserversbynode(n):
|
||||
if s not in servers:
|
||||
servers.append(s)
|
||||
self._objslock.release()
|
||||
|
||||
for server in servers:
|
||||
if server == "localhost":
|
||||
continue
|
||||
(host, port, sock) = self.session.broker.getserver(server)
|
||||
if sock is None:
|
||||
continue
|
||||
platformid += 1
|
||||
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
|
||||
values[names.index("platform_id_start")] = str(platformid)
|
||||
values[names.index("nem_id_start")] = str(nemid)
|
||||
msg = EmaneGlobalModel.toconfmsg(flags=0, nodenum=None,
|
||||
typeflags=typeflags, values=values)
|
||||
sock.send(msg)
|
||||
# increment nemid for next server by number of interfaces
|
||||
self._ifccountslock.acquire()
|
||||
if server in self._ifccounts:
|
||||
nemid += self._ifccounts[server]
|
||||
self._ifccountslock.release()
|
||||
|
||||
return False
|
||||
|
||||
def buildxml(self):
|
||||
''' Build all of the XML files required to run EMANE.
|
||||
'''
|
||||
# assume self._objslock is already held here
|
||||
if self.verbose:
|
||||
self.info("Emane.buildxml()")
|
||||
self.buildplatformxml()
|
||||
self.buildnemxml()
|
||||
self.buildtransportxml()
|
||||
|
||||
def xmldoc(self, doctype):
|
||||
''' Returns an XML xml.minidom.Document with a DOCTYPE tag set to the
|
||||
provided doctype string, and an initial element having the same
|
||||
name.
|
||||
'''
|
||||
# we hack in the DOCTYPE using the parser
|
||||
docstr = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE %s SYSTEM "file:///usr/share/emane/dtd/%s.dtd">
|
||||
<%s/>""" % (doctype, doctype, doctype)
|
||||
# normally this would be: doc = Document()
|
||||
return parseString(docstr)
|
||||
|
||||
def xmlparam(self, doc, name, value):
|
||||
''' Convenience function for building a parameter tag of the format:
|
||||
<param name="name" value="value" />
|
||||
'''
|
||||
p = doc.createElement("param")
|
||||
p.setAttribute("name", name)
|
||||
p.setAttribute("value", value)
|
||||
return p
|
||||
|
||||
def xmlshimdefinition(self, doc, name):
|
||||
''' Convenience function for building a definition tag of the format:
|
||||
<shim definition="name" />
|
||||
'''
|
||||
p = doc.createElement("shim")
|
||||
p.setAttribute("definition", name)
|
||||
return p
|
||||
|
||||
def xmlwrite(self, doc, filename):
|
||||
''' Write the given XML document to the specified filename.
|
||||
'''
|
||||
#self.info("%s" % doc.toprettyxml(indent=" "))
|
||||
pathname = os.path.join(self.session.sessiondir, filename)
|
||||
f = open(pathname, "w")
|
||||
doc.writexml(writer=f, indent="", addindent=" ", newl="\n", \
|
||||
encoding="UTF-8")
|
||||
f.close()
|
||||
|
||||
def setnodemodels(self):
|
||||
''' Associate EmaneModel classes with EmaneNode nodes. The model
|
||||
configurations are stored in self.configs.
|
||||
'''
|
||||
for n in self._objs:
|
||||
self.setnodemodel(n)
|
||||
|
||||
def setnodemodel(self, n):
|
||||
emanenode = self._objs[n]
|
||||
for (t, v) in self.configs[n]:
|
||||
if t is None:
|
||||
continue
|
||||
if t == self.emane_config._name:
|
||||
continue
|
||||
# only use the first valid EmaneModel
|
||||
# convert model name to class (e.g. emane_rfpipe -> EmaneRfPipe)
|
||||
cls = self._modelclsmap[t]
|
||||
emanenode.setmodel(cls, v)
|
||||
return True
|
||||
# no model has been configured for this EmaneNode
|
||||
return False
|
||||
|
||||
def nemlookup(self, nemid):
|
||||
''' Look for the given numerical NEM ID and return the first matching
|
||||
EmaneNode and NEM interface.
|
||||
'''
|
||||
emanenode = None
|
||||
netif = None
|
||||
|
||||
for n in self._objs:
|
||||
emanenode = self._objs[n]
|
||||
netif = emanenode.getnemnetif(nemid)
|
||||
if netif is not None:
|
||||
break
|
||||
else:
|
||||
emanenode = None
|
||||
return (emanenode, netif)
|
||||
|
||||
def numnems(self):
|
||||
''' Return the number of NEMs emulated locally.
|
||||
'''
|
||||
count = 0
|
||||
for o in self._objs.values():
|
||||
count += len(o.netifs())
|
||||
return count
|
||||
|
||||
def buildplatformxml(self):
|
||||
''' Build a platform.xml file now that all nodes are configured.
|
||||
'''
|
||||
values = self.getconfig(None, "emane",
|
||||
self.emane_config.getdefaultvalues())[1]
|
||||
doc = self.xmldoc("platform")
|
||||
plat = doc.getElementsByTagName("platform").pop()
|
||||
platformid = self.emane_config.valueof("platform_id_start", values)
|
||||
plat.setAttribute("name", "Platform %s" % platformid)
|
||||
plat.setAttribute("id", platformid)
|
||||
|
||||
names = list(self.emane_config.getnames())
|
||||
platform_names = names[:len(self.emane_config._confmatrix_platform)]
|
||||
platform_names.remove('platform_id_start')
|
||||
|
||||
# append all platform options (except starting id) to doc
|
||||
map( lambda n: plat.appendChild(self.xmlparam(doc, n, \
|
||||
self.emane_config.valueof(n, values))), platform_names)
|
||||
|
||||
nemid = int(self.emane_config.valueof("nem_id_start", values))
|
||||
# assume self._objslock is already held here
|
||||
for n in sorted(self._objs.keys()):
|
||||
emanenode = self._objs[n]
|
||||
nems = emanenode.buildplatformxmlentry(doc)
|
||||
for netif in sorted(nems, key=lambda n: n.node.objid):
|
||||
# set ID, endpoints here
|
||||
nementry = nems[netif]
|
||||
nementry.setAttribute("id", "%d" % nemid)
|
||||
# insert nem options (except nem id) to doc
|
||||
trans_addr = self.emane_config.valueof("transportendpoint", \
|
||||
values)
|
||||
nementry.insertBefore(self.xmlparam(doc, "transportendpoint", \
|
||||
"%s:%d" % (trans_addr, self.transformport)),
|
||||
nementry.firstChild)
|
||||
platform_addr = self.emane_config.valueof("platformendpoint", \
|
||||
values)
|
||||
nementry.insertBefore(self.xmlparam(doc, "platformendpoint", \
|
||||
"%s:%d" % (platform_addr, self.platformport)),
|
||||
nementry.firstChild)
|
||||
plat.appendChild(nementry)
|
||||
emanenode.setnemid(netif, nemid)
|
||||
# NOTE: MAC address set before here is incorrect, including the one
|
||||
# sent from the GUI via link message
|
||||
# MAC address determined by NEM ID: 02:02:00:00:nn:nn"
|
||||
macstr = self._hwaddr_prefix + ":00:00:"
|
||||
macstr += "%02X:%02X" % ((nemid >> 8) & 0xFF, nemid & 0xFF)
|
||||
netif.sethwaddr(MacAddr.fromstring(macstr))
|
||||
# increment counters used to manage IDs, endpoint port numbers
|
||||
nemid += 1
|
||||
self.platformport += 1
|
||||
self.transformport += 1
|
||||
self.xmlwrite(doc, "platform.xml")
|
||||
|
||||
def buildnemxml(self):
|
||||
''' Builds the xxxnem.xml, xxxmac.xml, and xxxphy.xml files which
|
||||
are defined on a per-EmaneNode basis.
|
||||
'''
|
||||
for n in sorted(self._objs.keys()):
|
||||
emanenode = self._objs[n]
|
||||
nems = emanenode.buildnemxmlfiles(self)
|
||||
|
||||
def buildtransportxml(self):
|
||||
''' Calls emanegentransportxml using a platform.xml file to build
|
||||
the transportdaemon*.xml.
|
||||
'''
|
||||
try:
|
||||
subprocess.check_call(["emanegentransportxml", "platform.xml"], \
|
||||
cwd=self.session.sessiondir)
|
||||
except Exception, e:
|
||||
self.info("error running emanegentransportxml: %s" % e)
|
||||
|
||||
def startdaemons(self):
|
||||
''' Start the appropriate EMANE daemons. The transport daemon will
|
||||
bind to the TAP interfaces.
|
||||
'''
|
||||
if self.verbose:
|
||||
self.info("Emane.startdaemons()")
|
||||
path = self.session.sessiondir
|
||||
loglevel = "2"
|
||||
cfgloglevel = self.session.getcfgitemint("emane_log_level")
|
||||
realtime = self.session.getcfgitembool("emane_realtime", True)
|
||||
if cfgloglevel:
|
||||
self.info("setting user-defined EMANE log level: %d" % cfgloglevel)
|
||||
loglevel = str(cfgloglevel)
|
||||
emanecmd = ["emane", "-d", "--logl", loglevel, "-f", \
|
||||
os.path.join(path, "emane.log")]
|
||||
if realtime:
|
||||
emanecmd += "-r",
|
||||
try:
|
||||
cmd = emanecmd + [os.path.join(path, "platform.xml")]
|
||||
if self.verbose:
|
||||
self.info("Emane.startdaemons() running %s" % str(cmd))
|
||||
subprocess.check_call(cmd, cwd=path)
|
||||
except Exception, e:
|
||||
errmsg = "error starting emane: %s" % e
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_FATAL, "emane",
|
||||
None, errmsg)
|
||||
self.info(errmsg)
|
||||
|
||||
# start one transport daemon per transportdaemon*.xml file
|
||||
transcmd = ["emanetransportd", "-d", "--logl", loglevel, "-f", \
|
||||
os.path.join(path, "emanetransportd.log")]
|
||||
if realtime:
|
||||
transcmd += "-r",
|
||||
files = os.listdir(path)
|
||||
for file in files:
|
||||
if file[-3:] == "xml" and file[:15] == "transportdaemon":
|
||||
cmd = transcmd + [os.path.join(path, file)]
|
||||
try:
|
||||
if self.verbose:
|
||||
self.info("Emane.startdaemons() running %s" % str(cmd))
|
||||
subprocess.check_call(cmd, cwd=path)
|
||||
except Exception, e:
|
||||
errmsg = "error starting emanetransportd: %s" % e
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_FATAL, "emane",
|
||||
None, errmsg)
|
||||
self.info(errmsg)
|
||||
|
||||
def stopdaemons(self):
|
||||
''' Kill the appropriate EMANE daemons.
|
||||
'''
|
||||
# TODO: we may want to improve this if we had the PIDs from the
|
||||
# specific EMANE daemons that we've started
|
||||
subprocess.call(["killall", "-q", "emane"])
|
||||
subprocess.call(["killall", "-q", "emanetransportd"])
|
||||
|
||||
def installnetifs(self):
|
||||
''' Install TUN/TAP virtual interfaces into their proper namespaces
|
||||
now that the EMANE daemons are running.
|
||||
'''
|
||||
for n in sorted(self._objs.keys()):
|
||||
emanenode = self._objs[n]
|
||||
if self.verbose:
|
||||
self.info("Emane.installnetifs() for node %d" % n)
|
||||
emanenode.installnetifs()
|
||||
|
||||
def deinstallnetifs(self):
|
||||
''' Uninstall TUN/TAP virtual interfaces.
|
||||
'''
|
||||
for n in sorted(self._objs.keys()):
|
||||
emanenode = self._objs[n]
|
||||
emanenode.deinstallnetifs()
|
||||
|
||||
def configure(self, session, msg):
|
||||
''' Handle configuration messages for global EMANE config.
|
||||
'''
|
||||
r = self.emane_config.configure_emane(session, msg)
|
||||
|
||||
# extra logic to start slave Emane object after nemid has been
|
||||
# configured from the master
|
||||
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
|
||||
if conftype == coreapi.CONF_TYPE_FLAGS_UPDATE and \
|
||||
self.session.master == False:
|
||||
self.startup()
|
||||
|
||||
return r
|
||||
|
||||
def doeventmonitor(self):
|
||||
''' Returns boolean whether or not EMANE events will be monitored.
|
||||
'''
|
||||
# this support must be explicitly turned on; by default, CORE will
|
||||
# generate the EMANE events when nodes are moved
|
||||
return self.session.getcfgitembool('emane_event_monitor', False)
|
||||
|
||||
def starteventmonitor(self):
|
||||
''' Start monitoring EMANE location events if configured to do so.
|
||||
'''
|
||||
if self.verbose:
|
||||
self.info("Emane.starteventmonitor()")
|
||||
if not self.doeventmonitor():
|
||||
return
|
||||
if self.service is None:
|
||||
errmsg = "Warning: EMANE events will not be generated " \
|
||||
"because the emaneeventservice\n binding was " \
|
||||
"unable to load " \
|
||||
"(install the python-emaneeventservice bindings)"
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_WARNING, "emane",
|
||||
None, errmsg)
|
||||
self.warn(errmsg)
|
||||
|
||||
return
|
||||
self.doeventloop = True
|
||||
self.eventmonthread = threading.Thread(target = self.eventmonitorloop)
|
||||
self.eventmonthread.daemon = True
|
||||
self.eventmonthread.start()
|
||||
|
||||
|
||||
def stopeventmonitor(self):
|
||||
''' Stop monitoring EMANE location events.
|
||||
'''
|
||||
self.doeventloop = False
|
||||
if self.service is not None:
|
||||
self.service.breakloop()
|
||||
# reset the service, otherwise nextEvent won't work
|
||||
del self.service
|
||||
self.service = emaneeventservice.EventService()
|
||||
if self.eventmonthread is not None:
|
||||
self.eventmonthread.join()
|
||||
self.eventmonthread = None
|
||||
|
||||
def eventmonitorloop(self):
|
||||
''' Thread target that monitors EMANE location events.
|
||||
'''
|
||||
if self.service is None:
|
||||
return
|
||||
self.info("subscribing to EMANE location events")
|
||||
#self.service.subscribe(emaneeventlocation.EVENT_ID,
|
||||
# self.handlelocationevent)
|
||||
#self.service.loop()
|
||||
#self.service.subscribe(emaneeventlocation.EVENT_ID, None)
|
||||
while self.doeventloop is True:
|
||||
(event, platform, nem, component, data) = self.service.nextEvent()
|
||||
if event == emaneeventlocation.EVENT_ID:
|
||||
self.handlelocationevent(event, platform, nem, component, data)
|
||||
|
||||
self.info("unsubscribing from EMANE location events")
|
||||
#self.service.unsubscribe(emaneeventlocation.EVENT_ID)
|
||||
|
||||
def handlelocationevent(self, event, platform, nem, component, data):
|
||||
''' Handle an EMANE location event.
|
||||
'''
|
||||
event = emaneeventlocation.EventLocation(data)
|
||||
entries = event.entries()
|
||||
for e in entries.values():
|
||||
# yaw,pitch,roll,azimuth,elevation,velocity are unhandled
|
||||
(nemid, lat, long, alt) = e[:4]
|
||||
# convert nemid to node number
|
||||
(emanenode, netif) = self.nemlookup(nemid)
|
||||
if netif is None:
|
||||
if self.verbose:
|
||||
self.info("location event for unknown NEM %s" % nemid)
|
||||
continue
|
||||
n = netif.node.objid
|
||||
# convert from lat/long/alt to x,y,z coordinates
|
||||
(x, y, z) = self.session.location.getxyz(lat, long, alt)
|
||||
x = int(x)
|
||||
y = int(y)
|
||||
z = int(z)
|
||||
if self.verbose:
|
||||
self.info("location event NEM %s (%s, %s, %s) -> (%s, %s, %s)" \
|
||||
% (nemid, lat, long, alt, x, y, z))
|
||||
try:
|
||||
if (x.bit_length() > 16) or (y.bit_length() > 16) or \
|
||||
(z.bit_length() > 16) or (x < 0) or (y < 0) or (z < 0):
|
||||
warntxt = "Unable to build node location message since " \
|
||||
"received lat/long/alt exceeds coordinate " \
|
||||
"space: NEM %s (%d, %d, %d)" % (nemid, x, y, z)
|
||||
self.info(warntxt)
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_ERROR,
|
||||
"emane", None, warntxt)
|
||||
continue
|
||||
except AttributeError:
|
||||
# int.bit_length() not present on Python 2.6
|
||||
pass
|
||||
|
||||
# generate a node message for this location update
|
||||
try:
|
||||
node = self.session.obj(n)
|
||||
except KeyError:
|
||||
self.warn("location event NEM %s has no corresponding node %s" \
|
||||
% (nemid, n))
|
||||
continue
|
||||
# don't use node.setposition(x,y,z) which generates an event
|
||||
node.position.set(x,y,z)
|
||||
msg = node.tonodemsg(flags=0)
|
||||
self.session.broadcastraw(None, msg)
|
||||
self.session.sdt.updatenodegeo(node, lat, long, alt)
|
||||
|
||||
|
||||
class EmaneModel(WirelessModel):
|
||||
''' EMANE models inherit from this parent class, which takes care of
|
||||
handling configuration messages based on the _confmatrix list of
|
||||
configurable parameters. Helper functions also live here.
|
||||
'''
|
||||
_prefix = {'y': 1e-24, # yocto
|
||||
'z': 1e-21, # zepto
|
||||
'a': 1e-18, # atto
|
||||
'f': 1e-15, # femto
|
||||
'p': 1e-12, # pico
|
||||
'n': 1e-9, # nano
|
||||
'u': 1e-6, # micro
|
||||
'm': 1e-3, # mili
|
||||
'c': 1e-2, # centi
|
||||
'd': 1e-1, # deci
|
||||
'k': 1e3, # kilo
|
||||
'M': 1e6, # mega
|
||||
'G': 1e9, # giga
|
||||
'T': 1e12, # tera
|
||||
'P': 1e15, # peta
|
||||
'E': 1e18, # exa
|
||||
'Z': 1e21, # zetta
|
||||
'Y': 1e24, # yotta
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def configure_emane(cls, session, msg):
|
||||
''' Handle configuration messages for setting up a model.
|
||||
Pass the Emane object as the manager object.
|
||||
'''
|
||||
return cls.configure(session.emane, msg)
|
||||
|
||||
@classmethod
|
||||
def emane074_fixup(cls, value, div=1.0):
|
||||
''' Helper for converting 0.8.1 and newer values to EMANE 0.7.4
|
||||
compatible values.
|
||||
NOTE: This should be removed when support for 0.7.4 has been
|
||||
deprecated.
|
||||
'''
|
||||
if div == 0:
|
||||
return "0"
|
||||
if type(value) is not str:
|
||||
return str(value / div)
|
||||
if value.endswith(tuple(cls._prefix.keys())):
|
||||
suffix = value[-1]
|
||||
value = float(value[:-1]) * cls._prefix[suffix]
|
||||
return str(int(value / div))
|
||||
|
||||
def buildnemxmlfiles(self, e, ifc):
|
||||
''' Build the necessary nem, mac, and phy XMLs in the given path.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def buildplatformxmlnementry(self, doc, n, ifc):
|
||||
''' Build the NEM definition that goes into the platform.xml file.
|
||||
This returns an XML element that will be added to the <platform/> element.
|
||||
This default method supports per-interface config
|
||||
(e.g. <nem definition="n2_0_63emane_rfpipe.xml" id="1"> or per-EmaneNode
|
||||
config (e.g. <nem definition="n1emane_rfpipe.xml" id="1">.
|
||||
This can be overriden by a model for NEM flexibility; n is the EmaneNode.
|
||||
'''
|
||||
nem = doc.createElement("nem")
|
||||
nem.setAttribute("name", ifc.localname)
|
||||
# if this netif contains a non-standard (per-interface) config,
|
||||
# then we need to use a more specific xml file here
|
||||
nem.setAttribute("definition", self.nemxmlname(ifc))
|
||||
return nem
|
||||
|
||||
def buildplatformxmltransportentry(self, doc, n, ifc):
|
||||
''' Build the transport definition that goes into the platform.xml file.
|
||||
This returns an XML element that will added to the nem definition.
|
||||
This default method supports raw and virtual transport types, but may be
|
||||
overriden by a model to support the e.g. pluggable virtual transport.
|
||||
n is the EmaneNode.
|
||||
'''
|
||||
type = ifc.transport_type
|
||||
if not type:
|
||||
e.info("warning: %s interface type unsupported!" % ifc.name)
|
||||
type = "raw"
|
||||
trans = doc.createElement("transport")
|
||||
trans.setAttribute("definition", n.transportxmlname(type))
|
||||
trans.setAttribute("group", "1")
|
||||
param = doc.createElement("param")
|
||||
param.setAttribute("name", "device")
|
||||
if type == "raw":
|
||||
# raw RJ45 name e.g. 'eth0'
|
||||
param.setAttribute("value", ifc.name)
|
||||
else:
|
||||
# virtual TAP name e.g. 'n3.0.17'
|
||||
param.setAttribute("value", ifc.localname)
|
||||
trans.appendChild(param)
|
||||
return trans
|
||||
|
||||
def basename(self, ifc = None):
|
||||
''' Return the string that other names are based on.
|
||||
If a specific config is stored for a node's interface, a unique
|
||||
filename is needed; otherwise the name of the EmaneNode is used.
|
||||
'''
|
||||
emane = self.session.emane
|
||||
name = "n%s" % self.objid
|
||||
if ifc is not None:
|
||||
nodenum = ifc.node.objid
|
||||
if emane.getconfig(nodenum, self._name, None)[1] is not None:
|
||||
name = ifc.localname.replace('.','_')
|
||||
return "%s%s" % (name, self._name)
|
||||
|
||||
def nemxmlname(self, ifc = None):
|
||||
''' Return the string name for the NEM XML file, e.g. 'n3rfpipenem.xml'
|
||||
'''
|
||||
return "%snem.xml" % self.basename(ifc)
|
||||
|
||||
def shimxmlname(self, ifc = None):
|
||||
''' Return the string name for the SHIM XML file, e.g. 'commeffectshim.xml'
|
||||
'''
|
||||
return "%sshim.xml" % self.basename(ifc)
|
||||
|
||||
def macxmlname(self, ifc = None):
|
||||
''' Return the string name for the MAC XML file, e.g. 'n3rfpipemac.xml'
|
||||
'''
|
||||
return "%smac.xml" % self.basename(ifc)
|
||||
|
||||
def phyxmlname(self, ifc = None):
|
||||
''' Return the string name for the PHY XML file, e.g. 'n3rfpipephy.xml'
|
||||
'''
|
||||
return "%sphy.xml" % self.basename(ifc)
|
||||
|
||||
def update(self, moved, moved_netifs):
|
||||
''' invoked from MobilityModel when nodes are moved; this causes
|
||||
EMANE location events to be generated for the nodes in the moved
|
||||
list, making EmaneModels compatible with Ns2ScriptedMobility
|
||||
'''
|
||||
try:
|
||||
wlan = self.session.obj(self.objid)
|
||||
except KeyError:
|
||||
return
|
||||
wlan.setnempositions(moved_netifs)
|
||||
|
||||
def linkconfig(self, netif, bw = None, delay = None,
|
||||
loss = None, duplicate = None, jitter = None, netif2 = None):
|
||||
''' Invoked when a Link Message is received. Default is unimplemented.
|
||||
'''
|
||||
warntxt = "EMANE model %s does not support link " % self._name
|
||||
warntxt += "configuration, dropping Link Message"
|
||||
self.session.warn(warntxt)
|
||||
|
||||
|
||||
class EmaneGlobalModel(EmaneModel):
|
||||
''' Global EMANE configuration options.
|
||||
'''
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
_name = "emane"
|
||||
_confmatrix_platform = [
|
||||
("otamanagerchannelenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'on,off', 'enable OTA Manager channel'),
|
||||
("otamanagergroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45702',
|
||||
'', 'OTA Manager group'),
|
||||
("otamanagerdevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
|
||||
'', 'OTA Manager device'),
|
||||
("eventservicegroup", coreapi.CONF_DATA_TYPE_STRING, '224.1.2.8:45703',
|
||||
'', 'Event Service group'),
|
||||
("eventservicedevice", coreapi.CONF_DATA_TYPE_STRING, 'lo',
|
||||
'', 'Event Service device'),
|
||||
("platform_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
||||
'', 'starting Platform ID'),
|
||||
("debugportenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'on,off', 'enable debug port'),
|
||||
("debugport", coreapi.CONF_DATA_TYPE_UINT16, '47000',
|
||||
'', 'debug port number'),
|
||||
]
|
||||
_confmatrix_nem = [
|
||||
("transportendpoint", coreapi.CONF_DATA_TYPE_STRING, 'localhost',
|
||||
'', 'Transport endpoint address (port is automatic)'),
|
||||
("platformendpoint", coreapi.CONF_DATA_TYPE_STRING, 'localhost',
|
||||
'', 'Platform endpoint address (port is automatic)'),
|
||||
("nem_id_start", coreapi.CONF_DATA_TYPE_INT32, '1',
|
||||
'', 'starting NEM ID'),
|
||||
]
|
||||
_confmatrix = _confmatrix_platform + _confmatrix_nem
|
||||
_confgroups = "Platform Attributes:1-%d|NEM Parameters:%d-%d" % \
|
||||
(len(_confmatrix_platform), len(_confmatrix_platform) + 1,
|
||||
len(_confmatrix))
|
||||
|
119
daemon/core/emane/ieee80211abg.py
Normal file
119
daemon/core/emane/ieee80211abg.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
ieee80211abg.py: EMANE IEEE 802.11abg model for CORE
|
||||
'''
|
||||
|
||||
import sys
|
||||
import string
|
||||
from core.api import coreapi
|
||||
|
||||
from core.constants import *
|
||||
from emane import EmaneModel
|
||||
from universal import EmaneUniversalModel
|
||||
|
||||
class EmaneIeee80211abgModel(EmaneModel):
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
# model name
|
||||
_name = "emane_ieee80211abg"
|
||||
_80211rates = '1 1 Mbps,2 2 Mbps,3 5.5 Mbps,4 11 Mbps,5 6 Mbps,' + \
|
||||
'6 9 Mbps,7 12 Mbps,8 18 Mbps,9 24 Mbps,10 36 Mbps,11 48 Mbps,' + \
|
||||
'12 54 Mbps'
|
||||
# MAC parameters
|
||||
_confmatrix_mac = [
|
||||
("mode", coreapi.CONF_DATA_TYPE_UINT8, '0',
|
||||
'0 802.11b (DSSS only),1 802.11b (DSSS only),' + \
|
||||
'2 802.11a or g (OFDM),3 802.11b/g (DSSS and OFDM)', 'mode'),
|
||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable promiscuous mode'),
|
||||
("distance", coreapi.CONF_DATA_TYPE_UINT32, '1000',
|
||||
'', 'max distance (m)'),
|
||||
("unicastrate", coreapi.CONF_DATA_TYPE_UINT8, '4', _80211rates,
|
||||
'unicast rate (Mbps)'),
|
||||
("multicastrate", coreapi.CONF_DATA_TYPE_UINT8, '1', _80211rates,
|
||||
'multicast rate (Mbps)'),
|
||||
("rtsthreshold", coreapi.CONF_DATA_TYPE_UINT16, '0',
|
||||
'', 'RTS threshold (bytes)'),
|
||||
("wmmenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'WiFi Multimedia (WMM)'),
|
||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
||||
'/usr/share/emane/models/ieee80211abg/xml/ieee80211pcr.xml',
|
||||
'', 'SINR/PCR curve file'),
|
||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable traffic flow control'),
|
||||
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
|
||||
'', 'number of flow control tokens'),
|
||||
("queuesize", coreapi.CONF_DATA_TYPE_STRING, '0:255 1:255 2:255 3:255',
|
||||
'', 'queue size (0-4:size)'),
|
||||
("cwmin", coreapi.CONF_DATA_TYPE_STRING, '0:32 1:32 2:16 3:8',
|
||||
'', 'min contention window (0-4:minw)'),
|
||||
("cwmax", coreapi.CONF_DATA_TYPE_STRING, '0:1024 1:1024 2:64 3:16',
|
||||
'', 'max contention window (0-4:maxw)'),
|
||||
("aifs", coreapi.CONF_DATA_TYPE_STRING, '0:2 1:2 2:2 3:1',
|
||||
'', 'arbitration inter frame space (0-4:aifs)'),
|
||||
("txop", coreapi.CONF_DATA_TYPE_STRING, '0:0 1:0 2:0 3:0',
|
||||
'', 'txop (0-4:usec)'),
|
||||
("retrylimit", coreapi.CONF_DATA_TYPE_STRING, '0:3 1:3 2:3 3:3',
|
||||
'', 'retry limit (0-4:numretries)'),
|
||||
]
|
||||
# PHY parameters from Universal PHY
|
||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
||||
|
||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
||||
# value groupings
|
||||
_confgroups = "802.11 MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
|
||||
% (len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
||||
|
||||
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_ieee80211abgnem.xml, nXXemane_ieee80211abgemac.xml,
|
||||
nXXemane_ieee80211abgphy.xml are used.
|
||||
'''
|
||||
# use the network-wide config values or interface(NEM)-specific values?
|
||||
if ifc is None:
|
||||
values = e.getconfig(self.objid, self._name,
|
||||
self.getdefaultvalues())[1]
|
||||
else:
|
||||
nodenum = ifc.node.objid
|
||||
values = e.getconfig(nodenum, self._name, None)[1]
|
||||
if values is None:
|
||||
# do not build specific files for this NEM when config is same
|
||||
# as the network
|
||||
return
|
||||
nemdoc = e.xmldoc("nem")
|
||||
nem = nemdoc.getElementsByTagName("nem").pop()
|
||||
nem.setAttribute("name", "ieee80211abg NEM")
|
||||
mactag = nemdoc.createElement("mac")
|
||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||
nem.appendChild(mactag)
|
||||
phytag = nemdoc.createElement("phy")
|
||||
phytag.setAttribute("definition", self.phyxmlname(ifc))
|
||||
nem.appendChild(phytag)
|
||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||
|
||||
macdoc = e.xmldoc("mac")
|
||||
mac = macdoc.getElementsByTagName("mac").pop()
|
||||
mac.setAttribute("name", "ieee80211abg MAC")
|
||||
mac.setAttribute("library", "ieee80211abgmaclayer")
|
||||
|
||||
names = self.getnames()
|
||||
macnames = names[:len(self._confmatrix_mac)]
|
||||
phynames = names[len(self._confmatrix_mac):]
|
||||
|
||||
# append all MAC options to macdoc
|
||||
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
||||
self.valueof(n, values))), macnames)
|
||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||
|
||||
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||
|
281
daemon/core/emane/nodes.py
Normal file
281
daemon/core/emane/nodes.py
Normal file
|
@ -0,0 +1,281 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
nodes.py: definition of an EmaneNode class for implementing configuration
|
||||
control of an EMANE emulation. An EmaneNode has several attached NEMs that
|
||||
share the same MAC+PHY model.
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
from core.api import coreapi
|
||||
from core.coreobj import PyCoreNet
|
||||
try:
|
||||
import emaneeventservice
|
||||
import emaneeventlocation
|
||||
except Exception, e:
|
||||
''' Don't require all CORE users to have EMANE libeventservice and its
|
||||
Python bindings installed.
|
||||
'''
|
||||
pass
|
||||
|
||||
class EmaneNet(PyCoreNet):
|
||||
''' EMANE network base class.
|
||||
'''
|
||||
apitype = coreapi.CORE_NODE_EMANE
|
||||
linktype = coreapi.CORE_LINK_WIRELESS
|
||||
type = "wlan" # icon used
|
||||
|
||||
class EmaneNode(EmaneNet):
|
||||
''' EMANE node contains NEM configuration and causes connected nodes
|
||||
to have TAP interfaces (instead of VEth). These are managed by the
|
||||
Emane controller object that exists in a session.
|
||||
'''
|
||||
def __init__(self, session, objid = None, name = None, verbose = False,
|
||||
start = True):
|
||||
PyCoreNet.__init__(self, session, objid, name, verbose, start)
|
||||
self.verbose = verbose
|
||||
self.conf = ""
|
||||
self.up = False
|
||||
self.nemidmap = {}
|
||||
self.model = None
|
||||
self.mobility = None
|
||||
|
||||
def linkconfig(self, netif, bw = None, delay = None,
|
||||
loss = None, duplicate = None, jitter = None, netif2 = None):
|
||||
''' The CommEffect model supports link configuration.
|
||||
'''
|
||||
if not self.model:
|
||||
return
|
||||
return self.model.linkconfig(netif=netif, bw=bw, delay=delay, loss=loss,
|
||||
duplicate=duplicate, jitter=jitter, netif2=netif2)
|
||||
|
||||
def config(self, conf):
|
||||
#print "emane", self.name, "got config:", conf
|
||||
self.conf = conf
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def link(self, netif1, netif2):
|
||||
pass
|
||||
|
||||
def unlink(self, netif1, netif2):
|
||||
pass
|
||||
|
||||
def setmodel(self, model, config):
|
||||
''' set the EmaneModel associated with this node
|
||||
'''
|
||||
if (self.verbose):
|
||||
self.info("adding model %s" % model._name)
|
||||
if model._type == coreapi.CORE_TLV_REG_WIRELESS:
|
||||
# EmaneModel really uses values from ConfigurableManager
|
||||
# when buildnemxml() is called, not during init()
|
||||
self.model = model(session=self.session, objid=self.objid,
|
||||
verbose=self.verbose)
|
||||
elif model._type == coreapi.CORE_TLV_REG_MOBILITY:
|
||||
self.mobility = model(session=self.session, objid=self.objid,
|
||||
verbose=self.verbose, values=config)
|
||||
|
||||
def setnemid(self, netif, nemid):
|
||||
''' Record an interface to numerical ID mapping. The Emane controller
|
||||
object manages and assigns these IDs for all NEMs.
|
||||
'''
|
||||
self.nemidmap[netif] = nemid
|
||||
|
||||
def getnemid(self, netif):
|
||||
''' Given an interface, return its numerical ID.
|
||||
'''
|
||||
if netif not in self.nemidmap:
|
||||
return None
|
||||
else:
|
||||
return self.nemidmap[netif]
|
||||
|
||||
def getnemnetif(self, nemid):
|
||||
''' Given a numerical NEM ID, return its interface. This returns the
|
||||
first interface that matches the given NEM ID.
|
||||
'''
|
||||
for netif in self.nemidmap:
|
||||
if self.nemidmap[netif] == nemid:
|
||||
return netif
|
||||
return None
|
||||
|
||||
def netifs(self, sort=True):
|
||||
''' Retrieve list of linked interfaces sorted by node number.
|
||||
'''
|
||||
return sorted(self._netif.values(), key=lambda ifc: ifc.node.objid)
|
||||
|
||||
def buildplatformxmlentry(self, doc):
|
||||
''' Return a dictionary of XML elements describing the NEMs
|
||||
connected to this EmaneNode for inclusion in the platform.xml file.
|
||||
'''
|
||||
ret = {}
|
||||
if self.model is None:
|
||||
self.info("warning: EmaneNode %s has no associated model" % \
|
||||
self.name)
|
||||
return ret
|
||||
for netif in self.netifs():
|
||||
# <nem name="NODE-001" definition="rfpipenem.xml">
|
||||
nementry = self.model.buildplatformxmlnementry(doc, self, netif)
|
||||
# <transport definition="transvirtual.xml" group="1">
|
||||
# <param name="device" value="n1.0.158" />
|
||||
# </transport>
|
||||
trans = self.model.buildplatformxmltransportentry(doc, self, netif)
|
||||
nementry.appendChild(trans)
|
||||
ret[netif] = nementry
|
||||
|
||||
return ret
|
||||
|
||||
def buildnemxmlfiles(self, emane):
|
||||
''' Let the configured model build the necessary nem, mac, and phy
|
||||
XMLs.
|
||||
'''
|
||||
if self.model is None:
|
||||
return
|
||||
# build XML for overall network (EmaneNode) configs
|
||||
self.model.buildnemxmlfiles(emane, ifc=None)
|
||||
# build XML for specific interface (NEM) configs
|
||||
need_virtual = False
|
||||
need_raw = False
|
||||
vtype = "virtual"
|
||||
rtype = "raw"
|
||||
for netif in self.netifs():
|
||||
self.model.buildnemxmlfiles(emane, netif)
|
||||
if "virtual" in netif.transport_type:
|
||||
need_virtual = True
|
||||
vtype = netif.transport_type
|
||||
else:
|
||||
need_raw = True
|
||||
rtype = netif.transport_type
|
||||
# build transport XML files depending on type of interfaces involved
|
||||
if need_virtual:
|
||||
self.buildtransportxml(emane, vtype)
|
||||
if need_raw:
|
||||
self.buildtransportxml(emane, rtype)
|
||||
|
||||
def buildtransportxml(self, emane, type):
|
||||
''' Write a transport XML file for the Virtual or Raw Transport.
|
||||
'''
|
||||
transdoc = emane.xmldoc("transport")
|
||||
trans = transdoc.getElementsByTagName("transport").pop()
|
||||
trans.setAttribute("name", "%s Transport" % type.capitalize())
|
||||
trans.setAttribute("library", "trans%s" % type.lower())
|
||||
trans.appendChild(emane.xmlparam(transdoc, "bitrate", "0"))
|
||||
if "virtual" in type.lower():
|
||||
trans.appendChild(emane.xmlparam(transdoc, "devicepath",
|
||||
"/dev/net/tun"))
|
||||
emane.xmlwrite(transdoc, self.transportxmlname(type.lower()))
|
||||
|
||||
def transportxmlname(self, type):
|
||||
''' Return the string name for the Transport XML file,
|
||||
e.g. 'n3transvirtual.xml'
|
||||
'''
|
||||
return "n%strans%s.xml" % (self.objid, type)
|
||||
|
||||
|
||||
def installnetifs(self):
|
||||
''' Install TAP devices into their namespaces. This is done after
|
||||
EMANE daemons have been started, because that is their only chance
|
||||
to bind to the TAPs.
|
||||
'''
|
||||
if not self.session.emane.doeventmonitor() and \
|
||||
self.session.emane.service is None:
|
||||
warntxt = "unable to publish EMANE events because the eventservice "
|
||||
warntxt += "Python bindings failed to load"
|
||||
self.session.exception(coreapi.CORE_EXCP_LEVEL_ERROR, self.name,
|
||||
self.objid, warntxt)
|
||||
|
||||
for netif in self.netifs():
|
||||
if "virtual" in netif.transport_type.lower():
|
||||
netif.install()
|
||||
# if we are listening for EMANE events, don't generate them
|
||||
if self.session.emane.doeventmonitor():
|
||||
netif.poshook = None
|
||||
continue
|
||||
# at this point we register location handlers for generating
|
||||
# EMANE location events
|
||||
netif.poshook = self.setnemposition
|
||||
(x,y,z) = netif.node.position.get()
|
||||
self.setnemposition(netif, x, y, z)
|
||||
|
||||
def deinstallnetifs(self):
|
||||
''' Uninstall TAP devices. This invokes their shutdown method for
|
||||
any required cleanup; the device may be actually removed when
|
||||
emanetransportd terminates.
|
||||
'''
|
||||
for netif in self.netifs():
|
||||
if "virtual" in netif.transport_type.lower():
|
||||
netif.shutdown()
|
||||
netif.poshook = None
|
||||
|
||||
def setnemposition(self, netif, x, y, z):
|
||||
''' Publish a NEM location change event using the EMANE event service.
|
||||
'''
|
||||
if self.session.emane.service is None:
|
||||
if self.verbose:
|
||||
self.info("position service not available")
|
||||
return
|
||||
nemid = self.getnemid(netif)
|
||||
ifname = netif.localname
|
||||
if nemid is None:
|
||||
self.info("nemid for %s is unknown" % ifname)
|
||||
return
|
||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
||||
if self.verbose:
|
||||
self.info("setnemposition %s (%s) x,y,z=(%d,%d,%s)"
|
||||
"(%.6f,%.6f,%.6f)" % \
|
||||
(ifname, nemid, x, y, z, lat, long, alt))
|
||||
event = emaneeventlocation.EventLocation(1)
|
||||
# altitude must be an integer or warning is printed
|
||||
# unused: yaw, pitch, roll, azimuth, elevation, velocity
|
||||
alt = int(round(alt))
|
||||
event.set(0, nemid, lat, long, alt)
|
||||
self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
|
||||
emaneeventservice.PLATFORMID_ANY,
|
||||
emaneeventservice.NEMID_ANY,
|
||||
emaneeventservice.COMPONENTID_ANY,
|
||||
event.export())
|
||||
|
||||
def setnempositions(self, moved_netifs):
|
||||
''' Several NEMs have moved, from e.g. a WaypointMobilityModel
|
||||
calculation. Generate an EMANE Location Event having several
|
||||
entries for each netif that has moved.
|
||||
'''
|
||||
if len(moved_netifs) == 0:
|
||||
return
|
||||
if self.session.emane.service is None:
|
||||
if self.verbose:
|
||||
self.info("position service not available")
|
||||
return
|
||||
|
||||
event = emaneeventlocation.EventLocation(len(moved_netifs))
|
||||
i = 0
|
||||
for netif in moved_netifs:
|
||||
nemid = self.getnemid(netif)
|
||||
ifname = netif.localname
|
||||
if nemid is None:
|
||||
self.info("nemid for %s is unknown" % ifname)
|
||||
continue
|
||||
(x, y, z) = netif.node.getposition()
|
||||
(lat, long, alt) = self.session.location.getgeo(x, y, z)
|
||||
if self.verbose:
|
||||
self.info("setnempositions %d %s (%s) x,y,z=(%d,%d,%s)"
|
||||
"(%.6f,%.6f,%.6f)" % \
|
||||
(i, ifname, nemid, x, y, z, lat, long, alt))
|
||||
# altitude must be an integer or warning is printed
|
||||
alt = int(round(alt))
|
||||
event.set(i, nemid, lat, long, alt)
|
||||
i += 1
|
||||
|
||||
self.session.emane.service.publish(emaneeventlocation.EVENT_ID,
|
||||
emaneeventservice.PLATFORMID_ANY,
|
||||
emaneeventservice.NEMID_ANY,
|
||||
emaneeventservice.COMPONENTID_ANY,
|
||||
event.export())
|
||||
|
||||
|
106
daemon/core/emane/rfpipe.py
Normal file
106
daemon/core/emane/rfpipe.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
# author: Harry Bullen <hbullen@i-a-i.com>
|
||||
#
|
||||
'''
|
||||
rfpipe.py: EMANE RF-PIPE model for CORE
|
||||
'''
|
||||
|
||||
import sys
|
||||
import string
|
||||
from core.api import coreapi
|
||||
|
||||
from core.constants import *
|
||||
from emane import EmaneModel
|
||||
from universal import EmaneUniversalModel
|
||||
|
||||
class EmaneRfPipeModel(EmaneModel):
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
EmaneModel.__init__(self, session, objid, verbose)
|
||||
|
||||
# model name
|
||||
_name = "emane_rfpipe"
|
||||
|
||||
# configuration parameters are
|
||||
# ( 'name', 'type', 'default', 'possible-value-list', 'caption')
|
||||
# MAC parameters
|
||||
_confmatrix_mac = [
|
||||
("enablepromiscuousmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'True,False', 'enable promiscuous mode'),
|
||||
("datarate", coreapi.CONF_DATA_TYPE_UINT32, '1M',
|
||||
'', 'data rate (bps)'),
|
||||
("jitter", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'', 'transmission jitter (usec)'),
|
||||
("delay", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'', 'transmission delay (usec)'),
|
||||
("flowcontrolenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable traffic flow control'),
|
||||
("flowcontroltokens", coreapi.CONF_DATA_TYPE_UINT16, '10',
|
||||
'', 'number of flow control tokens'),
|
||||
("enabletighttiming", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off', 'enable tight timing for pkt delay'),
|
||||
("pcrcurveuri", coreapi.CONF_DATA_TYPE_STRING,
|
||||
'/usr/share/emane/models/rfpipe/xml/rfpipepcr.xml',
|
||||
'', 'SINR/PCR curve file'),
|
||||
("transmissioncontrolmap", coreapi.CONF_DATA_TYPE_STRING, '',
|
||||
'', 'tx control map (nem:rate:freq:tx_dBm)'),
|
||||
]
|
||||
|
||||
# PHY parameters from Universal PHY
|
||||
_confmatrix_phy = EmaneUniversalModel._confmatrix
|
||||
|
||||
_confmatrix = _confmatrix_mac + _confmatrix_phy
|
||||
|
||||
# value groupings
|
||||
_confgroups = "RF-PIPE MAC Parameters:1-%d|Universal PHY Parameters:%d-%d" \
|
||||
% ( len(_confmatrix_mac), len(_confmatrix_mac) + 1, len(_confmatrix))
|
||||
|
||||
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_rfpipenem.xml,
|
||||
nXXemane_rfpipemac.xml, nXXemane_rfpipephy.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()
|
||||
nem.setAttribute("name", "RF-PIPE NEM")
|
||||
mactag = nemdoc.createElement("mac")
|
||||
mactag.setAttribute("definition", self.macxmlname(ifc))
|
||||
nem.appendChild(mactag)
|
||||
phytag = nemdoc.createElement("phy")
|
||||
phytag.setAttribute("definition", self.phyxmlname(ifc))
|
||||
nem.appendChild(phytag)
|
||||
e.xmlwrite(nemdoc, self.nemxmlname(ifc))
|
||||
|
||||
names = list(self.getnames())
|
||||
macnames = names[:len(self._confmatrix_mac)]
|
||||
phynames = names[len(self._confmatrix_mac):]
|
||||
|
||||
macdoc = e.xmldoc("mac")
|
||||
mac = macdoc.getElementsByTagName("mac").pop()
|
||||
mac.setAttribute("name", "RF-PIPE MAC")
|
||||
mac.setAttribute("library", "rfpipemaclayer")
|
||||
if self.valueof("transmissioncontrolmap", values) is "":
|
||||
macnames.remove("transmissioncontrolmap")
|
||||
# EMANE 0.7.4 support
|
||||
if e.emane074:
|
||||
# convert datarate from bps to kbps
|
||||
i = names.index('datarate')
|
||||
values = list(values)
|
||||
values[i] = self.emane074_fixup(values[i], 1000)
|
||||
# append MAC options to macdoc
|
||||
map( lambda n: mac.appendChild(e.xmlparam(macdoc, n, \
|
||||
self.valueof(n, values))), macnames)
|
||||
e.xmlwrite(macdoc, self.macxmlname(ifc))
|
||||
|
||||
phydoc = EmaneUniversalModel.getphydoc(e, self, values, phynames)
|
||||
e.xmlwrite(phydoc, self.phyxmlname(ifc))
|
||||
|
113
daemon/core/emane/universal.py
Normal file
113
daemon/core/emane/universal.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2010-2013 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
universal.py: EMANE Universal PHY model for CORE. Enumerates configuration items
|
||||
used for the Universal PHY.
|
||||
'''
|
||||
|
||||
import sys
|
||||
import string
|
||||
from core.api import coreapi
|
||||
|
||||
from core.constants import *
|
||||
from emane import EmaneModel
|
||||
|
||||
class EmaneUniversalModel(EmaneModel):
|
||||
''' This Univeral PHY model is meant to be imported by other models,
|
||||
not instantiated.
|
||||
'''
|
||||
def __init__(self, session, objid = None, verbose = False):
|
||||
raise SyntaxError
|
||||
|
||||
_name = "emane_universal"
|
||||
_xmlname = "universalphy"
|
||||
_xmllibrary = "universalphylayer"
|
||||
|
||||
# universal PHY parameters
|
||||
_confmatrix = [
|
||||
("antennagain", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'','antenna gain (dBi)'),
|
||||
("antennaazimuth", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'','antenna azimuth (deg)'),
|
||||
("antennaelevation", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'','antenna elevation (deg)'),
|
||||
("antennaprofileid", coreapi.CONF_DATA_TYPE_STRING, '1',
|
||||
'','antenna profile ID'),
|
||||
("antennaprofilemanifesturi", coreapi.CONF_DATA_TYPE_STRING, '',
|
||||
'','antenna profile manifest URI'),
|
||||
("antennaprofileenable", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off','antenna profile mode'),
|
||||
("bandwidth", coreapi.CONF_DATA_TYPE_UINT64, '1M',
|
||||
'', 'rf bandwidth (hz)'),
|
||||
("defaultconnectivitymode", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
||||
'On,Off','default connectivity'),
|
||||
("frequency", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
|
||||
'','frequency (Hz)'),
|
||||
("frequencyofinterest", coreapi.CONF_DATA_TYPE_UINT64, '2.347G',
|
||||
'','frequency of interest (Hz)'),
|
||||
("frequencyofinterestfilterenable", coreapi.CONF_DATA_TYPE_BOOL, '1',
|
||||
'On,Off','frequency of interest filter enable'),
|
||||
("noiseprocessingmode", coreapi.CONF_DATA_TYPE_BOOL, '0',
|
||||
'On,Off','enable noise processing'),
|
||||
("pathlossmode", coreapi.CONF_DATA_TYPE_STRING, '2ray',
|
||||
'pathloss,2ray,freespace','path loss mode'),
|
||||
("subid", coreapi.CONF_DATA_TYPE_UINT16, '1',
|
||||
'','subid'),
|
||||
("systemnoisefigure", coreapi.CONF_DATA_TYPE_FLOAT, '4.0',
|
||||
'','system noise figure (dB)'),
|
||||
("txpower", coreapi.CONF_DATA_TYPE_FLOAT, '0.0',
|
||||
'','transmit power (dBm)'),
|
||||
]
|
||||
|
||||
# old parameters
|
||||
_confmatrix_ver074 = [
|
||||
("antennaazimuthbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '360.0',
|
||||
'','azimith beam width (deg)'),
|
||||
("antennaelevationbeamwidth", coreapi.CONF_DATA_TYPE_FLOAT, '180.0',
|
||||
'','elevation beam width (deg)'),
|
||||
("antennatype", coreapi.CONF_DATA_TYPE_STRING, 'omnidirectional',
|
||||
'omnidirectional,unidirectional','antenna type'),
|
||||
]
|
||||
|
||||
# parameters that require unit conversion for 0.7.4
|
||||
_update_ver074 = ("bandwidth", "frequency", "frequencyofinterest")
|
||||
# parameters that should be removed for 0.7.4
|
||||
_remove_ver074 = ("antennaprofileenable", "antennaprofileid",
|
||||
"antennaprofilemanifesturi",
|
||||
"frequencyofinterestfilterenable")
|
||||
|
||||
|
||||
@classmethod
|
||||
def getphydoc(cls, e, mac, values, phynames):
|
||||
phydoc = e.xmldoc("phy")
|
||||
phy = phydoc.getElementsByTagName("phy").pop()
|
||||
phy.setAttribute("name", cls._xmlname)
|
||||
phy.setAttribute("library", cls._xmllibrary)
|
||||
# EMANE 0.7.4 suppport - to be removed when 0.7.4 support is deprecated
|
||||
if e.emane074:
|
||||
names = mac.getnames()
|
||||
values = list(values)
|
||||
phynames = list(phynames)
|
||||
# update units for some parameters
|
||||
for p in cls._update_ver074:
|
||||
i = names.index(p)
|
||||
# these all happen to be KHz, so 1000 is used
|
||||
values[i] = cls.emane074_fixup(values[i], 1000)
|
||||
# remove new incompatible options
|
||||
for p in cls._remove_ver074:
|
||||
phynames.remove(p)
|
||||
# insert old options with their default values
|
||||
for old in cls._confmatrix_ver074:
|
||||
phy.appendChild(e.xmlparam(phydoc, old[0], old[2]))
|
||||
|
||||
# append all PHY options to phydoc
|
||||
map( lambda n: phy.appendChild(e.xmlparam(phydoc, n, \
|
||||
mac.valueof(n, values))), phynames)
|
||||
return phydoc
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue