Merge pull request #504 from coreemu/bugfix/emane-mobility
Bugfix/emane mobility
This commit is contained in:
commit
f54ddc0912
7 changed files with 76 additions and 53 deletions
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
except CoreError:
|
||||
continue
|
||||
node = get_mobility_node(self.session, node_id)
|
||||
if node.model:
|
||||
node.model.update(moved, moved_ifaces)
|
||||
except CoreError:
|
||||
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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue