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.enumerations import LinkTypes, NodeTypes
from core.emulator.session import Session
from core.errors import CoreError
from core.location.mobility import BasicRangeModel, Ns2ScriptedMobility
from core.nodes.base import CoreNode, CoreNodeBase, NodeBase
from core.nodes.interface import CoreInterface
from core.nodes.network import WlanNode
from core.services.coreservices import CoreService
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]:
current_config = session.emane.get_configs()
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)
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
if request.action == MobilityAction.START:
node.mobility.start()

View file

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

View file

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

View file

@ -206,6 +206,7 @@ class CanvasNode:
self.context.delete(0, tk.END)
is_wlan = self.core_node.type == NodeType.WIRELESS_LAN
is_emane = self.core_node.type == NodeType.EMANE
is_mobility = is_wlan or is_emane
if self.app.core.is_runtime():
self.context.add_command(label="Configure", command=self.show_config)
if is_emane:
@ -216,7 +217,7 @@ class CanvasNode:
self.context.add_command(
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(
label="Mobility Player", command=self.show_mobility_player
)
@ -235,6 +236,7 @@ class CanvasNode:
self.context.add_command(
label="WLAN Config", command=self.show_wlan_config
)
if is_mobility:
self.context.add_command(
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}
RJ45_NODES: Set[NodeType] = {NodeType.RJ45}
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"}
ROUTER_NODES: Set[str] = {"router", "mdr"}
ANTENNA_ICON: PhotoImage = None
@classmethod
def is_mobility(cls, node: Node) -> bool:
return node.type in cls.MOBILITY_NODES
@classmethod
def is_router_node(cls, node: Node) -> bool:
return cls.is_model_node(node.type) and node.model in cls.ROUTER_NODES

View file

@ -9,10 +9,11 @@ import threading
import time
from functools import total_ordering
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.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
from core.emane.nodes import EmaneNet
from core.emulator.data import EventData, LinkData, LinkOptions
from core.emulator.enumerations import (
ConfigDataTypes,
@ -31,6 +32,13 @@ if TYPE_CHECKING:
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):
"""
Member of session class for handling configuration data for mobility and
@ -69,35 +77,30 @@ class MobilityManager(ModelManager):
"""
if node_ids is None:
node_ids = self.nodes()
for node_id in node_ids:
logging.debug("checking mobility startup for node: %s", node_id)
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:
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:
logging.exception("mobility startup error")
logging.warning(
"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:
"""
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
:return: nothing
@ -106,40 +109,35 @@ class MobilityManager(ModelManager):
node_id = event_data.node
name = event_data.name
try:
node = self.session.get_node(node_id, WlanNode)
node = get_mobility_node(self.session, node_id)
except CoreError:
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
# name is e.g. "mobility:ns2script"
models = name[9:].split(",")
for model in models:
try:
cls = self.models[model]
except KeyError:
logging.warning("Ignoring event for unknown model '%s'", model)
cls = self.models.get(model)
if not cls:
logging.warning("ignoring event for unknown model '%s'", model)
continue
if cls.config_type in [RegisterTlvs.WIRELESS, RegisterTlvs.MOBILITY]:
model = node.mobility
else:
continue
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
if cls.name != model.name:
logging.warning(
"Ignoring event for %s wrong model %s,%s",
"ignoring event for %s wrong model %s,%s",
node.name,
cls.name,
model.name,
)
continue
if event_type in [EventTypes.STOP, EventTypes.RESTART]:
model.stop(move_initial=True)
if event_type in [EventTypes.START, EventTypes.RESTART]:
@ -162,11 +160,9 @@ class MobilityManager(ModelManager):
event_type = EventTypes.START
elif model.state == model.STATE_PAUSED:
event_type = EventTypes.PAUSE
start_time = int(model.lasttime - model.timezero)
end_time = int(model.endtime)
data = f"start={start_time} end={end_time}"
event_data = EventData(
node=model.id,
event_type=event_type,
@ -174,15 +170,14 @@ class MobilityManager(ModelManager):
data=data,
time=str(time.monotonic()),
)
self.session.broadcast_event(event_data)
def updatewlans(
def update_nets(
self, moved: List[CoreNode], moved_ifaces: 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
Update every mobility network. This saves range calculations if the model
were to recalculate for each individual node movement.
:param moved: moved nodes
@ -191,11 +186,11 @@ class MobilityManager(ModelManager):
"""
for node_id in self.nodes():
try:
node = self.session.get_node(node_id, WlanNode)
node = get_mobility_node(self.session, node_id)
if node.model:
node.model.update(moved, moved_ifaces)
except CoreError:
continue
if node.model:
node.model.update(moved, moved_ifaces)
logging.exception("error updating mobility node")
class WirelessModel(ConfigurableOptions):
@ -593,7 +588,7 @@ class WayPointMobility(WirelessModel):
self.lasttime: Optional[float] = None
self.endtime: Optional[int] = None
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
self.loop: bool = False
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)
self.empty_queue_stop: bool = True
def startup(self):
raise NotImplementedError
def runround(self) -> None:
"""
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?
moved = []
moved_ifaces = []
for iface in self.wlan.get_ifaces():
for iface in self.net.get_ifaces():
node = iface.node
if self.movenode(node, dt):
moved.append(node)
moved_ifaces.append(iface)
# 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
self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround)
@ -723,7 +721,7 @@ class WayPointMobility(WirelessModel):
"""
moved = []
moved_ifaces = []
for iface in self.wlan.get_ifaces():
for iface in self.net.get_ifaces():
node = iface.node
if node.id not in self.initial:
continue
@ -731,7 +729,7 @@ class WayPointMobility(WirelessModel):
self.setnodeposition(node, x, y, z)
moved.append(node)
moved_ifaces.append(iface)
self.session.mobility.updatewlans(moved, moved_ifaces)
self.session.mobility.update_nets(moved, moved_ifaces)
def addwaypoint(
self,
@ -1094,7 +1092,7 @@ class Ns2ScriptedMobility(WayPointMobility):
:return: nothing
"""
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
try:
t = float(self.autostart)
@ -1102,11 +1100,11 @@ class Ns2ScriptedMobility(WayPointMobility):
logging.exception(
"Invalid auto-start seconds specified '%s' for %s",
self.autostart,
self.wlan.name,
self.net.name,
)
return
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.session.event_loop.add_event(t, self.run)