diff --git a/daemon/core/xml/corexml.py b/daemon/core/xml/corexml.py index 0266912d..df73901f 100644 --- a/daemon/core/xml/corexml.py +++ b/daemon/core/xml/corexml.py @@ -1,17 +1,30 @@ import logging +from typing import TYPE_CHECKING, Any, Dict, Generic, List, Optional, Type, TypeVar from lxml import etree import core.nodes.base import core.nodes.physical from core.emane.nodes import EmaneNet +from core.emulator.data import LinkData from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions from core.emulator.enumerations import NodeTypes -from core.nodes.base import CoreNetworkBase +from core.nodes.base import CoreNetworkBase, NodeBase from core.nodes.network import CtrlNet +from core.services.coreservices import CoreService + +if TYPE_CHECKING: + from core.emane.emanemanager import EmaneGlobalModel + from core.emane.emanemodel import EmaneModel + from core.emulator.session import Session + + EmaneModelType = Type[EmaneModel] +T = TypeVar("T") -def write_xml_file(xml_element, file_path, doctype=None): +def write_xml_file( + xml_element: etree.Element, file_path: str, doctype: str = None +) -> None: xml_data = etree.tostring( xml_element, xml_declaration=True, @@ -23,27 +36,27 @@ def write_xml_file(xml_element, file_path, doctype=None): xml_file.write(xml_data) -def get_type(element, name, _type): +def get_type(element: etree.Element, name: str, _type: Generic[T]) -> Optional[T]: value = element.get(name) if value is not None: value = _type(value) return value -def get_float(element, name): +def get_float(element: etree.Element, name: str) -> float: return get_type(element, name, float) -def get_int(element, name): +def get_int(element: etree.Element, name: str) -> int: return get_type(element, name, int) -def add_attribute(element, name, value): +def add_attribute(element: etree.Element, name: str, value: Any) -> None: if value is not None: element.set(name, str(value)) -def create_interface_data(interface_element): +def create_interface_data(interface_element: etree.Element) -> InterfaceData: interface_id = int(interface_element.get("id")) name = interface_element.get("name") mac = interface_element.get("mac") @@ -54,7 +67,9 @@ def create_interface_data(interface_element): return InterfaceData(interface_id, name, mac, ip4, ip4_mask, ip6, ip6_mask) -def create_emane_config(node_id, emane_config, config): +def create_emane_config( + node_id: int, emane_config: "EmaneGlobalModel", config: Dict[str, str] +) -> etree.Element: emane_configuration = etree.Element("emane_configuration") add_attribute(emane_configuration, "node", node_id) add_attribute(emane_configuration, "model", "emane") @@ -72,7 +87,9 @@ def create_emane_config(node_id, emane_config, config): return emane_configuration -def create_emane_model_config(node_id, model, config): +def create_emane_model_config( + node_id: int, model: "EmaneModelType", config: Dict[str, str] +) -> etree.Element: emane_element = etree.Element("emane_configuration") add_attribute(emane_element, "node", node_id) add_attribute(emane_element, "model", model.name) @@ -95,14 +112,14 @@ def create_emane_model_config(node_id, model, config): return emane_element -def add_configuration(parent, name, value): +def add_configuration(parent: etree.Element, name: str, value: str) -> None: config_element = etree.SubElement(parent, "configuration") add_attribute(config_element, "name", name) add_attribute(config_element, "value", value) class NodeElement: - def __init__(self, session, node, element_name): + def __init__(self, session: "Session", node: NodeBase, element_name: str) -> None: self.session = session self.node = node self.element = etree.Element(element_name) @@ -112,7 +129,7 @@ class NodeElement: add_attribute(self.element, "canvas", node.canvas) self.add_position() - def add_position(self): + def add_position(self) -> None: x = self.node.position.x y = self.node.position.y z = self.node.position.z @@ -129,7 +146,7 @@ class NodeElement: class ServiceElement: - def __init__(self, service): + def __init__(self, service: Type[CoreService]) -> None: self.service = service self.element = etree.Element("service") add_attribute(self.element, "name", service.name) @@ -139,7 +156,7 @@ class ServiceElement: self.add_shutdown() self.add_files() - def add_directories(self): + def add_directories(self) -> None: # get custom directories directories = etree.Element("directories") for directory in self.service.dirs: @@ -149,7 +166,7 @@ class ServiceElement: if directories.getchildren(): self.element.append(directories) - def add_files(self): + def add_files(self) -> None: # get custom files file_elements = etree.Element("files") for file_name in self.service.config_data: @@ -161,7 +178,7 @@ class ServiceElement: if file_elements.getchildren(): self.element.append(file_elements) - def add_startup(self): + def add_startup(self) -> None: # get custom startup startup_elements = etree.Element("startups") for startup in self.service.startup: @@ -171,7 +188,7 @@ class ServiceElement: if startup_elements.getchildren(): self.element.append(startup_elements) - def add_validate(self): + def add_validate(self) -> None: # get custom validate validate_elements = etree.Element("validates") for validate in self.service.validate: @@ -181,7 +198,7 @@ class ServiceElement: if validate_elements.getchildren(): self.element.append(validate_elements) - def add_shutdown(self): + def add_shutdown(self) -> None: # get custom shutdown shutdown_elements = etree.Element("shutdowns") for shutdown in self.service.shutdown: @@ -193,12 +210,12 @@ class ServiceElement: class DeviceElement(NodeElement): - def __init__(self, session, node): + def __init__(self, session: "Session", node: NodeBase) -> None: super().__init__(session, node, "device") add_attribute(self.element, "type", node.type) self.add_services() - def add_services(self): + def add_services(self) -> None: service_elements = etree.Element("services") for service in self.node.services: etree.SubElement(service_elements, "service", name=service.name) @@ -208,7 +225,7 @@ class DeviceElement(NodeElement): class NetworkElement(NodeElement): - def __init__(self, session, node): + def __init__(self, session: "Session", node: NodeBase) -> None: super().__init__(session, node, "network") model = getattr(self.node, "model", None) if model: @@ -221,7 +238,7 @@ class NetworkElement(NodeElement): add_attribute(self.element, "grekey", grekey) self.add_type() - def add_type(self): + def add_type(self) -> None: if self.node.apitype: node_type = NodeTypes(self.node.apitype).name else: @@ -230,14 +247,14 @@ class NetworkElement(NodeElement): class CoreXmlWriter: - def __init__(self, session): + def __init__(self, session: "Session") -> None: self.session = session self.scenario = etree.Element("scenario") self.networks = None self.devices = None self.write_session() - def write_session(self): + def write_session(self) -> None: # generate xml content links = self.write_nodes() self.write_links(links) @@ -250,7 +267,7 @@ class CoreXmlWriter: self.write_session_metadata() self.write_default_services() - def write(self, file_name): + def write(self, file_name: str) -> None: self.scenario.set("name", file_name) # write out generated xml @@ -259,7 +276,7 @@ class CoreXmlWriter: file_name, xml_declaration=True, pretty_print=True, encoding="UTF-8" ) - def write_session_origin(self): + def write_session_origin(self) -> None: # origin: geolocation of cartesian coordinate 0,0,0 lat, lon, alt = self.session.location.refgeo origin = etree.Element("session_origin") @@ -279,7 +296,7 @@ class CoreXmlWriter: add_attribute(origin, "y", y) add_attribute(origin, "z", z) - def write_session_hooks(self): + def write_session_hooks(self) -> None: # hook scripts hooks = etree.Element("session_hooks") for state in sorted(self.session._hooks.keys()): @@ -292,7 +309,7 @@ class CoreXmlWriter: if hooks.getchildren(): self.scenario.append(hooks) - def write_session_options(self): + def write_session_options(self) -> None: option_elements = etree.Element("session_options") options_config = self.session.options.get_configs() if not options_config: @@ -307,7 +324,7 @@ class CoreXmlWriter: if option_elements.getchildren(): self.scenario.append(option_elements) - def write_session_metadata(self): + def write_session_metadata(self) -> None: # metadata metadata_elements = etree.Element("session_metadata") config = self.session.metadata @@ -321,7 +338,7 @@ class CoreXmlWriter: if metadata_elements.getchildren(): self.scenario.append(metadata_elements) - def write_emane_configs(self): + def write_emane_configs(self) -> None: emane_configurations = etree.Element("emane_configurations") for node_id in self.session.emane.nodes(): all_configs = self.session.emane.get_all_configs(node_id) @@ -347,7 +364,7 @@ class CoreXmlWriter: if emane_configurations.getchildren(): self.scenario.append(emane_configurations) - def write_mobility_configs(self): + def write_mobility_configs(self) -> None: mobility_configurations = etree.Element("mobility_configurations") for node_id in self.session.mobility.nodes(): all_configs = self.session.mobility.get_all_configs(node_id) @@ -371,7 +388,7 @@ class CoreXmlWriter: if mobility_configurations.getchildren(): self.scenario.append(mobility_configurations) - def write_service_configs(self): + def write_service_configs(self) -> None: service_configurations = etree.Element("service_configurations") service_configs = self.session.services.all_configs() for node_id, service in service_configs: @@ -382,7 +399,7 @@ class CoreXmlWriter: if service_configurations.getchildren(): self.scenario.append(service_configurations) - def write_default_services(self): + def write_default_services(self) -> None: node_types = etree.Element("default_services") for node_type in self.session.services.default_services: services = self.session.services.default_services[node_type] @@ -393,7 +410,7 @@ class CoreXmlWriter: if node_types.getchildren(): self.scenario.append(node_types) - def write_nodes(self): + def write_nodes(self) -> List[LinkData]: self.networks = etree.SubElement(self.scenario, "networks") self.devices = etree.SubElement(self.scenario, "devices") @@ -416,7 +433,7 @@ class CoreXmlWriter: return links - def write_network(self, node): + def write_network(self, node: NodeBase) -> None: # ignore p2p and other nodes that are not part of the api if not node.apitype: return @@ -424,7 +441,7 @@ class CoreXmlWriter: network = NetworkElement(self.session, node) self.networks.append(network.element) - def write_links(self, links): + def write_links(self, links: List[LinkData]) -> None: link_elements = etree.Element("links") # add link data for link_data in links: @@ -438,13 +455,21 @@ class CoreXmlWriter: if link_elements.getchildren(): self.scenario.append(link_elements) - def write_device(self, node): + def write_device(self, node: NodeBase) -> None: device = DeviceElement(self.session, node) self.devices.append(device.element) def create_interface_element( - self, element_name, node_id, interface_id, mac, ip4, ip4_mask, ip6, ip6_mask - ): + self, + element_name: str, + node_id: int, + interface_id: int, + mac: str, + ip4: str, + ip4_mask: int, + ip6: str, + ip6_mask: int, + ) -> etree.Element: interface = etree.Element(element_name) node = self.session.get_node(node_id) interface_name = None @@ -467,7 +492,7 @@ class CoreXmlWriter: return interface - def create_link_element(self, link_data): + def create_link_element(self, link_data: LinkData) -> etree.Element: link_element = etree.Element("link") add_attribute(link_element, "node_one", link_data.node1_id) add_attribute(link_element, "node_two", link_data.node2_id) @@ -525,11 +550,11 @@ class CoreXmlWriter: class CoreXmlReader: - def __init__(self, session): + def __init__(self, session: "Session") -> None: self.session = session self.scenario = None - def read(self, file_name): + def read(self, file_name: str) -> None: xml_tree = etree.parse(file_name) self.scenario = xml_tree.getroot() @@ -545,7 +570,7 @@ class CoreXmlReader: self.read_nodes() self.read_links() - def read_default_services(self): + def read_default_services(self) -> None: default_services = self.scenario.find("default_services") if default_services is None: return @@ -560,7 +585,7 @@ class CoreXmlReader: ) self.session.services.default_services[node_type] = services - def read_session_metadata(self): + def read_session_metadata(self) -> None: session_metadata = self.scenario.find("session_metadata") if session_metadata is None: return @@ -573,7 +598,7 @@ class CoreXmlReader: logging.info("reading session metadata: %s", configs) self.session.metadata = configs - def read_session_options(self): + def read_session_options(self) -> None: session_options = self.scenario.find("session_options") if session_options is None: return @@ -586,7 +611,7 @@ class CoreXmlReader: logging.info("reading session options: %s", configs) self.session.options.set_configs(configs) - def read_session_hooks(self): + def read_session_hooks(self) -> None: session_hooks = self.scenario.find("session_hooks") if session_hooks is None: return @@ -601,7 +626,7 @@ class CoreXmlReader: hook_type, file_name=name, source_name=None, data=data ) - def read_session_origin(self): + def read_session_origin(self) -> None: session_origin = self.scenario.find("session_origin") if session_origin is None: return @@ -625,7 +650,7 @@ class CoreXmlReader: logging.info("reading session reference xyz: %s, %s, %s", x, y, z) self.session.location.refxyz = (x, y, z) - def read_service_configs(self): + def read_service_configs(self) -> None: service_configurations = self.scenario.find("service_configurations") if service_configurations is None: return @@ -669,7 +694,7 @@ class CoreXmlReader: files.add(name) service.configs = tuple(files) - def read_emane_configs(self): + def read_emane_configs(self) -> None: emane_configurations = self.scenario.find("emane_configurations") if emane_configurations is None: return @@ -702,7 +727,7 @@ class CoreXmlReader: ) self.session.emane.set_model_config(node_id, model_name, configs) - def read_mobility_configs(self): + def read_mobility_configs(self) -> None: mobility_configurations = self.scenario.find("mobility_configurations") if mobility_configurations is None: return @@ -722,7 +747,7 @@ class CoreXmlReader: ) self.session.mobility.set_model_config(node_id, model_name, configs) - def read_nodes(self): + def read_nodes(self) -> None: device_elements = self.scenario.find("devices") if device_elements is not None: for device_element in device_elements.iterchildren(): @@ -733,7 +758,7 @@ class CoreXmlReader: for network_element in network_elements.iterchildren(): self.read_network(network_element) - def read_device(self, device_element): + def read_device(self, device_element: etree.Element) -> None: node_id = get_int(device_element, "id") name = device_element.get("name") model = device_element.get("type") @@ -759,7 +784,7 @@ class CoreXmlReader: logging.info("reading node id(%s) model(%s) name(%s)", node_id, model, name) self.session.add_node(_id=node_id, options=options) - def read_network(self, network_element): + def read_network(self, network_element: etree.Element) -> None: node_id = get_int(network_element, "id") name = network_element.get("name") node_type = NodeTypes[network_element.get("type")] @@ -783,7 +808,7 @@ class CoreXmlReader: ) self.session.add_node(_type=node_type, _id=node_id, options=options) - def read_links(self): + def read_links(self) -> None: link_elements = self.scenario.find("links") if link_elements is None: return diff --git a/daemon/core/xml/corexmldeployment.py b/daemon/core/xml/corexmldeployment.py index 5c817fc2..5f340b69 100644 --- a/daemon/core/xml/corexmldeployment.py +++ b/daemon/core/xml/corexmldeployment.py @@ -1,5 +1,6 @@ import os import socket +from typing import TYPE_CHECKING, List, Tuple import netaddr from lxml import etree @@ -7,26 +8,40 @@ from lxml import etree from core import utils from core.constants import IP_BIN from core.emane.nodes import EmaneNet -from core.nodes.base import CoreNodeBase +from core.nodes.base import CoreNodeBase, NodeBase +from core.nodes.interface import CoreInterface + +if TYPE_CHECKING: + from core.emulator.session import Session -def add_type(parent_element, name): +def add_type(parent_element: etree.Element, name: str) -> None: type_element = etree.SubElement(parent_element, "type") type_element.text = name -def add_address(parent_element, address_type, address, interface_name=None): +def add_address( + parent_element: etree.Element, + address_type: str, + address: str, + interface_name: str = None, +) -> None: address_element = etree.SubElement(parent_element, "address", type=address_type) address_element.text = address if interface_name is not None: address_element.set("iface", interface_name) -def add_mapping(parent_element, maptype, mapref): +def add_mapping(parent_element: etree.Element, maptype: str, mapref: str) -> None: etree.SubElement(parent_element, "mapping", type=maptype, ref=mapref) -def add_emane_interface(host_element, netif, platform_name="p1", transport_name="t1"): +def add_emane_interface( + host_element: etree.Element, + netif: CoreInterface, + platform_name: str = "p1", + transport_name: str = "t1", +) -> etree.Element: nem_id = netif.net.nemidmap[netif] host_id = host_element.get("id") @@ -54,7 +69,7 @@ def add_emane_interface(host_element, netif, platform_name="p1", transport_name= return platform_element -def get_address_type(address): +def get_address_type(address: str) -> str: addr, _slash, _prefixlen = address.partition("/") if netaddr.valid_ipv4(addr): address_type = "IPv4" @@ -65,7 +80,7 @@ def get_address_type(address): return address_type -def get_ipv4_addresses(hostname): +def get_ipv4_addresses(hostname: str) -> List[Tuple[str, str]]: if hostname == "localhost": addresses = [] args = f"{IP_BIN} -o -f inet address show" @@ -85,7 +100,7 @@ def get_ipv4_addresses(hostname): class CoreXmlDeployment: - def __init__(self, session, scenario): + def __init__(self, session: "Session", scenario: etree.Element) -> None: self.session = session self.scenario = scenario self.root = etree.SubElement( @@ -93,17 +108,17 @@ class CoreXmlDeployment: ) self.add_deployment() - def find_device(self, name): + def find_device(self, name: str) -> etree.Element: device = self.scenario.find(f"devices/device[@name='{name}']") return device - def find_interface(self, device, name): + def find_interface(self, device: NodeBase, name: str) -> etree.Element: interface = self.scenario.find( f"devices/device[@name='{device.name}']/interfaces/interface[@name='{name}']" ) return interface - def add_deployment(self): + def add_deployment(self) -> None: physical_host = self.add_physical_host(socket.gethostname()) for node_id in self.session.nodes: @@ -111,7 +126,7 @@ class CoreXmlDeployment: if isinstance(node, CoreNodeBase): self.add_virtual_host(physical_host, node) - def add_physical_host(self, name): + def add_physical_host(self, name: str) -> etree.Element: # add host root_id = self.root.get("id") host_id = f"{root_id}/{name}" @@ -126,7 +141,7 @@ class CoreXmlDeployment: return host_element - def add_virtual_host(self, physical_host, node): + def add_virtual_host(self, physical_host: etree.Element, node: NodeBase) -> None: if not isinstance(node, CoreNodeBase): raise TypeError(f"invalid node type: {node}") diff --git a/daemon/core/xml/emanexml.py b/daemon/core/xml/emanexml.py index a62b54e5..da1e089e 100644 --- a/daemon/core/xml/emanexml.py +++ b/daemon/core/xml/emanexml.py @@ -1,16 +1,26 @@ import logging import os from tempfile import NamedTemporaryFile +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple from lxml import etree from core import utils +from core.config import Configuration +from core.emane.nodes import EmaneNet +from core.emulator.distributed import DistributedServer +from core.nodes.interface import CoreInterface +from core.nodes.network import CtrlNet from core.xml import corexml +if TYPE_CHECKING: + from core.emane.emanemanager import EmaneManager + from core.emane.emanemodel import EmaneModel + _hwaddr_prefix = "02:02" -def is_external(config): +def is_external(config: Dict[str, str]) -> bool: """ Checks if the configuration is for an external transport. @@ -21,7 +31,7 @@ def is_external(config): return config.get("external") == "1" -def _value_to_params(value): +def _value_to_params(value: str) -> Optional[Tuple[str]]: """ Helper to convert a parameter to a parameter tuple. @@ -44,7 +54,12 @@ def _value_to_params(value): return None -def create_file(xml_element, doc_name, file_path, server=None): +def create_file( + xml_element: etree.Element, + doc_name: str, + file_path: str, + server: DistributedServer = None, +) -> None: """ Create xml file. @@ -68,7 +83,7 @@ def create_file(xml_element, doc_name, file_path, server=None): corexml.write_xml_file(xml_element, file_path, doctype=doctype) -def add_param(xml_element, name, value): +def add_param(xml_element: etree.Element, name: str, value: str) -> None: """ Add emane configuration parameter to xml element. @@ -80,7 +95,12 @@ def add_param(xml_element, name, value): etree.SubElement(xml_element, "param", name=name, value=value) -def add_configurations(xml_element, configurations, config, config_ignore): +def add_configurations( + xml_element: etree.Element, + configurations: List[Configuration], + config: Dict[str, str], + config_ignore: Set, +) -> None: """ Add emane model configurations to xml element. @@ -107,7 +127,13 @@ def add_configurations(xml_element, configurations, config, config_ignore): add_param(xml_element, name, value) -def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_xmls): +def build_node_platform_xml( + emane_manager: "EmaneManager", + control_net: CtrlNet, + node: EmaneNet, + nem_id: int, + platform_xmls: Dict[str, etree.Element], +) -> int: """ Create platform xml for a specific node. @@ -131,7 +157,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x if node.model is None: logging.warning("warning: EMANE network %s has no associated model", node.name) - return nem_entries + return nem_id for netif in node.netifs(): logging.debug( @@ -228,7 +254,7 @@ def build_node_platform_xml(emane_manager, control_net, node, nem_id, platform_x return nem_id -def build_xml_files(emane_manager, node): +def build_xml_files(emane_manager: "EmaneManager", node: EmaneNet) -> None: """ Generate emane xml files required for node. @@ -276,7 +302,9 @@ def build_xml_files(emane_manager, node): build_transport_xml(emane_manager, node, rtype) -def build_transport_xml(emane_manager, node, transport_type): +def build_transport_xml( + emane_manager: "EmaneManager", node: EmaneNet, transport_type: str +) -> None: """ Build transport xml file for node and transport type. @@ -317,7 +345,12 @@ def build_transport_xml(emane_manager, node, transport_type): ) -def create_phy_xml(emane_model, config, file_path, server): +def create_phy_xml( + emane_model: "EmaneModel", + config: Dict[str, str], + file_path: str, + server: DistributedServer, +) -> None: """ Create the phy xml document. @@ -345,7 +378,12 @@ def create_phy_xml(emane_model, config, file_path, server): ) -def create_mac_xml(emane_model, config, file_path, server): +def create_mac_xml( + emane_model: "EmaneModel", + config: Dict[str, str], + file_path: str, + server: DistributedServer, +) -> None: """ Create the mac xml document. @@ -376,14 +414,14 @@ def create_mac_xml(emane_model, config, file_path, server): def create_nem_xml( - emane_model, - config, - nem_file, - transport_definition, - mac_definition, - phy_definition, - server, -): + emane_model: "EmaneModel", + config: Dict[str, str], + nem_file: str, + transport_definition: str, + mac_definition: str, + phy_definition: str, + server: DistributedServer, +) -> None: """ Create the nem xml document. @@ -413,7 +451,13 @@ def create_nem_xml( ) -def create_event_service_xml(group, port, device, file_directory, server=None): +def create_event_service_xml( + group: str, + port: str, + device: str, + file_directory: str, + server: DistributedServer = None, +) -> None: """ Create a emane event service xml file. @@ -440,7 +484,7 @@ def create_event_service_xml(group, port, device, file_directory, server=None): create_file(event_element, "emaneeventmsgsvc", file_path, server) -def transport_file_name(node_id, transport_type): +def transport_file_name(node_id: int, transport_type: str) -> str: """ Create name for a transport xml file. @@ -451,10 +495,11 @@ def transport_file_name(node_id, transport_type): return f"n{node_id}trans{transport_type}.xml" -def _basename(emane_model, interface=None): +def _basename(emane_model: "EmaneModel", interface: CoreInterface = None) -> str: """ Create name that is leveraged for configuration file creation. + :param emane_model: emane model to create name for :param interface: interface for this model :return: basename used for file creation :rtype: str @@ -469,7 +514,7 @@ def _basename(emane_model, interface=None): return f"{name}{emane_model.name}" -def nem_file_name(emane_model, interface=None): +def nem_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str: """ Return the string name for the NEM XML file, e.g. "n3rfpipenem.xml" @@ -485,7 +530,7 @@ def nem_file_name(emane_model, interface=None): return f"{basename}nem{append}.xml" -def shim_file_name(emane_model, interface=None): +def shim_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str: """ Return the string name for the SHIM XML file, e.g. "commeffectshim.xml" @@ -498,7 +543,7 @@ def shim_file_name(emane_model, interface=None): return f"{name}shim.xml" -def mac_file_name(emane_model, interface=None): +def mac_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str: """ Return the string name for the MAC XML file, e.g. "n3rfpipemac.xml" @@ -511,7 +556,7 @@ def mac_file_name(emane_model, interface=None): return f"{name}mac.xml" -def phy_file_name(emane_model, interface=None): +def phy_file_name(emane_model: "EmaneModel", interface: CoreInterface = None) -> str: """ Return the string name for the PHY XML file, e.g. "n3rfpipephy.xml"