Merge pull request #504 from coreemu/bugfix/emane-mobility

Bugfix/emane mobility
This commit is contained in:
bharnden 2020-07-29 17:15:29 -07:00 committed by GitHub
commit f54ddc0912
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 53 deletions

View file

@ -22,9 +22,11 @@ from core.emane.nodes import EmaneNet
from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions from core.emulator.data import InterfaceData, LinkData, LinkOptions, NodeOptions
from core.emulator.enumerations import LinkTypes, NodeTypes from core.emulator.enumerations import LinkTypes, NodeTypes
from core.emulator.session import Session from core.emulator.session import Session
from core.errors import CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
from core.nodes.interface import CoreInterface from core.nodes.interface import CoreInterface
from core.nodes.network import WlanNode
from core.services.coreservices import CoreService from core.services.coreservices import CoreService
WORKERS = 10 WORKERS = 10
@ -661,3 +663,15 @@ def get_node_config_service_configs(session: Session) -> List[ConfigServiceConfi
def get_emane_config(session: Session) -> Dict[str, common_pb2.ConfigOption]: def get_emane_config(session: Session) -> Dict[str, common_pb2.ConfigOption]:
current_config = session.emane.get_configs() current_config = session.emane.get_configs()
return get_config_options(current_config, session.emane.emane_config) return get_config_options(current_config, session.emane.emane_config)
def get_mobility_node(
session: Session, node_id: int, context: ServicerContext
) -> Union[WlanNode, EmaneNet]:
try:
return session.get_node(node_id, WlanNode)
except CoreError:
try:
return session.get_node(node_id, EmaneNet)
except CoreError:
context.abort(grpc.StatusCode.NOT_FOUND, "node id is not for wlan or emane")

View file

@ -1125,7 +1125,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
""" """
logging.debug("mobility action: %s", request) logging.debug("mobility action: %s", request)
session = self.get_session(request.session_id, context) session = self.get_session(request.session_id, context)
node = self.get_node(session, request.node_id, context, WlanNode) node = grpcutils.get_mobility_node(session, request.node_id, context)
if not node.mobility:
context.abort(
grpc.StatusCode.NOT_FOUND, f"node({node.name}) does not have mobility"
)
result = True result = True
if request.action == MobilityAction.START: if request.action == MobilityAction.START:
node.mobility.start() node.mobility.start()

View file

@ -130,8 +130,8 @@ class EmaneModel(WirelessModel):
:return: nothing :return: nothing
""" """
try: try:
wlan = self.session.get_node(self.id, EmaneNet) emane_net = self.session.get_node(self.id, EmaneNet)
wlan.setnempositions(moved_ifaces) emane_net.setnempositions(moved_ifaces)
except CoreError: except CoreError:
logging.exception("error during update") logging.exception("error during update")

View file

@ -538,7 +538,7 @@ class CoreClient:
def show_mobility_players(self) -> None: def show_mobility_players(self) -> None:
for node in self.session.nodes.values(): for node in self.session.nodes.values():
if node.type != NodeType.WIRELESS_LAN: if not NodeUtils.is_mobility(node):
continue continue
if node.mobility_config: if node.mobility_config:
mobility_player = MobilityPlayer(self.app, node) mobility_player = MobilityPlayer(self.app, node)
@ -927,7 +927,7 @@ class CoreClient:
def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]: def get_mobility_configs_proto(self) -> List[mobility_pb2.MobilityConfig]:
configs = [] configs = []
for node in self.session.nodes.values(): for node in self.session.nodes.values():
if node.type != NodeType.WIRELESS_LAN: if not NodeUtils.is_mobility(node):
continue continue
if not node.mobility_config: if not node.mobility_config:
continue continue

View file

