2017-04-25 08:45:34 -07:00
|
|
|
"""
|
2013-08-29 14:21:13 +00:00
|
|
|
commeffect.py: EMANE CommEffect model for CORE
|
2017-04-25 08:45:34 -07:00
|
|
|
"""
|
|
|
|
|
2019-02-16 09:50:19 -08:00
|
|
|
import logging
|
2021-03-19 16:54:24 -07:00
|
|
|
from pathlib import Path
|
2020-01-14 16:27:08 -08:00
|
|
|
from typing import Dict, List
|
2019-09-10 14:20:51 -07:00
|
|
|
|
2018-07-03 18:49:36 -07:00
|
|
|
from lxml import etree
|
|
|
|
|
2020-01-14 16:27:08 -08:00
|
|
|
from core.config import ConfigGroup, Configuration
|
2019-09-10 14:20:51 -07:00
|
|
|
from core.emane import emanemanifest, emanemodel
|
2020-06-16 12:50:24 -07:00
|
|
|
from core.emulator.data import LinkOptions
|
2020-01-14 16:27:08 -08:00
|
|
|
from core.nodes.interface import CoreInterface
|
2018-07-03 18:49:36 -07:00
|
|
|
from core.xml import emanexml
|
2013-08-29 14:21:13 +00:00
|
|
|
|
2021-04-21 21:09:35 -07:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2013-08-29 14:21:13 +00:00
|
|
|
try:
|
2018-03-29 14:04:15 -07:00
|
|
|
from emane.events.commeffectevent import CommEffectEvent
|
2017-04-25 08:45:34 -07:00
|
|
|
except ImportError:
|
2018-04-20 21:22:08 -07:00
|
|
|
try:
|
|
|
|
from emanesh.events.commeffectevent import CommEffectEvent
|
|
|
|
except ImportError:
|
2020-06-12 09:52:01 -07:00
|
|
|
CommEffectEvent = None
|
2021-04-21 21:09:35 -07:00
|
|
|
logger.debug("compatible emane python bindings not installed")
|
2017-04-25 08:45:34 -07:00
|
|
|
|
2013-08-29 14:21:13 +00:00
|
|
|
|
2020-01-14 16:27:08 -08:00
|
|
|
def convert_none(x: float) -> int:
|
2018-03-19 16:33:36 -07:00
|
|
|
"""
|
|
|
|
Helper to use 0 for None values.
|
|
|
|
"""
|
2019-10-17 14:52:31 -07:00
|
|
|
if isinstance(x, str):
|
2018-03-19 16:33:36 -07:00
|
|
|
x = float(x)
|
|
|
|
if x is None:
|
|
|
|
return 0
|
|
|
|
else:
|
|
|
|
return int(x)
|
2013-08-29 14:21:13 +00:00
|
|
|
|
2018-03-19 16:33:36 -07:00
|
|
|
|
2018-03-29 14:38:32 -07:00
|
|
|
class EmaneCommEffectModel(emanemodel.EmaneModel):
|
2020-06-12 09:52:01 -07:00
|
|
|
name: str = "emane_commeffect"
|
|
|
|
shim_library: str = "commeffectshim"
|
|
|
|
shim_xml: str = "commeffectshim.xml"
|
|
|
|
shim_defaults: Dict[str, str] = {}
|
|
|
|
config_shim: List[Configuration] = []
|
2013-08-29 14:21:13 +00:00
|
|
|
|
2018-07-11 09:19:06 -07:00
|
|
|
# comm effect does not need the default phy and external configurations
|
2020-06-12 09:52:01 -07:00
|
|
|
phy_config: List[Configuration] = []
|
|
|
|
external_config: List[Configuration] = []
|
2019-04-08 09:49:37 -07:00
|
|
|
|
|
|
|
@classmethod
|
2021-03-19 16:54:24 -07:00
|
|
|
def load(cls, emane_prefix: Path) -> None:
|
2021-05-18 21:29:38 -07:00
|
|
|
cls._load_platform_config(emane_prefix)
|
2021-03-19 16:54:24 -07:00
|
|
|
shim_xml_path = emane_prefix / "share/emane/manifest" / cls.shim_xml
|
2019-04-08 09:49:37 -07:00
|
|
|
cls.config_shim = emanemanifest.parse(shim_xml_path, cls.shim_defaults)
|
2018-07-03 18:49:36 -07:00
|
|
|
|
2018-06-06 14:51:45 -07:00
|
|
|
@classmethod
|
2020-01-14 16:27:08 -08:00
|
|
|
def configurations(cls) -> List[Configuration]:
|
2021-05-18 21:29:38 -07:00
|
|
|
return cls.platform_config + cls.config_shim
|
2018-06-06 14:51:45 -07:00
|
|
|
|
|
|
|
@classmethod
|
2020-01-14 16:27:08 -08:00
|
|
|
def config_groups(cls) -> List[ConfigGroup]:
|
2021-05-18 21:29:38 -07:00
|
|
|
platform_len = len(cls.platform_config)
|
|
|
|
return [
|
|
|
|
ConfigGroup("Platform Parameters", 1, platform_len),
|
|
|
|
ConfigGroup(
|
|
|
|
"CommEffect SHIM Parameters",
|
|
|
|
platform_len + 1,
|
|
|
|
len(cls.configurations()),
|
|
|
|
),
|
|
|
|
]
|
2018-03-19 16:33:36 -07:00
|
|
|
|
2020-07-02 15:37:51 -07:00
|
|
|
def build_xml_files(self, config: Dict[str, str], iface: CoreInterface) -> None:
|
2017-04-25 08:45:34 -07:00
|
|
|
"""
|
|
|
|
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.
|
2018-03-28 13:58:49 -07:00
|
|
|
|
2020-01-16 11:00:57 -08:00
|
|
|
:param config: emane model configuration for the node and interface
|
2020-06-16 09:30:16 -07:00
|
|
|
:param iface: interface for the emane node
|
2018-03-28 13:58:49 -07:00
|
|
|
:return: nothing
|
2017-04-25 08:45:34 -07:00
|
|
|
"""
|
2018-07-03 18:49:36 -07:00
|
|
|
# create and write nem document
|
2019-10-18 10:33:31 -07:00
|
|
|
nem_element = etree.Element("nem", name=f"{self.name} NEM", type="unstructured")
|
2020-07-02 17:49:56 -07:00
|
|
|
transport_name = emanexml.transport_file_name(iface)
|
|
|
|
etree.SubElement(nem_element, "transport", definition=transport_name)
|
2018-03-30 12:08:33 -07:00
|
|
|
|
2018-07-03 18:49:36 -07:00
|
|
|
# set shim configuration
|
2020-07-02 17:49:56 -07:00
|
|
|
nem_name = emanexml.nem_file_name(iface)
|
|
|
|
shim_name = emanexml.shim_file_name(iface)
|
2018-07-03 18:49:36 -07:00
|
|
|
etree.SubElement(nem_element, "shim", definition=shim_name)
|
2020-09-09 10:27:06 -07:00
|
|
|
emanexml.create_node_file(iface.node, nem_element, "nem", nem_name)
|
2018-07-03 18:49:36 -07:00
|
|
|
|
|
|
|
# create and write shim document
|
2019-09-10 15:10:24 -07:00
|
|
|
shim_element = etree.Element(
|
2019-10-18 10:33:31 -07:00
|
|
|
"shim", name=f"{self.name} SHIM", library=self.shim_library
|
2019-09-10 15:10:24 -07:00
|
|
|
)
|
2018-03-30 12:08:33 -07:00
|
|
|
|
2013-08-29 14:21:13 +00:00
|
|
|
# append all shim options (except filterfile) to shimdoc
|
2018-06-06 14:51:45 -07:00
|
|
|
for configuration in self.config_shim:
|
|
|
|
name = configuration.id
|
|
|
|
if name == "filterfile":
|
|
|
|
continue
|
|
|
|
value = config[name]
|
2018-07-03 18:49:36 -07:00
|
|
|
emanexml.add_param(shim_element, name, value)
|
2018-03-28 13:58:49 -07:00
|
|
|
|
2013-08-29 14:21:13 +00:00
|
|
|
# empty filterfile is not allowed
|
2018-06-06 14:51:45 -07:00
|
|
|
ff = config["filterfile"]
|
2017-08-04 14:34:44 -07:00
|
|
|
if ff.strip() != "":
|
2018-07-03 18:49:36 -07:00
|
|
|
emanexml.add_param(shim_element, "filterfile", ff)
|
2020-09-09 10:27:06 -07:00
|
|
|
emanexml.create_node_file(iface.node, shim_element, "shim", shim_name)
|
2018-07-03 18:49:36 -07:00
|
|
|
|
2020-07-02 17:49:56 -07:00
|
|
|
# create transport xml
|
|
|
|
emanexml.create_transport_xml(iface, config)
|
2018-03-28 13:58:49 -07:00
|
|
|
|
2019-09-10 15:10:24 -07:00
|
|
|
def linkconfig(
|
2020-06-16 09:30:16 -07:00
|
|
|
self, iface: CoreInterface, options: LinkOptions, iface2: CoreInterface = None
|
2020-01-14 16:27:08 -08:00
|
|
|
) -> None:
|
2017-04-25 08:45:34 -07:00
|
|
|
"""
|
|
|
|
Generate CommEffect events when a Link Message is received having
|
2013-08-29 14:21:13 +00:00
|
|
|
link parameters.
|
2017-04-25 08:45:34 -07:00
|
|
|
"""
|
2020-06-16 09:30:16 -07:00
|
|
|
if iface is None or iface2 is None:
|
2021-04-21 21:09:35 -07:00
|
|
|
logger.warning("%s: missing NEM information", self.name)
|
2013-08-29 14:21:13 +00:00
|
|
|
return
|
|
|
|
# TODO: batch these into multiple events per transmission
|
2013-12-03 19:48:06 +00:00
|
|
|
# TODO: may want to split out seconds portion of delay and jitter
|
2018-03-19 16:33:36 -07:00
|
|
|
event = CommEffectEvent()
|
2020-07-05 21:29:03 -07:00
|
|
|
nem1 = self.session.emane.get_nem_id(iface)
|
|
|
|
nem2 = self.session.emane.get_nem_id(iface2)
|
2021-04-21 21:09:35 -07:00
|
|
|
logger.info("sending comm effect event")
|
2018-03-19 16:33:36 -07:00
|
|
|
event.append(
|
2020-07-05 21:29:03 -07:00
|
|
|
nem1,
|
2020-06-09 13:41:31 -07:00
|
|
|
latency=convert_none(options.delay),
|
|
|
|
jitter=convert_none(options.jitter),
|
2020-06-12 12:49:53 -07:00
|
|
|
loss=convert_none(options.loss),
|
2020-06-09 13:41:31 -07:00
|
|
|
duplicate=convert_none(options.dup),
|
|
|
|
unicast=int(convert_none(options.bandwidth)),
|
|
|
|
broadcast=int(convert_none(options.bandwidth)),
|
2018-03-19 16:33:36 -07:00
|
|
|
)
|
2021-05-24 21:41:05 -07:00
|
|
|
self.session.emane.publish_event(nem2, event)
|