From 02156867e272075c59f446a0e2d0f1027da5232c Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Mon, 13 Jan 2020 22:15:44 -0800 Subject: [PATCH] added type hinting to location module funcs --- daemon/core/emulator/session.py | 6 +- daemon/core/location/corelocation.py | 23 ++-- daemon/core/location/event.py | 31 +++--- daemon/core/location/mobility.py | 153 +++++++++++++++------------ 4 files changed, 119 insertions(+), 94 deletions(-) diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index 36fd76f2..f819c4c3 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -12,7 +12,7 @@ import subprocess import tempfile import threading import time -from typing import Callable, Dict, Iterable, List, Optional, Tuple, Type +from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Type from core import constants, utils from core.emane.emanemanager import EmaneManager @@ -1342,9 +1342,7 @@ class Session: break return node_id - def create_node( - self, cls: Type[NodeBase], *args: Iterable, **kwargs: Dict - ) -> NodeBase: + def create_node(self, cls: Type[NodeBase], *args: Any, **kwargs: Any) -> NodeBase: """ Create an emulation node. diff --git a/daemon/core/location/corelocation.py b/daemon/core/location/corelocation.py index aeab8896..fc803fac 100644 --- a/daemon/core/location/corelocation.py +++ b/daemon/core/location/corelocation.py @@ -6,6 +6,7 @@ https://pypi.python.org/pypi/utm (version 0.3.0). """ import logging +from typing import Optional, Tuple from core.emulator.enumerations import RegisterTlvs from core.location import utm @@ -21,7 +22,7 @@ class CoreLocation: name = "location" config_type = RegisterTlvs.UTILITY.value - def __init__(self): + def __init__(self) -> None: """ Creates a MobilityManager instance. @@ -37,7 +38,7 @@ class CoreLocation: for n, l in utm.ZONE_LETTERS: self.zonemap[l] = n - def reset(self): + def reset(self) -> None: """ Reset to initial state. """ @@ -50,7 +51,7 @@ class CoreLocation: # cached distance to refpt in other zones self.zoneshifts = {} - def px2m(self, val): + def px2m(self, val: float) -> float: """ Convert the specified value in pixels to meters using the configured scale. The scale is given as s, where @@ -61,7 +62,7 @@ class CoreLocation: """ return (val / 100.0) * self.refscale - def m2px(self, val): + def m2px(self, val: float) -> float: """ Convert the specified value in meters to pixels using the configured scale. The scale is given as s, where @@ -74,7 +75,7 @@ class CoreLocation: return 0.0 return 100.0 * (val / self.refscale) - def setrefgeo(self, lat, lon, alt): + def setrefgeo(self, lat: float, lon: float, alt: float) -> None: """ Record the geographical reference point decimal (lat, lon, alt) and convert and store its UTM equivalent for later use. @@ -89,7 +90,7 @@ class CoreLocation: e, n, zonen, zonel = utm.from_latlon(lat, lon) self.refutm = ((zonen, zonel), e, n, alt) - def getgeo(self, x, y, z): + def getgeo(self, x: float, y: float, z: float) -> Tuple[float, float, float]: """ Given (x, y, z) Cartesian coordinates, convert them to latitude, longitude, and altitude based on the configured reference point @@ -130,7 +131,7 @@ class CoreLocation: lat, lon = self.refgeo[:2] return lat, lon, alt - def getxyz(self, lat, lon, alt): + def getxyz(self, lat: float, lon: float, alt: float) -> Tuple[float, float, float]: """ Given latitude, longitude, and altitude location data, convert them to (x, y, z) Cartesian coordinates based on the configured @@ -165,7 +166,7 @@ class CoreLocation: z = self.m2px(zm) + self.refxyz[2] return x, y, z - def geteastingshift(self, zonen, zonel): + def geteastingshift(self, zonen: float, zonel: float) -> Optional[float]: """ If the lat, lon coordinates being converted are located in a different UTM zone than the canvas reference point, the UTM meters @@ -201,7 +202,7 @@ class CoreLocation: self.zoneshifts[z] = (xshift, yshift) return xshift - def getnorthingshift(self, zonen, zonel): + def getnorthingshift(self, zonen: float, zonel: float) -> Optional[float]: """ If the lat, lon coordinates being converted are located in a different UTM zone than the canvas reference point, the UTM meters @@ -238,7 +239,9 @@ class CoreLocation: self.zoneshifts[z] = (xshift, yshift) return yshift - def getutmzoneshift(self, e, n): + def getutmzoneshift( + self, e: float, n: float + ) -> Tuple[float, float, Tuple[float, str]]: """ Given UTM easting and northing values, check if they fall outside the reference point's zone boundary. Return the UTM coordinates in a diff --git a/daemon/core/location/event.py b/daemon/core/location/event.py index f930d9b7..d553e4ee 100644 --- a/daemon/core/location/event.py +++ b/daemon/core/location/event.py @@ -6,6 +6,7 @@ import heapq import threading import time from functools import total_ordering +from typing import Any, Callable class Timer(threading.Thread): @@ -14,7 +15,9 @@ class Timer(threading.Thread): already running. """ - def __init__(self, interval, function, args=None, kwargs=None): + def __init__( + self, interval: float, function: Callable, args: Any = None, kwargs: Any = None + ) -> None: """ Create a Timer instance. @@ -42,7 +45,7 @@ class Timer(threading.Thread): else: self.kwargs = {} - def cancel(self): + def cancel(self) -> bool: """ Stop the timer if it hasn't finished yet. Return False if the timer was already running. @@ -56,7 +59,7 @@ class Timer(threading.Thread): self._running.release() return locked - def run(self): + def run(self) -> None: """ Run the timer. @@ -75,7 +78,9 @@ class Event: Provides event objects that can be used within the EventLoop class. """ - def __init__(self, eventnum, event_time, func, *args, **kwds): + def __init__( + self, eventnum: int, event_time: float, func: Callable, *args: Any, **kwds: Any + ) -> None: """ Create an Event instance. @@ -92,13 +97,13 @@ class Event: self.kwds = kwds self.canceled = False - def __lt__(self, other): + def __lt__(self, other: "Event") -> bool: result = self.time < other.time if result: result = self.eventnum < other.eventnum return result - def run(self): + def run(self) -> None: """ Run an event. @@ -108,7 +113,7 @@ class Event: return self.func(*self.args, **self.kwds) - def cancel(self): + def cancel(self) -> None: """ Cancel event. @@ -123,7 +128,7 @@ class EventLoop: Provides an event loop for running events. """ - def __init__(self): + def __init__(self) -> None: """ Creates a EventLoop instance. """ @@ -134,7 +139,7 @@ class EventLoop: self.running = False self.start = None - def __run_events(self): + def __run_events(self) -> None: """ Run events. @@ -159,7 +164,7 @@ class EventLoop: if schedule: self.__schedule_event() - def __schedule_event(self): + def __schedule_event(self) -> None: """ Schedule event. @@ -177,7 +182,7 @@ class EventLoop: self.timer.daemon = True self.timer.start() - def run(self): + def run(self) -> None: """ Start event loop. @@ -192,7 +197,7 @@ class EventLoop: event.time += self.start self.__schedule_event() - def stop(self): + def stop(self) -> None: """ Stop event loop. @@ -209,7 +214,7 @@ class EventLoop: self.running = False self.start = None - def add_event(self, delaysec, func, *args, **kwds): + def add_event(self, delaysec: float, func: Callable, *args: Any, **kwds: Any): """ Add an event to the event loop. diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 41678d43..e1917636 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -9,6 +9,7 @@ import os import threading import time from functools import total_ordering +from typing import TYPE_CHECKING, Dict, List, Tuple from core import utils from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager @@ -21,6 +22,11 @@ from core.emulator.enumerations import ( RegisterTlvs, ) from core.errors import CoreError +from core.nodes.base import CoreNode, NodeBase +from core.nodes.interface import CoreInterface + +if TYPE_CHECKING: + from core.emulator.session import Session class MobilityManager(ModelManager): @@ -32,7 +38,7 @@ class MobilityManager(ModelManager): name = "MobilityManager" config_type = RegisterTlvs.WIRELESS.value - def __init__(self, session): + def __init__(self, session: "Session") -> None: """ Creates a MobilityManager instance. @@ -43,7 +49,7 @@ class MobilityManager(ModelManager): self.models[BasicRangeModel.name] = BasicRangeModel self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility - def reset(self): + def reset(self) -> None: """ Clear out all current configurations. @@ -51,7 +57,7 @@ class MobilityManager(ModelManager): """ self.config_reset() - def startup(self, node_ids=None): + def startup(self, node_ids: List[int] = None) -> None: """ Session is transitioning from instantiation to runtime state. Instantiate any mobility models that have been configured for a WLAN. @@ -86,7 +92,7 @@ class MobilityManager(ModelManager): if node.mobility: self.session.event_loop.add_event(0.0, node.mobility.startup) - def handleevent(self, event_data): + def handleevent(self, event_data: EventData) -> None: """ Handle an Event Message used to start, stop, or pause mobility scripts for a given WlanNode. @@ -149,7 +155,7 @@ class MobilityManager(ModelManager): if event_type == EventTypes.PAUSE.value: model.pause() - def sendevent(self, model): + def sendevent(self, model: "WayPointMobility") -> None: """ Send an event message on behalf of a mobility model. This communicates the current and end (max) times to the GUI. @@ -179,7 +185,9 @@ class MobilityManager(ModelManager): self.session.broadcast_event(event_data) - def updatewlans(self, moved, moved_netifs): + def updatewlans( + self, moved: List[NodeBase], moved_netifs: List[CoreInterface] + ) -> None: """ A mobility script has caused nodes in the 'moved' list to move. Update every WlanNode. This saves range calculations if the model @@ -208,7 +216,7 @@ class WirelessModel(ConfigurableOptions): bitmap = None position_callback = None - def __init__(self, session, _id): + def __init__(self, session: "Session", _id: int): """ Create a WirelessModel instance. @@ -218,7 +226,7 @@ class WirelessModel(ConfigurableOptions): self.session = session self.id = _id - def all_link_data(self, flags): + def all_link_data(self, flags: int) -> List: """ May be used if the model can populate the GUI with wireless (green) link lines. @@ -229,7 +237,7 @@ class WirelessModel(ConfigurableOptions): """ return [] - def update(self, moved, moved_netifs): + def update(self, moved: bool, moved_netifs: List[CoreInterface]) -> None: """ Update this wireless model. @@ -239,10 +247,10 @@ class WirelessModel(ConfigurableOptions): """ raise NotImplementedError - def update_config(self, config): + def update_config(self, config: Dict[str, str]) -> None: """ - For run-time updates of model config. Returns True when position callback and set link - parameters should be invoked. + For run-time updates of model config. Returns True when position callback and + set link parameters should be invoked. :param dict config: configuration values to update :return: nothing @@ -295,7 +303,7 @@ class BasicRangeModel(WirelessModel): def config_groups(cls): return [ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))] - def __init__(self, session, _id): + def __init__(self, session: "Session", _id: int) -> None: """ Create a BasicRangeModel instance. @@ -314,7 +322,7 @@ class BasicRangeModel(WirelessModel): self.loss = None self.jitter = None - def values_from_config(self, config): + def values_from_config(self, config: Dict[str, str]) -> None: """ Values to convert to link parameters. @@ -340,7 +348,7 @@ class BasicRangeModel(WirelessModel): if self.jitter == 0: self.jitter = None - def setlinkparams(self): + def setlinkparams(self) -> None: """ Apply link parameters to all interfaces. This is invoked from WlanNode.setmodel() after the position callback has been set. @@ -356,7 +364,7 @@ class BasicRangeModel(WirelessModel): jitter=self.jitter, ) - def get_position(self, netif): + def get_position(self, netif: CoreInterface) -> Tuple[float, float, float]: """ Retrieve network interface position. @@ -366,7 +374,9 @@ class BasicRangeModel(WirelessModel): with self._netifslock: return self._netifs[netif] - def set_position(self, netif, x=None, y=None, z=None): + def set_position( + self, netif: CoreInterface, x: float = None, y: float = None, z: float = None + ) -> None: """ A node has moved; given an interface, a new (x,y,z) position has been set; calculate the new distance between other nodes and link or @@ -389,7 +399,7 @@ class BasicRangeModel(WirelessModel): position_callback = set_position - def update(self, moved, moved_netifs): + def update(self, moved: bool, moved_netifs: List[CoreInterface]) -> None: """ Node positions have changed without recalc. Update positions from node.position, then re-calculate links for those that have moved. @@ -411,7 +421,7 @@ class BasicRangeModel(WirelessModel): continue self.calclink(netif, netif2) - def calclink(self, netif, netif2): + def calclink(self, netif: CoreInterface, netif2: CoreInterface) -> None: """ Helper used by set_position() and update() to calculate distance between two interfaces and perform @@ -455,7 +465,9 @@ class BasicRangeModel(WirelessModel): logging.exception("error getting interfaces during calclinkS") @staticmethod - def calcdistance(p1, p2): + def calcdistance( + p1: Tuple[float, float, float], p2: Tuple[float, float, float] + ) -> float: """ Calculate the distance between two three-dimensional points. @@ -471,7 +483,7 @@ class BasicRangeModel(WirelessModel): c = p1[2] - p2[2] return math.hypot(math.hypot(a, b), c) - def update_config(self, config): + def update_config(self, config: Dict[str, str]) -> None: """ Configuration has changed during runtime. @@ -482,12 +494,14 @@ class BasicRangeModel(WirelessModel): self.setlinkparams() return True - def create_link_data(self, interface1, interface2, message_type): + def create_link_data( + self, interface1: CoreInterface, interface2: CoreInterface, message_type: int + ) -> LinkData: """ Create a wireless link/unlink data message. - :param core.coreobj.PyCoreNetIf interface1: interface one - :param core.coreobj.PyCoreNetIf interface2: interface two + :param core.nodes.interface.CoreInterface interface1: interface one + :param core.nodes.interface.CoreInterface interface2: interface two :param message_type: link message type :return: link data :rtype: LinkData @@ -500,7 +514,9 @@ class BasicRangeModel(WirelessModel): link_type=LinkTypes.WIRELESS.value, ) - def sendlinkmsg(self, netif, netif2, unlink=False): + def sendlinkmsg( + self, netif: CoreInterface, netif2: CoreInterface, unlink: bool = False + ) -> None: """ Send a wireless link/unlink API message to the GUI. @@ -517,7 +533,7 @@ class BasicRangeModel(WirelessModel): link_data = self.create_link_data(netif, netif2, message_type) self.session.broadcast_link(link_data) - def all_link_data(self, flags): + def all_link_data(self, flags: int) -> List[LinkData]: """ Return a list of wireless link messages for when the GUI reconnects. @@ -540,7 +556,7 @@ class WayPoint: Maintains information regarding waypoints. """ - def __init__(self, time, nodenum, coords, speed): + def __init__(self, time: float, nodenum: int, coords, speed: float): """ Creates a WayPoint instance. @@ -554,13 +570,13 @@ class WayPoint: self.coords = coords self.speed = speed - def __eq__(self, other): - return (self.time, self.nodenum) == (other.time, other.nodedum) + def __eq__(self, other: "WayPoint") -> bool: + return (self.time, self.nodenum) == (other.time, other.nodenum) - def __ne__(self, other): + def __ne__(self, other: "WayPoint") -> bool: return not self == other - def __lt__(self, other): + def __lt__(self, other: "WayPoint") -> bool: result = self.time < other.time if result: result = self.nodenum < other.nodenum @@ -579,7 +595,7 @@ class WayPointMobility(WirelessModel): STATE_RUNNING = 1 STATE_PAUSED = 2 - def __init__(self, session, _id): + def __init__(self, session: "Session", _id: int) -> None: """ Create a WayPointMobility instance. @@ -603,7 +619,7 @@ class WayPointMobility(WirelessModel): # (ns-3 sets this to False as new waypoints may be added from trace) self.empty_queue_stop = True - def runround(self): + def runround(self) -> None: """ Advance script time and move nodes. @@ -657,7 +673,7 @@ class WayPointMobility(WirelessModel): # TODO: check session state self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround) - def run(self): + def run(self) -> None: """ Run the waypoint mobility scenario. @@ -670,7 +686,7 @@ class WayPointMobility(WirelessModel): self.runround() self.session.mobility.sendevent(self) - def movenode(self, node, dt): + def movenode(self, node: CoreNode, dt: float) -> bool: """ Calculate next node location and update its coordinates. Returns True if the node's position has changed. @@ -723,7 +739,7 @@ class WayPointMobility(WirelessModel): self.setnodeposition(node, x1 + dx, y1 + dy, z1) return True - def movenodesinitial(self): + def movenodesinitial(self) -> None: """ Move nodes to their initial positions. Then calculate the ranges. @@ -741,11 +757,13 @@ class WayPointMobility(WirelessModel): moved_netifs.append(netif) self.session.mobility.updatewlans(moved, moved_netifs) - def addwaypoint(self, time, nodenum, x, y, z, speed): + def addwaypoint( + self, _time: float, nodenum: int, x: float, y: float, z: float, speed: float + ) -> None: """ Waypoints are pushed to a heapq, sorted by time. - :param time: waypoint time + :param _time: waypoint time :param int nodenum: node id :param x: x position :param y: y position @@ -753,10 +771,10 @@ class WayPointMobility(WirelessModel): :param speed: speed :return: nothing """ - wp = WayPoint(time, nodenum, coords=(x, y, z), speed=speed) + wp = WayPoint(_time, nodenum, coords=(x, y, z), speed=speed) heapq.heappush(self.queue, wp) - def addinitial(self, nodenum, x, y, z): + def addinitial(self, nodenum: int, x: float, y: float, z: float) -> None: """ Record initial position in a dict. @@ -769,11 +787,11 @@ class WayPointMobility(WirelessModel): wp = WayPoint(0, nodenum, coords=(x, y, z), speed=0) self.initial[nodenum] = wp - def updatepoints(self, now): + def updatepoints(self, now: float) -> None: """ Move items from self.queue to self.points when their time has come. - :param int now: current timestamp + :param float now: current timestamp :return: nothing """ while len(self.queue): @@ -782,7 +800,7 @@ class WayPointMobility(WirelessModel): wp = heapq.heappop(self.queue) self.points[wp.nodenum] = wp - def copywaypoints(self): + def copywaypoints(self) -> None: """ Store backup copy of waypoints for looping and stopping. @@ -790,7 +808,7 @@ class WayPointMobility(WirelessModel): """ self.queue_copy = list(self.queue) - def loopwaypoints(self): + def loopwaypoints(self) -> None: """ Restore backup copy of waypoints when looping. @@ -799,13 +817,13 @@ class WayPointMobility(WirelessModel): self.queue = list(self.queue_copy) return self.loop - def setnodeposition(self, node, x, y, z): + def setnodeposition(self, node: CoreNode, x: float, y: float, z: float) -> None: """ Helper to move a node, notify any GUI (connected session handlers), without invoking the interface poshook callback that may perform range calculation. - :param core.netns.vnode.CoreNode node: node to set position for + :param core.nodes.base.CoreNode node: node to set position for :param x: x position :param y: y position :param z: z position @@ -815,7 +833,7 @@ class WayPointMobility(WirelessModel): node_data = node.data(message_type=0) self.session.broadcast_node(node_data) - def setendtime(self): + def setendtime(self) -> None: """ Set self.endtime to the time of the last waypoint in the queue of waypoints. This is just an estimate. The endtime will later be @@ -829,7 +847,7 @@ class WayPointMobility(WirelessModel): except IndexError: self.endtime = 0 - def start(self): + def start(self) -> None: """ Run the script from the beginning or unpause from where it was before. @@ -849,11 +867,12 @@ class WayPointMobility(WirelessModel): self.lasttime = now - (0.001 * self.refresh_ms) self.runround() - def stop(self, move_initial=True): + def stop(self, move_initial: bool = True) -> None: """ Stop the script and move nodes to initial positions. - :param bool move_initial: flag to check if we should move nodes to initial position + :param bool move_initial: flag to check if we should move nodes to initial + position :return: nothing """ self.state = self.STATE_STOPPED @@ -864,7 +883,7 @@ class WayPointMobility(WirelessModel): self.movenodesinitial() self.session.mobility.sendevent(self) - def pause(self): + def pause(self) -> None: """ Pause the script; pause time is stored to self.lasttime. @@ -926,12 +945,12 @@ class Ns2ScriptedMobility(WayPointMobility): ] @classmethod - def config_groups(cls): + def config_groups(cls) -> List[ConfigGroup]: return [ ConfigGroup("ns-2 Mobility Script Parameters", 1, len(cls.configurations())) ] - def __init__(self, session, _id): + def __init__(self, session: "Session", _id: int): """ Creates a Ns2ScriptedMobility instance. @@ -951,7 +970,7 @@ class Ns2ScriptedMobility(WayPointMobility): self.script_pause = None self.script_stop = None - def update_config(self, config): + def update_config(self, config: Dict[str, str]) -> None: self.file = config["file"] logging.info( "ns-2 scripted mobility configured for WLAN %d using file: %s", @@ -969,7 +988,7 @@ class Ns2ScriptedMobility(WayPointMobility): self.copywaypoints() self.setendtime() - def readscriptfile(self): + def readscriptfile(self) -> None: """ Read in mobility script from a file. This adds waypoints to a priority queue, sorted by waypoint time. Initial waypoints are @@ -1012,7 +1031,6 @@ class Ns2ScriptedMobility(WayPointMobility): # initial position (time=0, speed=0): # $node_(6) set X_ 780.0 parts = line.split() - time = 0.0 nodenum = parts[0][1 + parts[0].index("(") : parts[0].index(")")] if parts[2] == "X_": if ix is not None and iy is not None: @@ -1036,7 +1054,7 @@ class Ns2ScriptedMobility(WayPointMobility): if ix is not None and iy is not None: self.addinitial(self.map(inodenum), ix, iy, iz) - def findfile(self, file_name): + def findfile(self, file_name: str) -> str: """ Locate a script file. If the specified file doesn't exist, look in the same directory as the scenario file, or in the default @@ -1065,7 +1083,7 @@ class Ns2ScriptedMobility(WayPointMobility): return file_name - def parsemap(self, mapstr): + def parsemap(self, mapstr: str) -> None: """ Parse a node mapping string, given as a configuration parameter. @@ -1085,18 +1103,18 @@ class Ns2ScriptedMobility(WayPointMobility): except ValueError: logging.exception("ns-2 mobility node map error") - def map(self, nodenum): + def map(self, nodenum: str) -> int: """ Map one node number (from a script file) to another. - :param str nodenum: node id to map + :param int nodenum: node id to map :return: mapped value or the node id itself :rtype: int """ nodenum = int(nodenum) return self.nodemap.get(nodenum, nodenum) - def startup(self): + def startup(self) -> None: """ Start running the script if autostart is enabled. Move node to initial positions when any autostart time is specified. @@ -1122,7 +1140,7 @@ class Ns2ScriptedMobility(WayPointMobility): self.state = self.STATE_RUNNING self.session.event_loop.add_event(t, self.run) - def start(self): + def start(self) -> None: """ Handle the case when un-paused. @@ -1134,7 +1152,7 @@ class Ns2ScriptedMobility(WayPointMobility): if laststate == self.STATE_PAUSED: self.statescript("unpause") - def run(self): + def run(self) -> None: """ Start is pressed or autostart is triggered. @@ -1143,7 +1161,7 @@ class Ns2ScriptedMobility(WayPointMobility): super().run() self.statescript("run") - def pause(self): + def pause(self) -> None: """ Pause the mobility script. @@ -1152,17 +1170,18 @@ class Ns2ScriptedMobility(WayPointMobility): super().pause() self.statescript("pause") - def stop(self, move_initial=True): + def stop(self, move_initial: bool = True) -> None: """ Stop the mobility script. - :param bool move_initial: flag to check if we should move node to initial position + :param bool move_initial: flag to check if we should move node to initial + position :return: nothing """ super().stop(move_initial=move_initial) self.statescript("stop") - def statescript(self, typestr): + def statescript(self, typestr: str) -> None: """ State of the mobility script.