@ -206,6 +206,7 @@ class CanvasNode:
self.context.delete(0, tk.END) self.context.delete(0, tk.END)
is_wlan = self.core_node.type == NodeType.WIRELESS_LAN is_wlan = self.core_node.type == NodeType.WIRELESS_LAN
is_emane = self.core_node.type == NodeType.EMANE is_emane = self.core_node.type == NodeType.EMANE
is_mobility = is_wlan or is_emane
if self.app.core.is_runtime(): if self.app.core.is_runtime():
self.context.add_command(label="Configure", command=self.show_config) self.context.add_command(label="Configure", command=self.show_config)
if is_emane: if is_emane:
@ -216,7 +217,7 @@ class CanvasNode:
self.context.add_command( self.context.add_command(
label="WLAN Config", command=self.show_wlan_config label="WLAN Config", command=self.show_wlan_config
) )
if is_wlan and self.core_node.id in self.app.core.mobility_players: if is_mobility and self.core_node.id in self.app.core.mobility_players:
self.context.add_command( self.context.add_command(
label="Mobility Player", command=self.show_mobility_player label="Mobility Player", command=self.show_mobility_player
) )
@ -235,6 +236,7 @@ class CanvasNode:
self.context.add_command( self.context.add_command(
label="WLAN Config", command=self.show_wlan_config label="WLAN Config", command=self.show_wlan_config
) )
if is_mobility:
self.context.add_command( self.context.add_command(
label="Mobility Config", command=self.show_mobility_config label="Mobility Config", command=self.show_mobility_config
) )

View file

@ -63,10 +63,15 @@ class NodeUtils:
WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE} WIRELESS_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
RJ45_NODES: Set[NodeType] = {NodeType.RJ45} RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET} IGNORE_NODES: Set[NodeType] = {NodeType.CONTROL_NET}
MOBILITY_NODES: Set[NodeType] = {NodeType.WIRELESS_LAN, NodeType.EMANE}
NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"} NODE_MODELS: Set[str] = {"router", "host", "PC", "mdr", "prouter"}
ROUTER_NODES: Set[str] = {"router", "mdr"} ROUTER_NODES: Set[str] = {"router", "mdr"}
ANTENNA_ICON: PhotoImage = None ANTENNA_ICON: PhotoImage = None
@classmethod
def is_mobility(cls, node: Node) -> bool:
return node.type in cls.MOBILITY_NODES
@classmethod @classmethod
def is_router_node(cls, node: Node) -> bool: def is_router_node(cls, node: Node) -> bool:
return cls.is_model_node(node.type) and node.model in cls.ROUTER_NODES return cls.is_model_node(node.type) and node.model in cls.ROUTER_NODES

View file

