daemon: refactoring to load emane models one time during startup, updates to account for this change

This commit is contained in:
Blake Harnden 2021-05-07 10:40:18 -07:00
parent 1ddb7b7b24
commit 50e3aadc6b
32 changed files with 271 additions and 332 deletions

View file

View file

@ -0,0 +1,35 @@
"""
EMANE Bypass model for CORE
"""
from typing import List, Set
from core.config import Configuration
from core.emane import emanemodel
from core.emulator.enumerations import ConfigDataTypes
class EmaneBypassModel(emanemodel.EmaneModel):
name: str = "emane_bypass"
# values to ignore, when writing xml files
config_ignore: Set[str] = {"none"}
# mac definitions
mac_library: str = "bypassmaclayer"
mac_config: List[Configuration] = [
Configuration(
id="none",
type=ConfigDataTypes.BOOL,
default="0",
label="There are no parameters for the bypass model.",
)
]
# phy definitions
phy_library: str = "bypassphylayer"
phy_config: List[Configuration] = []
@classmethod
def load(cls, emane_prefix: str) -> None:
# ignore default logic
pass

View file

@ -0,0 +1,140 @@
"""
commeffect.py: EMANE CommEffect model for CORE
"""
import logging
from pathlib import Path
from typing import Dict, List
from lxml import etree
from core.config import ConfigGroup, Configuration
from core.emane import emanemanifest, emanemodel
from core.emulator.data import LinkOptions
from core.nodes.interface import CoreInterface
from core.xml import emanexml
logger = logging.getLogger(__name__)
try:
from emane.events.commeffectevent import CommEffectEvent
except ImportError:
try:
from emanesh.events.commeffectevent import CommEffectEvent
except ImportError:
CommEffectEvent = None
logger.debug("compatible emane python bindings not installed")
def convert_none(x: float) -> int:
"""
Helper to use 0 for None values.
"""
if isinstance(x, str):
x = float(x)
if x is None:
return 0
else:
return int(x)
class EmaneCommEffectModel(emanemodel.EmaneModel):
name: str = "emane_commeffect"
shim_library: str = "commeffectshim"
shim_xml: str = "commeffectshim.xml"
shim_defaults: Dict[str, str] = {}
config_shim: List[Configuration] = []
# comm effect does not need the default phy and external configurations
phy_config: List[Configuration] = []
external_config: List[Configuration] = []
@classmethod
def load(cls, emane_prefix: Path) -> None:
shim_xml_path = emane_prefix / "share/emane/manifest" / cls.shim_xml
cls.config_shim = emanemanifest.parse(shim_xml_path, cls.shim_defaults)
@classmethod
def configurations(cls) -> List[Configuration]:
return cls.config_shim
@classmethod
def config_groups(cls) -> List[ConfigGroup]:
return [ConfigGroup("CommEffect SHIM Parameters", 1, len(cls.configurations()))]
def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
"""
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.
:param config: emane model configuration for the node and interface
:param iface: interface for the emane node
:return: nothing
"""
# create and write nem document
nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured")
transport_name = emanexml.transport_file_name(iface)
etree.SubElement(nem_element, "transport", definition=transport_name)
# set shim configuration
nem_name = emanexml.nem_file_name(iface)
shim_name = emanexml.shim_file_name(iface)
etree.SubElement(nem_element, "shim", definition=shim_name)
emanexml.create_node_file(iface.node, nem_element, "nem", nem_name)
# create and write shim document
shim_element = etree.Element(
"shim", name=f"{self.name} SHIM", library=self.shim_library
)
# append all shim options (except filterfile) to shimdoc
for configuration in self.config_shim:
name = configuration.id
if name == "filterfile":
continue
value = config[name]
emanexml.add_param(shim_element, name, value)
# empty filterfile is not allowed
ff = config["filterfile"]
if ff.strip() != "":
emanexml.add_param(shim_element, "filterfile", ff)
emanexml.create_node_file(iface.node, shim_element, "shim", shim_name)
# create transport xml
emanexml.create_transport_xml(iface, config)
def linkconfig(
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
) -> None:
"""
Generate CommEffect events when a Link Message is received having
link parameters.
"""
service = self.session.emane.service
if service is None:
logger.warning("%s: EMANE event service unavailable", self.name)
return
if iface is None or iface2 is None:
logger.warning("%s: missing NEM information", self.name)
return
# TODO: batch these into multiple events per transmission
# TODO: may want to split out seconds portion of delay and jitter
event = CommEffectEvent()
nem1 = self.session.emane.get_nem_id(iface)
nem2 = self.session.emane.get_nem_id(iface2)
logger.info("sending comm effect event")
event.append(
nem1,
latency=convert_none(options.delay),
jitter=convert_none(options.jitter),
loss=convert_none(options.loss),
duplicate=convert_none(options.dup),
unicast=int(convert_none(options.bandwidth)),
broadcast=int(convert_none(options.bandwidth)),
)
service.publish(nem2, event)