@ -9,10 +9,11 @@ import threading
import time import time
from functools import total_ordering from functools import total_ordering
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, Union
from core import utils from core import utils
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
from core.emane.nodes import EmaneNet
from core.emulator.data import EventData, LinkData, LinkOptions from core.emulator.data import EventData, LinkData, LinkOptions
from core.emulator.enumerations import ( from core.emulator.enumerations import (
ConfigDataTypes, ConfigDataTypes,
@ -31,6 +32,13 @@ if TYPE_CHECKING:
from core.emulator.session import Session from core.emulator.session import Session
def get_mobility_node(session: "Session", node_id: int) -> Union[WlanNode, EmaneNet]:
try:
return session.get_node(node_id, WlanNode)
except CoreError:
return session.get_node(node_id, EmaneNet)
class MobilityManager(ModelManager): class MobilityManager(ModelManager):
""" """
Member of session class for handling configuration data for mobility and Member of session class for handling configuration data for mobility and
@ -69,35 +77,30 @@ class MobilityManager(ModelManager):
""" """
if node_ids is None: if node_ids is None:
node_ids = self.nodes() node_ids = self.nodes()
for node_id in node_ids: for node_id in node_ids:
logging.debug("checking mobility startup for node: %s", node_id)
logging.debug( logging.debug(
"node mobility configurations: %s", self.get_all_configs(node_id) "node(%s) mobility startup: %s", node_id, self.get_all_configs(node_id)
) )
try: try:
node = self.session.get_node(node_id, WlanNode) node = get_mobility_node(self.session, node_id)
# TODO: may be an issue if there are multiple mobility models
for model in self.models.values():
config = self.get_configs(node_id, model.name)
if not config:
continue
self.set_model(node, model, config)
if node.mobility:
self.session.event_loop.add_event(0.0, node.mobility.startup)
except CoreError: except CoreError:
logging.exception("mobility startup error")
logging.warning( logging.warning(
"skipping mobility configuration for unknown node: %s", node_id "skipping mobility configuration for unknown node: %s", node_id
) )
continue
for model_name in self.models:
config = self.get_configs(node_id, model_name)
if not config:
continue
model_class = self.models[model_name]
self.set_model(node, model_class, config)
if node.mobility:
self.session.event_loop.add_event(0.0, node.mobility.startup)
def handleevent(self, event_data: EventData) -> None: def handleevent(self, event_data: EventData) -> None:
""" """
Handle an Event Message used to start, stop, or pause Handle an Event Message used to start, stop, or pause
mobility scripts for a given WlanNode. mobility scripts for a given mobility network.
:param event_data: event data to handle :param event_data: event data to handle
:return: nothing :return: nothing
@ -106,40 +109,35 @@ class MobilityManager(ModelManager):
node_id = event_data.node node_id = event_data.node
name = event_data.name name = event_data.name
try: try:
node = self.session.get_node(node_id, WlanNode) node = get_mobility_node(self.session, node_id)
except CoreError: except CoreError:
logging.exception( logging.exception(
"Ignoring event for model '%s', unknown node '%s'", name, node_id "ignoring event for model(%s), unknown node(%s)", name, node_id
) )
return return
# name is e.g. "mobility:ns2script" # name is e.g. "mobility:ns2script"
models = name[9:].split(",") models = name[9:].split(",")
for model in models: for model in models:
try: cls = self.models.get(model)
cls = self.models[model] if not cls:
except KeyError: logging.warning("ignoring event for unknown model '%s'", model)
logging.warning("Ignoring event for unknown model '%s'", model)
continue continue
if cls.config_type in [RegisterTlvs.WIRELESS, RegisterTlvs.MOBILITY]: if cls.config_type in [RegisterTlvs.WIRELESS, RegisterTlvs.MOBILITY]:
model = node.mobility model = node.mobility
else: else:
continue continue
if model is None: if model is None:
logging.warning("Ignoring event, %s has no model", node.name) logging.warning("ignoring event, %s has no model", node.name)
continue continue
if cls.name != model.name: if cls.name != model.name:
logging.warning( logging.warning(
"Ignoring event for %s wrong model %s,%s", "ignoring event for %s wrong model %s,%s",
node.name, node.name,
cls.name, cls.name,
model.name, model.name,
) )
continue continue
if event_type in [EventTypes.STOP, EventTypes.RESTART]: if event_type in [EventTypes.STOP, EventTypes.RESTART]:
model.stop(move_initial=True) model.stop(move_initial=True)
if event_type in [EventTypes.START, EventTypes.RESTART]: if event_type in [EventTypes.START, EventTypes.RESTART]:
@ -162,11 +160,9 @@ class MobilityManager(ModelManager):
event_type = EventTypes.START event_type = EventTypes.START
elif model.state == model.STATE_PAUSED: elif model.state == model.STATE_PAUSED:
event_type = EventTypes.PAUSE event_type = EventTypes.PAUSE
start_time = int(model.lasttime - model.timezero) start_time = int(model.lasttime - model.timezero)
end_time = int(model.endtime) end_time = int(model.endtime)
data = f"start={start_time} end={end_time}" data = f"start={start_time} end={end_time}"
event_data = EventData( event_data = EventData(
node=model.id, node=model.id,
event_type=event_type, event_type=event_type,
@ -174,15 +170,14 @@ class MobilityManager(ModelManager):
data=data, data=data,
time=str(time.monotonic()), time=str(time.monotonic()),
) )
self.session.broadcast_event(event_data) self.session.broadcast_event(event_data)
def updatewlans( def update_nets(
self, moved: List[CoreNode], moved_ifaces: List[CoreInterface] self, moved: List[CoreNode], moved_ifaces: List[CoreInterface]
) -> None: ) -> None:
""" """
A mobility script has caused nodes in the 'moved' list to move. A mobility script has caused nodes in the 'moved' list to move.
Update every WlanNode. This saves range calculations if the model Update every mobility network. This saves range calculations if the model
were to recalculate for each individual node movement. were to recalculate for each individual node movement.
:param moved: moved nodes :param moved: moved nodes
@ -191,11 +186,11 @@ class MobilityManager(ModelManager):
""" """
for node_id in self.nodes(): for node_id in self.nodes():
try: try:
node = self.session.get_node(node_id, WlanNode) node = get_mobility_node(self.session, node_id)
except CoreError:
continue
if node.model: if node.model:
node.model.update(moved, moved_ifaces) node.model.update(moved, moved_ifaces)
except CoreError:
logging.exception("error updating mobility node")
class WirelessModel(ConfigurableOptions): class WirelessModel(ConfigurableOptions):
@ -593,7 +588,7 @@ class WayPointMobility(WirelessModel):
self.lasttime: Optional[float] = None self.lasttime: Optional[float] = None
self.endtime: Optional[int] = None self.endtime: Optional[int] = None
self.timezero: float = 0.0 self.timezero: float = 0.0
self.wlan: WlanNode = session.get_node(_id, WlanNode) self.net: Union[WlanNode, EmaneNet] = get_mobility_node(self.session, self.id)
# these are really set in child class via confmatrix # these are really set in child class via confmatrix
self.loop: bool = False self.loop: bool = False
self.refresh_ms: int = 50 self.refresh_ms: int = 50
@ -601,6 +596,9 @@ class WayPointMobility(WirelessModel):
# (ns-3 sets this to False as new waypoints may be added from trace) # (ns-3 sets this to False as new waypoints may be added from trace)
self.empty_queue_stop: bool = True self.empty_queue_stop: bool = True
def startup(self):
raise NotImplementedError
def runround(self) -> None: def runround(self) -> None:
""" """
Advance script time and move nodes. Advance script time and move nodes.
@ -643,14 +641,14 @@ class WayPointMobility(WirelessModel):
# only move interfaces attached to self.wlan, or all nodenum in script? # only move interfaces attached to self.wlan, or all nodenum in script?
moved = [] moved = []
moved_ifaces = [] moved_ifaces = []
for iface in self.wlan.get_ifaces(): for iface in self.net.get_ifaces():
node = iface.node node = iface.node
if self.movenode(node, dt): if self.movenode(node, dt):
moved.append(node) moved.append(node)
moved_ifaces.append(iface) moved_ifaces.append(iface)
# calculate all ranges after moving nodes; this saves calculations # calculate all ranges after moving nodes; this saves calculations
self.session.mobility.updatewlans(moved, moved_ifaces) self.session.mobility.update_nets(moved, moved_ifaces)
# TODO: check session state # TODO: check session state
self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround) self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround)
@ -723,7 +721,7 @@ class WayPointMobility(WirelessModel):
""" """
moved = [] moved = []
moved_ifaces = [] moved_ifaces = []
for iface in self.wlan.get_ifaces(): for iface in self.net.get_ifaces():
node = iface.node node = iface.node
if node.id not in self.initial: if node.id not in self.initial:
continue continue
@ -731,7 +729,7 @@ class WayPointMobility(WirelessModel):
self.setnodeposition(node, x, y, z) self.setnodeposition(node, x, y, z)
moved.append(node) moved.append(node)
moved_ifaces.append(iface) moved_ifaces.append(iface)
self.session.mobility.updatewlans(moved, moved_ifaces) self.session.mobility.update_nets(moved, moved_ifaces)
def addwaypoint( def addwaypoint(
self, self,
@ -1094,7 +1092,7 @@ class Ns2ScriptedMobility(WayPointMobility):
:return: nothing :return: nothing
""" """
if self.autostart == "": if self.autostart == "":
logging.info("not auto-starting ns-2 script for %s", self.wlan.name) logging.info("not auto-starting ns-2 script for %s", self.net.name)
return return
try: try:
t = float(self.autostart) t = float(self.autostart)
@ -1102,11 +1100,11 @@ class Ns2ScriptedMobility(WayPointMobility):
logging.exception( logging.exception(
"Invalid auto-start seconds specified '%s' for %s", "Invalid auto-start seconds specified '%s' for %s",
self.autostart, self.autostart,
self.wlan.name, self.net.name,
) )
return return
self.movenodesinitial() self.movenodesinitial()
logging.info("scheduling ns-2 script for %s autostart at %s", self.wlan.name, t) logging.info("scheduling ns-2 script for %s autostart at %s", self.net.name, t)
self.state = self.STATE_RUNNING self.state = self.STATE_RUNNING
self.session.event_loop.add_event(t, self.run) self.session.event_loop.add_event(t, self.run)