View file

@ -0,0 +1,22 @@
"""
ieee80211abg.py: EMANE IEEE 802.11abg model for CORE
"""
from pathlib import Path
from core.emane import emanemodel
class EmaneIeee80211abgModel(emanemodel.EmaneModel):
# model name
name: str = "emane_ieee80211abg"
# mac configuration
mac_library: str = "ieee80211abgmaclayer"
mac_xml: str = "ieee80211abgmaclayer.xml"
@classmethod
def load(cls, emane_prefix: Path) -> None:
cls.mac_defaults["pcrcurveuri"] = str(
emane_prefix / "share/emane/xml/models/mac/ieee80211abg/ieee80211pcr.xml"
)
super().load(emane_prefix)

View file

@ -0,0 +1,22 @@
"""
rfpipe.py: EMANE RF-PIPE model for CORE
"""
from pathlib import Path
from core.emane import emanemodel
class EmaneRfPipeModel(emanemodel.EmaneModel):
# model name
name: str = "emane_rfpipe"
# mac configuration
mac_library: str = "rfpipemaclayer"
mac_xml: str = "rfpipemaclayer.xml"
@classmethod
def load(cls, emane_prefix: Path) -> None:
cls.mac_defaults["pcrcurveuri"] = str(
emane_prefix / "share/emane/xml/models/mac/rfpipe/rfpipepcr.xml"
)
super().load(emane_prefix)

View file

@ -0,0 +1,66 @@
"""
tdma.py: EMANE TDMA model bindings for CORE
"""
import logging
from pathlib import Path
from typing import Set
from core import constants, utils
from core.config import Configuration
from core.emane import emanemodel
from core.emulator.enumerations import ConfigDataTypes
logger = logging.getLogger(__name__)
class EmaneTdmaModel(emanemodel.EmaneModel):
# model name
name: str = "emane_tdma"
# mac configuration
mac_library: str = "tdmaeventschedulerradiomodel"
mac_xml: str = "tdmaeventschedulerradiomodel.xml"
# add custom schedule options and ignore it when writing emane xml
schedule_name: str = "schedule"
default_schedule: Path = (
constants.CORE_DATA_DIR / "examples" / "tdma" / "schedule.xml"
)
config_ignore: Set[str] = {schedule_name}
@classmethod
def load(cls, emane_prefix: Path) -> None:
cls.mac_defaults["pcrcurveuri"] = str(
emane_prefix
/ "share/emane/xml/models/mac/tdmaeventscheduler/tdmabasemodelpcr.xml"
)
super().load(emane_prefix)
config_item = Configuration(
id=cls.schedule_name,
type=ConfigDataTypes.STRING,
default=str(cls.default_schedule),
label="TDMA schedule file (core)",
)
cls.mac_config.insert(0, config_item)
def post_startup(self) -> None:
"""
Logic to execute after the emane manager is finished with startup.
:return: nothing
"""
# get configured schedule
config = self.session.emane.get_config(self.id, self.name)
if not config:
return
schedule = Path(config[self.schedule_name])
if not schedule.is_file():
logger.warning("ignoring invalid tdma schedule: %s", schedule)
return
# initiate tdma schedule
event_device = self.session.emane.event_device
logger.info(
"setting up tdma schedule: schedule(%s) device(%s)", schedule, event_device
)
utils.cmd(f"emaneevent-tdmaschedule -i {event_device} {schedule}")