2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
mobility.py: mobility helpers for moving nodes and calculating wireless range.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
import heapq
|
2019-02-16 17:50:19 +00:00
|
|
|
import logging
|
2017-04-25 16:45:34 +01:00
|
|
|
import math
|
|
|
|
import os
|
|
|
|
import threading
|
|
|
|
import time
|
2019-06-11 23:07:36 +01:00
|
|
|
from functools import total_ordering
|
2020-01-14 06:15:44 +00:00
|
|
|
from typing import TYPE_CHECKING, Dict, List, Tuple
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2019-09-28 07:29:15 +01:00
|
|
|
from core import utils
|
2019-09-10 22:20:51 +01:00
|
|
|
from core.config import ConfigGroup, ConfigurableOptions, Configuration, ModelManager
|
|
|
|
from core.emulator.data import EventData, LinkData
|
|
|
|
from core.emulator.enumerations import (
|
|
|
|
ConfigDataTypes,
|
|
|
|
EventTypes,
|
|
|
|
LinkTypes,
|
|
|
|
MessageFlags,
|
|
|
|
RegisterTlvs,
|
|
|
|
)
|
2019-09-28 07:29:15 +01:00
|
|
|
from core.errors import CoreError
|
2020-01-14 06:15:44 +00:00
|
|
|
from core.nodes.base import CoreNode, NodeBase
|
|
|
|
from core.nodes.interface import CoreInterface
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
from core.emulator.session import Session
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2018-06-14 00:17:47 +01:00
|
|
|
class MobilityManager(ModelManager):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Member of session class for handling configuration data for mobility and
|
2013-08-29 15:21:13 +01:00
|
|
|
range models.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
name = "MobilityManager"
|
|
|
|
config_type = RegisterTlvs.WIRELESS.value
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, session: "Session") -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Creates a MobilityManager instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: session this manager is tied to
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().__init__()
|
2017-04-25 16:45:34 +01:00
|
|
|
self.session = session
|
2018-06-14 00:17:47 +01:00
|
|
|
self.models[BasicRangeModel.name] = BasicRangeModel
|
|
|
|
self.models[Ns2ScriptedMobility.name] = Ns2ScriptedMobility
|
2018-06-06 22:51:45 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def reset(self) -> None:
|
2019-06-25 20:29:46 +01:00
|
|
|
"""
|
|
|
|
Clear out all current configurations.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
|
|
|
self.config_reset()
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def startup(self, node_ids: List[int] = None) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Session is transitioning from instantiation to runtime state.
|
2013-08-29 15:21:13 +01:00
|
|
|
Instantiate any mobility models that have been configured for a WLAN.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param node_ids: node ids to startup
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
if node_ids is None:
|
2018-06-06 22:51:45 +01:00
|
|
|
node_ids = self.nodes()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
for node_id in node_ids:
|
2019-09-11 23:05:05 +01:00
|
|
|
logging.debug("checking mobility startup for node: %s", node_id)
|
|
|
|
logging.debug(
|
2019-09-10 23:10:24 +01:00
|
|
|
"node mobility configurations: %s", self.get_all_configs(node_id)
|
|
|
|
)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2019-04-30 07:31:47 +01:00
|
|
|
node = self.session.get_node(node_id)
|
2019-09-12 23:48:09 +01:00
|
|
|
except CoreError:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.warning(
|
|
|
|
"skipping mobility configuration for unknown node: %s", node_id
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2019-05-06 05:23:43 +01:00
|
|
|
for model_name in self.models:
|
2018-07-12 05:34:21 +01:00
|
|
|
config = self.get_configs(node_id, model_name)
|
2018-06-13 19:59:50 +01:00
|
|
|
if not config:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2018-06-14 00:17:47 +01:00
|
|
|
model_class = self.models[model_name]
|
2018-06-13 19:59:50 +01:00
|
|
|
self.set_model(node, model_class, config)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
if node.mobility:
|
|
|
|
self.session.event_loop.add_event(0.0, node.mobility.startup)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def handleevent(self, event_data: EventData) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Handle an Event Message used to start, stop, or pause
|
|
|
|
mobility scripts for a given WlanNode.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param event_data: event data to handle
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
event_type = event_data.event_type
|
|
|
|
node_id = event_data.node
|
|
|
|
name = event_data.name
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2019-04-30 07:31:47 +01:00
|
|
|
node = self.session.get_node(node_id)
|
2019-09-12 23:48:09 +01:00
|
|
|
except CoreError:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.exception(
|
|
|
|
"Ignoring event for model '%s', unknown node '%s'", name, node_id
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
# name is e.g. "mobility:ns2script"
|
2019-09-10 23:10:24 +01:00
|
|
|
models = name[9:].split(",")
|
2017-04-25 16:45:34 +01:00
|
|
|
for model in models:
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2018-06-14 00:17:47 +01:00
|
|
|
cls = self.models[model]
|
2013-08-29 15:21:13 +01:00
|
|
|
except KeyError:
|
2019-06-03 22:36:21 +01:00
|
|
|
logging.warning("Ignoring event for unknown model '%s'", model)
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2019-09-10 23:10:24 +01:00
|
|
|
if cls.config_type in [
|
|
|
|
RegisterTlvs.WIRELESS.value,
|
|
|
|
RegisterTlvs.MOBILITY.value,
|
|
|
|
]:
|
2013-08-29 15:21:13 +01:00
|
|
|
model = node.mobility
|
|
|
|
else:
|
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if model is None:
|
2019-06-03 22:36:21 +01:00
|
|
|
logging.warning("Ignoring event, %s has no model", node.name)
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
if cls.name != model.name:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.warning(
|
|
|
|
"Ignoring event for %s wrong model %s,%s",
|
|
|
|
node.name,
|
|
|
|
cls.name,
|
|
|
|
model.name,
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2019-09-10 23:10:24 +01:00
|
|
|
if (
|
|
|
|
event_type == EventTypes.STOP.value
|
|
|
|
or event_type == EventTypes.RESTART.value
|
|
|
|
):
|
2013-08-29 15:21:13 +01:00
|
|
|
model.stop(move_initial=True)
|
2019-09-10 23:10:24 +01:00
|
|
|
if (
|
|
|
|
event_type == EventTypes.START.value
|
|
|
|
or event_type == EventTypes.RESTART.value
|
|
|
|
):
|
2013-08-29 15:21:13 +01:00
|
|
|
model.start()
|
2017-04-25 16:45:34 +01:00
|
|
|
if event_type == EventTypes.PAUSE.value:
|
2013-08-29 15:21:13 +01:00
|
|
|
model.pause()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def sendevent(self, model: "WayPointMobility") -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Send an event message on behalf of a mobility model.
|
|
|
|
This communicates the current and end (max) times to the GUI.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param model: mobility model to send event for
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
event_type = EventTypes.NONE.value
|
2013-08-29 15:21:13 +01:00
|
|
|
if model.state == model.STATE_STOPPED:
|
2017-04-25 16:45:34 +01:00
|
|
|
event_type = EventTypes.STOP.value
|
2013-08-29 15:21:13 +01:00
|
|
|
elif model.state == model.STATE_RUNNING:
|
2017-04-25 16:45:34 +01:00
|
|
|
event_type = EventTypes.START.value
|
2013-08-29 15:21:13 +01:00
|
|
|
elif model.state == model.STATE_PAUSED:
|
2017-04-25 16:45:34 +01:00
|
|
|
event_type = EventTypes.PAUSE.value
|
|
|
|
|
2019-10-18 18:33:31 +01:00
|
|
|
start_time = int(model.lasttime - model.timezero)
|
|
|
|
end_time = int(model.endtime)
|
|
|
|
data = f"start={start_time} end={end_time}"
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
event_data = EventData(
|
2019-04-30 07:31:47 +01:00
|
|
|
node=model.id,
|
2017-04-25 16:45:34 +01:00
|
|
|
event_type=event_type,
|
2019-10-18 18:33:31 +01:00
|
|
|
name=f"mobility:{model.name}",
|
2017-04-25 16:45:34 +01:00
|
|
|
data=data,
|
2019-12-06 17:42:41 +00:00
|
|
|
time=str(time.monotonic()),
|
2017-04-25 16:45:34 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
self.session.broadcast_event(event_data)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def updatewlans(
|
|
|
|
self, moved: List[NodeBase], moved_netifs: List[CoreInterface]
|
|
|
|
) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
A mobility script has caused nodes in the 'moved' list to move.
|
|
|
|
Update every WlanNode. This saves range calculations if the model
|
|
|
|
were to recalculate for each individual node movement.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param moved: moved nodes
|
|
|
|
:param moved_netifs: moved network interfaces
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-06-06 22:51:45 +01:00
|
|
|
for node_id in self.nodes():
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2019-04-30 07:31:47 +01:00
|
|
|
node = self.session.get_node(node_id)
|
2019-09-12 23:48:09 +01:00
|
|
|
except CoreError:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2018-06-06 22:51:45 +01:00
|
|
|
if node.model:
|
|
|
|
node.model.update(moved, moved_netifs)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2018-06-06 22:51:45 +01:00
|
|
|
class WirelessModel(ConfigurableOptions):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Base class used by EMANE models and the basic range model.
|
2013-08-29 15:21:13 +01:00
|
|
|
Used for managing arbitrary configuration parameters.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
config_type = RegisterTlvs.WIRELESS.value
|
|
|
|
bitmap = None
|
|
|
|
position_callback = None
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, session: "Session", _id: int):
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Create a WirelessModel instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: core session we are tied to
|
|
|
|
:param _id: object id
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
2018-06-06 22:51:45 +01:00
|
|
|
self.session = session
|
2019-04-30 07:31:47 +01:00
|
|
|
self.id = _id
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def all_link_data(self, flags: int) -> List:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
May be used if the model can populate the GUI with wireless (green)
|
|
|
|
link lines.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:param flags: link data flags
|
|
|
|
:return: link data
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
return []
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def update(self, moved: bool, moved_netifs: List[CoreInterface]) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Update this wireless model.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param moved: flag is it was moved
|
|
|
|
:param moved_netifs: moved network interfaces
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
raise NotImplementedError
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def update_config(self, config: Dict[str, str]) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2020-01-14 06:15:44 +00:00
|
|
|
For run-time updates of model config. Returns True when position callback and
|
|
|
|
set link parameters should be invoked.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param config: configuration values to update
|
2018-06-13 19:59:50 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-06-13 19:59:50 +01:00
|
|
|
pass
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class BasicRangeModel(WirelessModel):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Basic Range wireless model, calculates range between nodes and links
|
2013-08-29 15:21:13 +01:00
|
|
|
and unlinks nodes based on this distance. This was formerly done from
|
|
|
|
the GUI.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
name = "basic_range"
|
2018-06-14 16:41:48 +01:00
|
|
|
options = [
|
2019-09-10 23:10:24 +01:00
|
|
|
Configuration(
|
|
|
|
_id="range",
|
|
|
|
_type=ConfigDataTypes.UINT32,
|
|
|
|
default="275",
|
|
|
|
label="wireless range (pixels)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="bandwidth",
|
|
|
|
_type=ConfigDataTypes.UINT64,
|
|
|
|
default="54000000",
|
|
|
|
label="bandwidth (bps)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="jitter",
|
|
|
|
_type=ConfigDataTypes.UINT64,
|
|
|
|
default="0",
|
|
|
|
label="transmission jitter (usec)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="delay",
|
|
|
|
_type=ConfigDataTypes.UINT64,
|
|
|
|
default="5000",
|
|
|
|
label="transmission delay (usec)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="error",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
default="0",
|
|
|
|
label="error rate (%)",
|
|
|
|
),
|
2018-06-14 16:41:48 +01:00
|
|
|
]
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2018-06-06 22:51:45 +01:00
|
|
|
@classmethod
|
2018-06-14 16:41:48 +01:00
|
|
|
def config_groups(cls):
|
2019-09-10 23:10:24 +01:00
|
|
|
return [ConfigGroup("Basic Range Parameters", 1, len(cls.configurations()))]
|
2018-06-06 22:51:45 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, session: "Session", _id: int) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 17:30:49 +01:00
|
|
|
Create a BasicRangeModel instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: related core session
|
|
|
|
:param _id: object id
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-10-23 18:40:40 +01:00
|
|
|
super().__init__(session, _id)
|
2018-06-06 22:51:45 +01:00
|
|
|
self.session = session
|
2019-04-30 07:31:47 +01:00
|
|
|
self.wlan = session.get_node(_id)
|
2013-08-29 15:21:13 +01:00
|
|
|
self._netifs = {}
|
|
|
|
self._netifslock = threading.Lock()
|
2013-12-09 17:56:06 +00:00
|
|
|
|
2019-09-12 05:54:07 +01:00
|
|
|
self.range = 0
|
2017-05-03 17:30:49 +01:00
|
|
|
self.bw = None
|
|
|
|
self.delay = None
|
|
|
|
self.loss = None
|
|
|
|
self.jitter = None
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def values_from_config(self, config: Dict[str, str]) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Values to convert to link parameters.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param config: values to convert
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2019-06-11 23:27:50 +01:00
|
|
|
self.range = int(float(config["range"]))
|
2019-09-11 23:05:05 +01:00
|
|
|
logging.debug(
|
2019-09-10 23:10:24 +01:00
|
|
|
"basic range model configured for WLAN %d using range %d",
|
|
|
|
self.wlan.id,
|
|
|
|
self.range,
|
|
|
|
)
|
2018-06-06 22:51:45 +01:00
|
|
|
self.bw = int(config["bandwidth"])
|
2019-06-09 00:56:39 +01:00
|
|
|
if self.bw == 0:
|
2013-08-29 15:21:13 +01:00
|
|
|
self.bw = None
|
2019-06-09 00:56:39 +01:00
|
|
|
self.delay = int(config["delay"])
|
|
|
|
if self.delay == 0:
|
2013-08-29 15:21:13 +01:00
|
|
|
self.delay = None
|
2019-10-12 06:27:04 +01:00
|
|
|
self.loss = int(float(config["error"]))
|
2019-06-09 00:56:39 +01:00
|
|
|
if self.loss == 0:
|
2013-08-29 15:21:13 +01:00
|
|
|
self.loss = None
|
2019-06-09 00:56:39 +01:00
|
|
|
self.jitter = int(config["jitter"])
|
|
|
|
if self.jitter == 0:
|
2013-08-29 15:21:13 +01:00
|
|
|
self.jitter = None
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def setlinkparams(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Apply link parameters to all interfaces. This is invoked from
|
2013-08-29 15:21:13 +01:00
|
|
|
WlanNode.setmodel() after the position callback has been set.
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
with self._netifslock:
|
|
|
|
for netif in self._netifs:
|
2019-09-10 23:10:24 +01:00
|
|
|
self.wlan.linkconfig(
|
|
|
|
netif,
|
|
|
|
bw=self.bw,
|
|
|
|
delay=self.delay,
|
|
|
|
loss=self.loss,
|
|
|
|
duplicate=None,
|
|
|
|
jitter=self.jitter,
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def get_position(self, netif: CoreInterface) -> Tuple[float, float, float]:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Retrieve network interface position.
|
|
|
|
|
|
|
|
:param netif: network interface position to retrieve
|
|
|
|
:return: network interface position
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
with self._netifslock:
|
|
|
|
return self._netifs[netif]
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def set_position(
|
|
|
|
self, netif: CoreInterface, x: float = None, y: float = None, z: float = None
|
|
|
|
) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
A node has moved; given an interface, a new (x,y,z) position has
|
2013-08-29 15:21:13 +01:00
|
|
|
been set; calculate the new distance between other nodes and link or
|
|
|
|
unlink node pairs based on the configured range.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:param netif: network interface to set position for
|
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self._netifslock.acquire()
|
|
|
|
self._netifs[netif] = (x, y, z)
|
|
|
|
if x is None or y is None:
|
|
|
|
self._netifslock.release()
|
|
|
|
return
|
|
|
|
for netif2 in self._netifs:
|
|
|
|
self.calclink(netif, netif2)
|
|
|
|
self._netifslock.release()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
|
|
|
position_callback = set_position
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def update(self, moved: bool, moved_netifs: List[CoreInterface]) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Node positions have changed without recalc. Update positions from
|
2013-08-29 15:21:13 +01:00
|
|
|
node.position, then re-calculate links for those that have moved.
|
|
|
|
Assumes bidirectional links, with one calculation per node pair, where
|
|
|
|
one of the nodes has moved.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param moved: flag is it was moved
|
|
|
|
:param moved_netifs: moved network interfaces
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
with self._netifslock:
|
|
|
|
while len(moved_netifs):
|
|
|
|
netif = moved_netifs.pop()
|
2018-06-06 22:51:45 +01:00
|
|
|
nx, ny, nz = netif.node.getposition()
|
2013-08-29 15:21:13 +01:00
|
|
|
if netif in self._netifs:
|
|
|
|
self._netifs[netif] = (nx, ny, nz)
|
|
|
|
for netif2 in self._netifs:
|
|
|
|
if netif2 in moved_netifs:
|
|
|
|
continue
|
|
|
|
self.calclink(netif, netif2)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def calclink(self, netif: CoreInterface, netif2: CoreInterface) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper used by set_position() and update() to
|
|
|
|
calculate distance between two interfaces and perform
|
|
|
|
linking/unlinking. Sends link/unlink messages and updates the
|
|
|
|
WlanNode's linked dict.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:param netif: interface one
|
|
|
|
:param netif2: interface two
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if netif == netif2:
|
|
|
|
return
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
2017-04-27 21:34:23 +01:00
|
|
|
x, y, z = self._netifs[netif]
|
|
|
|
x2, y2, z2 = self._netifs[netif2]
|
|
|
|
|
|
|
|
if x2 is None or y2 is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
d = self.calcdistance((x, y, z), (x2, y2, z2))
|
|
|
|
|
|
|
|
# ordering is important, to keep the wlan._linked dict organized
|
|
|
|
a = min(netif, netif2)
|
|
|
|
b = max(netif, netif2)
|
|
|
|
|
|
|
|
with self.wlan._linked_lock:
|
|
|
|
linked = self.wlan.linked(a, b)
|
|
|
|
|
|
|
|
if d > self.range:
|
|
|
|
if linked:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.debug("was linked, unlinking")
|
2017-04-27 21:34:23 +01:00
|
|
|
self.wlan.unlink(a, b)
|
|
|
|
self.sendlinkmsg(a, b, unlink=True)
|
|
|
|
else:
|
|
|
|
if not linked:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.debug("was not linked, linking")
|
2017-04-27 21:34:23 +01:00
|
|
|
self.wlan.link(a, b)
|
|
|
|
self.sendlinkmsg(a, b)
|
2013-08-29 15:21:13 +01:00
|
|
|
except KeyError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("error getting interfaces during calclinkS")
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2014-10-31 16:46:43 +00:00
|
|
|
@staticmethod
|
2020-01-14 06:15:44 +00:00
|
|
|
def calcdistance(
|
|
|
|
p1: Tuple[float, float, float], p2: Tuple[float, float, float]
|
|
|
|
) -> float:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Calculate the distance between two three-dimensional points.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param p1: point one
|
|
|
|
:param p2: point two
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: distance petween the points
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
a = p1[0] - p2[0]
|
|
|
|
b = p1[1] - p2[1]
|
|
|
|
c = 0
|
|
|
|
if p1[2] is not None and p2[2] is not None:
|
|
|
|
c = p1[2] - p2[2]
|
|
|
|
return math.hypot(math.hypot(a, b), c)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def update_config(self, config: Dict[str, str]) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Configuration has changed during runtime.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param config: values to update configuration
|
2018-06-13 19:59:50 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2018-06-06 22:51:45 +01:00
|
|
|
self.values_from_config(config)
|
2018-06-11 20:26:12 +01:00
|
|
|
self.setlinkparams()
|
2013-12-09 17:56:06 +00:00
|
|
|
return True
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def create_link_data(
|
|
|
|
self, interface1: CoreInterface, interface2: CoreInterface, message_type: int
|
|
|
|
) -> LinkData:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Create a wireless link/unlink data message.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param interface1: interface one
|
|
|
|
:param interface2: interface two
|
2017-05-03 17:30:49 +01:00
|
|
|
:param message_type: link message type
|
|
|
|
:return: link data
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
return LinkData(
|
|
|
|
message_type=message_type,
|
2019-04-27 06:07:51 +01:00
|
|
|
node1_id=interface1.node.id,
|
|
|
|
node2_id=interface2.node.id,
|
|
|
|
network_id=self.wlan.id,
|
2019-09-10 23:10:24 +01:00
|
|
|
link_type=LinkTypes.WIRELESS.value,
|
2017-04-25 16:45:34 +01:00
|
|
|
)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def sendlinkmsg(
|
|
|
|
self, netif: CoreInterface, netif2: CoreInterface, unlink: bool = False
|
|
|
|
) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Send a wireless link/unlink API message to the GUI.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param netif: interface one
|
|
|
|
:param netif2: interface two
|
|
|
|
:param unlink: unlink or not
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if unlink:
|
2017-04-25 16:45:34 +01:00
|
|
|
message_type = MessageFlags.DELETE.value
|
2013-08-29 15:21:13 +01:00
|
|
|
else:
|
2017-04-25 16:45:34 +01:00
|
|
|
message_type = MessageFlags.ADD.value
|
|
|
|
|
|
|
|
link_data = self.create_link_data(netif, netif2, message_type)
|
|
|
|
self.session.broadcast_link(link_data)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def all_link_data(self, flags: int) -> List[LinkData]:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Return a list of wireless link messages for when the GUI reconnects.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:param flags: link flags
|
|
|
|
:return: all link data
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2017-04-25 16:45:34 +01:00
|
|
|
all_links = []
|
2013-08-29 15:21:13 +01:00
|
|
|
with self.wlan._linked_lock:
|
|
|
|
for a in self.wlan._linked:
|
|
|
|
for b in self.wlan._linked[a]:
|
|
|
|
if self.wlan._linked[a][b]:
|
2017-04-25 16:45:34 +01:00
|
|
|
all_links.append(self.create_link_data(a, b, flags))
|
|
|
|
return all_links
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2019-06-11 23:07:36 +01:00
|
|
|
@total_ordering
|
2019-10-23 17:31:07 +01:00
|
|
|
class WayPoint:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Maintains information regarding waypoints.
|
|
|
|
"""
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, time: float, nodenum: int, coords, speed: float):
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Creates a WayPoint instance.
|
|
|
|
|
|
|
|
:param time: waypoint time
|
2020-01-16 19:00:57 +00:00
|
|
|
:param nodenum: node id
|
2017-05-03 17:30:49 +01:00
|
|
|
:param coords: waypoint coordinates
|
|
|
|
:param speed: waypoint speed
|
|
|
|
"""
|
|
|
|
self.time = time
|
|
|
|
self.nodenum = nodenum
|
|
|
|
self.coords = coords
|
|
|
|
self.speed = speed
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __eq__(self, other: "WayPoint") -> bool:
|
|
|
|
return (self.time, self.nodenum) == (other.time, other.nodenum)
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __ne__(self, other: "WayPoint") -> bool:
|
2019-06-11 23:07:36 +01:00
|
|
|
return not self == other
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __lt__(self, other: "WayPoint") -> bool:
|
2019-06-11 23:07:36 +01:00
|
|
|
result = self.time < other.time
|
|
|
|
if result:
|
|
|
|
result = self.nodenum < other.nodenum
|
|
|
|
return result
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
class WayPointMobility(WirelessModel):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Abstract class for mobility models that set node waypoints.
|
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
name = "waypoint"
|
|
|
|
config_type = RegisterTlvs.MOBILITY.value
|
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
STATE_STOPPED = 0
|
|
|
|
STATE_RUNNING = 1
|
|
|
|
STATE_PAUSED = 2
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, session: "Session", _id: int) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Create a WayPointMobility instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: CORE session instance
|
|
|
|
:param _id: object id
|
2017-05-03 17:30:49 +01:00
|
|
|
:return:
|
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().__init__(session=session, _id=_id)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.state = self.STATE_STOPPED
|
|
|
|
self.queue = []
|
|
|
|
self.queue_copy = []
|
|
|
|
self.points = {}
|
|
|
|
self.initial = {}
|
|
|
|
self.lasttime = None
|
|
|
|
self.endtime = None
|
2019-04-30 07:31:47 +01:00
|
|
|
self.wlan = session.get_node(_id)
|
2013-08-29 15:21:13 +01:00
|
|
|
# these are really set in child class via confmatrix
|
|
|
|
self.loop = False
|
|
|
|
self.refresh_ms = 50
|
|
|
|
# flag whether to stop scheduling when queue is empty
|
|
|
|
# (ns-3 sets this to False as new waypoints may be added from trace)
|
|
|
|
self.empty_queue_stop = True
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def runround(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Advance script time and move nodes.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.state != self.STATE_RUNNING:
|
2017-04-25 16:45:34 +01:00
|
|
|
return
|
2013-08-29 15:21:13 +01:00
|
|
|
t = self.lasttime
|
2019-12-06 17:42:41 +00:00
|
|
|
self.lasttime = time.monotonic()
|
2013-08-29 15:21:13 +01:00
|
|
|
now = self.lasttime - self.timezero
|
|
|
|
dt = self.lasttime - t
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
# keep current waypoints up-to-date
|
|
|
|
self.updatepoints(now)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if not len(self.points):
|
|
|
|
if len(self.queue):
|
|
|
|
# more future waypoints, allow time for self.lasttime update
|
|
|
|
nexttime = self.queue[0].time - now
|
|
|
|
if nexttime > (0.001 * self.refresh_ms):
|
2017-04-25 16:45:34 +01:00
|
|
|
nexttime -= 0.001 * self.refresh_ms
|
2017-08-02 22:07:56 +01:00
|
|
|
self.session.event_loop.add_event(nexttime, self.runround)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
|
|
|
else:
|
|
|
|
# no more waypoints or queued items, loop?
|
|
|
|
if not self.empty_queue_stop:
|
|
|
|
# keep running every refresh_ms, even with empty queue
|
2019-09-10 23:10:24 +01:00
|
|
|
self.session.event_loop.add_event(
|
|
|
|
0.001 * self.refresh_ms, self.runround
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
|
|
|
if not self.loopwaypoints():
|
|
|
|
return self.stop(move_initial=False)
|
|
|
|
if not len(self.queue):
|
|
|
|
# prevent busy loop
|
|
|
|
return
|
|
|
|
return self.run()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
# only move netifs attached to self.wlan, or all nodenum in script?
|
|
|
|
moved = []
|
|
|
|
moved_netifs = []
|
|
|
|
for netif in self.wlan.netifs():
|
|
|
|
node = netif.node
|
|
|
|
if self.movenode(node, dt):
|
|
|
|
moved.append(node)
|
|
|
|
moved_netifs.append(netif)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
# calculate all ranges after moving nodes; this saves calculations
|
|
|
|
self.session.mobility.updatewlans(moved, moved_netifs)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
# TODO: check session state
|
2017-08-02 22:07:56 +01:00
|
|
|
self.session.event_loop.add_event(0.001 * self.refresh_ms, self.runround)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def run(self) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Run the waypoint mobility scenario.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("running mobility scenario")
|
2019-12-06 17:42:41 +00:00
|
|
|
self.timezero = time.monotonic()
|
2013-08-29 15:21:13 +01:00
|
|
|
self.lasttime = self.timezero - (0.001 * self.refresh_ms)
|
|
|
|
self.movenodesinitial()
|
|
|
|
self.runround()
|
|
|
|
self.session.mobility.sendevent(self)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def movenode(self, node: CoreNode, dt: float) -> bool:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Calculate next node location and update its coordinates.
|
|
|
|
Returns True if the node's position has changed.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param node: node to move
|
2017-05-03 17:30:49 +01:00
|
|
|
:param dt: move factor
|
|
|
|
:return: True if node was moved, False otherwise
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2019-04-27 06:07:51 +01:00
|
|
|
if node.id not in self.points:
|
2013-08-29 15:21:13 +01:00
|
|
|
return False
|
|
|
|
x1, y1, z1 = node.getposition()
|
2019-04-27 06:07:51 +01:00
|
|
|
x2, y2, z2 = self.points[node.id].coords
|
|
|
|
speed = self.points[node.id].speed
|
2013-08-29 15:21:13 +01:00
|
|
|
# instantaneous move (prevents dx/dy == 0.0 below)
|
|
|
|
if speed == 0:
|
|
|
|
self.setnodeposition(node, x2, y2, z2)
|
2019-04-27 06:07:51 +01:00
|
|
|
del self.points[node.id]
|
2013-08-29 15:21:13 +01:00
|
|
|
return True
|
2020-01-10 00:36:00 +00:00
|
|
|
# speed can be a velocity vector or speed value
|
2013-08-29 15:21:13 +01:00
|
|
|
if isinstance(speed, (float, int)):
|
|
|
|
# linear speed value
|
|
|
|
alpha = math.atan2(y2 - y1, x2 - x1)
|
|
|
|
sx = speed * math.cos(alpha)
|
|
|
|
sy = speed * math.sin(alpha)
|
|
|
|
else:
|
|
|
|
# velocity vector
|
|
|
|
sx = speed[0]
|
|
|
|
sy = speed[1]
|
|
|
|
|
|
|
|
# calculate dt * speed = distance moved
|
|
|
|
dx = sx * dt
|
|
|
|
dy = sy * dt
|
|
|
|
# prevent overshoot
|
|
|
|
if abs(dx) > abs(x2 - x1):
|
|
|
|
dx = x2 - x1
|
|
|
|
if abs(dy) > abs(y2 - y1):
|
|
|
|
dy = y2 - y1
|
|
|
|
if dx == 0.0 and dy == 0.0:
|
|
|
|
if self.endtime < (self.lasttime - self.timezero):
|
|
|
|
# the last node to reach the last waypoint determines this
|
|
|
|
# script's endtime
|
|
|
|
self.endtime = self.lasttime - self.timezero
|
2019-04-27 06:07:51 +01:00
|
|
|
del self.points[node.id]
|
2013-08-29 15:21:13 +01:00
|
|
|
return False
|
|
|
|
if (x1 + dx) < 0.0:
|
|
|
|
dx = 0.0 - x1
|
|
|
|
if (y1 + dy) < 0.0:
|
|
|
|
dy = 0.0 - y1
|
|
|
|
self.setnodeposition(node, x1 + dx, y1 + dy, z1)
|
|
|
|
return True
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def movenodesinitial(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Move nodes to their initial positions. Then calculate the ranges.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
moved = []
|
|
|
|
moved_netifs = []
|
|
|
|
for netif in self.wlan.netifs():
|
|
|
|
node = netif.node
|
2019-04-27 06:07:51 +01:00
|
|
|
if node.id not in self.initial:
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
2019-04-27 06:07:51 +01:00
|
|
|
x, y, z = self.initial[node.id].coords
|
2013-08-29 15:21:13 +01:00
|
|
|
self.setnodeposition(node, x, y, z)
|
|
|
|
moved.append(node)
|
|
|
|
moved_netifs.append(netif)
|
|
|
|
self.session.mobility.updatewlans(moved, moved_netifs)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def addwaypoint(
|
|
|
|
self, _time: float, nodenum: int, x: float, y: float, z: float, speed: float
|
|
|
|
) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Waypoints are pushed to a heapq, sorted by time.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
:param _time: waypoint time
|
2020-01-16 19:00:57 +00:00
|
|
|
:param nodenum: node id
|
2017-05-03 17:30:49 +01:00
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
|
|
|
:param speed: speed
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2020-01-14 06:15:44 +00:00
|
|
|
wp = WayPoint(_time, nodenum, coords=(x, y, z), speed=speed)
|
2013-08-29 15:21:13 +01:00
|
|
|
heapq.heappush(self.queue, wp)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def addinitial(self, nodenum: int, x: float, y: float, z: float) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Record initial position in a dict.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param nodenum: node id
|
2017-05-03 17:30:49 +01:00
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2017-05-03 17:30:49 +01:00
|
|
|
wp = WayPoint(0, nodenum, coords=(x, y, z), speed=0)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.initial[nodenum] = wp
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def updatepoints(self, now: float) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Move items from self.queue to self.points when their time has come.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param now: current timestamp
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
while len(self.queue):
|
|
|
|
if self.queue[0].time > now:
|
2017-04-25 16:45:34 +01:00
|
|
|
break
|
2013-08-29 15:21:13 +01:00
|
|
|
wp = heapq.heappop(self.queue)
|
|
|
|
self.points[wp.nodenum] = wp
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def copywaypoints(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Store backup copy of waypoints for looping and stopping.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.queue_copy = list(self.queue)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def loopwaypoints(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Restore backup copy of waypoints when looping.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.queue = list(self.queue_copy)
|
|
|
|
return self.loop
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def setnodeposition(self, node: CoreNode, x: float, y: float, z: float) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Helper to move a node, notify any GUI (connected session handlers),
|
|
|
|
without invoking the interface poshook callback that may perform
|
|
|
|
range calculation.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param node: node to set position for
|
2017-05-03 17:30:49 +01:00
|
|
|
:param x: x position
|
|
|
|
:param y: y position
|
|
|
|
:param z: z position
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
node.position.set(x, y, z)
|
2017-04-25 16:45:34 +01:00
|
|
|
node_data = node.data(message_type=0)
|
|
|
|
self.session.broadcast_node(node_data)
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def setendtime(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
adjusted, after one round of the script has run, to be the time
|
|
|
|
that the last moving node has reached its final waypoint.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
|
|
|
self.endtime = self.queue[-1].time
|
|
|
|
except IndexError:
|
|
|
|
self.endtime = 0
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def start(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Run the script from the beginning or unpause from where it
|
|
|
|
was before.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
laststate = self.state
|
|
|
|
self.state = self.STATE_RUNNING
|
|
|
|
if laststate == self.STATE_STOPPED or laststate == self.STATE_RUNNING:
|
|
|
|
self.loopwaypoints()
|
|
|
|
self.timezero = 0
|
|
|
|
self.lasttime = 0
|
|
|
|
self.run()
|
|
|
|
elif laststate == self.STATE_PAUSED:
|
2019-12-06 17:42:41 +00:00
|
|
|
now = time.monotonic()
|
2013-08-29 15:21:13 +01:00
|
|
|
self.timezero += now - self.lasttime
|
|
|
|
self.lasttime = now - (0.001 * self.refresh_ms)
|
|
|
|
self.runround()
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def stop(self, move_initial: bool = True) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Stop the script and move nodes to initial positions.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param move_initial: flag to check if we should move nodes to initial
|
2020-01-14 06:15:44 +00:00
|
|
|
position
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.state = self.STATE_STOPPED
|
|
|
|
self.loopwaypoints()
|
|
|
|
self.timezero = 0
|
|
|
|
self.lasttime = 0
|
|
|
|
if move_initial:
|
|
|
|
self.movenodesinitial()
|
|
|
|
self.session.mobility.sendevent(self)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def pause(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Pause the script; pause time is stored to self.lasttime.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.state = self.STATE_PAUSED
|
2019-12-06 17:42:41 +00:00
|
|
|
self.lasttime = time.monotonic()
|
2013-08-29 15:21:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Ns2ScriptedMobility(WayPointMobility):
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Handles the ns-2 script format, generated by scengen/setdest or
|
|
|
|
BonnMotion.
|
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
|
2017-04-25 16:45:34 +01:00
|
|
|
name = "ns2script"
|
2018-06-14 16:41:48 +01:00
|
|
|
options = [
|
2019-09-10 23:10:24 +01:00
|
|
|
Configuration(
|
|
|
|
_id="file", _type=ConfigDataTypes.STRING, label="mobility script file"
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="refresh_ms",
|
|
|
|
_type=ConfigDataTypes.UINT32,
|
|
|
|
default="50",
|
|
|
|
label="refresh time (ms)",
|
|
|
|
),
|
|
|
|
Configuration(
|
2020-01-24 21:19:08 +00:00
|
|
|
_id="loop", _type=ConfigDataTypes.BOOL, default="1", label="loop"
|
2019-09-10 23:10:24 +01:00
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="autostart",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
label="auto-start seconds (0.0 for runtime)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="map",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
label="node mapping (optional, e.g. 0:1,1:2,2:3)",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="script_start",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
label="script file to run upon start",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="script_pause",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
label="script file to run upon pause",
|
|
|
|
),
|
|
|
|
Configuration(
|
|
|
|
_id="script_stop",
|
|
|
|
_type=ConfigDataTypes.STRING,
|
|
|
|
label="script file to run upon stop",
|
|
|
|
),
|
2018-06-14 16:41:48 +01:00
|
|
|
]
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2018-06-06 22:51:45 +01:00
|
|
|
@classmethod
|
2020-01-14 06:15:44 +00:00
|
|
|
def config_groups(cls) -> List[ConfigGroup]:
|
2018-06-06 22:51:45 +01:00
|
|
|
return [
|
2018-06-14 16:41:48 +01:00
|
|
|
ConfigGroup("ns-2 Mobility Script Parameters", 1, len(cls.configurations()))
|
2018-06-06 22:51:45 +01:00
|
|
|
]
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def __init__(self, session: "Session", _id: int):
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Creates a Ns2ScriptedMobility instance.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param session: CORE session instance
|
|
|
|
:param _id: object id
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().__init__(session, _id)
|
2013-08-29 15:21:13 +01:00
|
|
|
self._netifs = {}
|
|
|
|
self._netifslock = threading.Lock()
|
2018-06-06 22:51:45 +01:00
|
|
|
|
2018-06-13 19:59:50 +01:00
|
|
|
self.file = None
|
|
|
|
self.refresh_ms = None
|
|
|
|
self.loop = None
|
|
|
|
self.autostart = None
|
|
|
|
self.nodemap = {}
|
|
|
|
self.script_start = None
|
|
|
|
self.script_pause = None
|
|
|
|
self.script_stop = None
|
2018-06-07 23:32:16 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def update_config(self, config: Dict[str, str]) -> None:
|
2018-06-07 23:32:16 +01:00
|
|
|
self.file = config["file"]
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.info(
|
|
|
|
"ns-2 scripted mobility configured for WLAN %d using file: %s",
|
|
|
|
self.id,
|
|
|
|
self.file,
|
|
|
|
)
|
2018-06-07 23:32:16 +01:00
|
|
|
self.refresh_ms = int(config["refresh_ms"])
|
|
|
|
self.loop = config["loop"].lower() == "on"
|
|
|
|
self.autostart = config["autostart"]
|
|
|
|
self.parsemap(config["map"])
|
|
|
|
self.script_start = config["script_start"]
|
|
|
|
self.script_pause = config["script_pause"]
|
|
|
|
self.script_stop = config["script_stop"]
|
2013-08-29 15:21:13 +01:00
|
|
|
self.readscriptfile()
|
|
|
|
self.copywaypoints()
|
|
|
|
self.setendtime()
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def readscriptfile(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Read in mobility script from a file. This adds waypoints to a
|
|
|
|
priority queue, sorted by waypoint time. Initial waypoints are
|
|
|
|
stored in a separate dict.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
filename = self.findfile(self.file)
|
|
|
|
try:
|
2018-06-11 20:26:12 +01:00
|
|
|
f = open(filename, "r")
|
2017-04-25 16:45:34 +01:00
|
|
|
except IOError:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.exception(
|
|
|
|
"ns-2 scripted mobility failed to load file: %s", self.file
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
2019-10-18 18:33:31 +01:00
|
|
|
logging.info("reading ns-2 script file: %s", filename)
|
2013-08-29 15:21:13 +01:00
|
|
|
ln = 0
|
|
|
|
ix = iy = iz = None
|
|
|
|
inodenum = None
|
|
|
|
for line in f:
|
|
|
|
ln += 1
|
2019-09-10 23:10:24 +01:00
|
|
|
if line[:2] != "$n":
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
try:
|
|
|
|
if line[:8] == "$ns_ at ":
|
|
|
|
if ix is not None and iy is not None:
|
|
|
|
self.addinitial(self.map(inodenum), ix, iy, iz)
|
|
|
|
ix = iy = iz = None
|
|
|
|
# waypoints:
|
|
|
|
# $ns_ at 1.00 "$node_(6) setdest 500.0 178.0 25.0"
|
|
|
|
parts = line.split()
|
|
|
|
time = float(parts[2])
|
2019-09-10 23:10:24 +01:00
|
|
|
nodenum = parts[3][1 + parts[3].index("(") : parts[3].index(")")]
|
2013-08-29 15:21:13 +01:00
|
|
|
x = float(parts[5])
|
|
|
|
y = float(parts[6])
|
|
|
|
z = None
|
|
|
|
speed = float(parts[7].strip('"'))
|
|
|
|
self.addwaypoint(time, self.map(nodenum), x, y, z, speed)
|
|
|
|
elif line[:7] == "$node_(":
|
|
|
|
# initial position (time=0, speed=0):
|
|
|
|
# $node_(6) set X_ 780.0
|
|
|
|
parts = line.split()
|
2019-09-10 23:10:24 +01:00
|
|
|
nodenum = parts[0][1 + parts[0].index("(") : parts[0].index(")")]
|
|
|
|
if parts[2] == "X_":
|
2013-08-29 15:21:13 +01:00
|
|
|
if ix is not None and iy is not None:
|
|
|
|
self.addinitial(self.map(inodenum), ix, iy, iz)
|
|
|
|
ix = iy = iz = None
|
|
|
|
ix = float(parts[3])
|
2019-09-10 23:10:24 +01:00
|
|
|
elif parts[2] == "Y_":
|
2013-08-29 15:21:13 +01:00
|
|
|
iy = float(parts[3])
|
2019-09-10 23:10:24 +01:00
|
|
|
elif parts[2] == "Z_":
|
2013-08-29 15:21:13 +01:00
|
|
|
iz = float(parts[3])
|
|
|
|
self.addinitial(self.map(nodenum), ix, iy, iz)
|
|
|
|
ix = iy = iz = None
|
|
|
|
inodenum = nodenum
|
|
|
|
else:
|
|
|
|
raise ValueError
|
2017-04-25 16:45:34 +01:00
|
|
|
except ValueError:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.exception(
|
|
|
|
"skipping line %d of file %s '%s'", ln, self.file, line
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
continue
|
|
|
|
if ix is not None and iy is not None:
|
|
|
|
self.addinitial(self.map(inodenum), ix, iy, iz)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def findfile(self, file_name: str) -> str:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Locate a script file. If the specified file doesn't exist, look in the
|
2017-08-02 22:07:56 +01:00
|
|
|
same directory as the scenario file, or in the default
|
2017-04-25 16:45:34 +01:00
|
|
|
configs directory (~/.core/configs). This allows for sample files without
|
2017-08-02 22:07:56 +01:00
|
|
|
absolute path names.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param file_name: file name to find
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: absolute path to the file
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2017-05-03 17:30:49 +01:00
|
|
|
if os.path.exists(file_name):
|
|
|
|
return file_name
|
|
|
|
|
2017-08-02 22:07:56 +01:00
|
|
|
if self.session.file_name is not None:
|
|
|
|
d = os.path.dirname(self.session.file_name)
|
2017-05-03 17:30:49 +01:00
|
|
|
sessfn = os.path.join(d, file_name)
|
2017-04-25 16:45:34 +01:00
|
|
|
if os.path.exists(sessfn):
|
2013-08-29 15:21:13 +01:00
|
|
|
return sessfn
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2013-08-29 15:21:13 +01:00
|
|
|
if self.session.user is not None:
|
2019-09-10 23:10:24 +01:00
|
|
|
userfn = os.path.join(
|
|
|
|
"/home", self.session.user, ".core", "configs", file_name
|
|
|
|
)
|
2017-04-25 16:45:34 +01:00
|
|
|
if os.path.exists(userfn):
|
2013-08-29 15:21:13 +01:00
|
|
|
return userfn
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
return file_name
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def parsemap(self, mapstr: str) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Parse a node mapping string, given as a configuration parameter.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param mapstr: mapping string to parse
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
self.nodemap = {}
|
2017-05-03 17:30:49 +01:00
|
|
|
if mapstr.strip() == "":
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
for pair in mapstr.split(","):
|
|
|
|
parts = pair.split(":")
|
2013-08-29 15:21:13 +01:00
|
|
|
try:
|
|
|
|
if len(parts) != 2:
|
|
|
|
raise ValueError
|
|
|
|
self.nodemap[int(parts[0])] = int(parts[1])
|
|
|
|
except ValueError:
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.exception("ns-2 mobility node map error")
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def map(self, nodenum: str) -> int:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Map one node number (from a script file) to another.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param nodenum: node id to map
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: mapped value or the node id itself
|
2020-01-17 00:12:01 +00:00
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
nodenum = int(nodenum)
|
2018-04-26 22:26:39 +01:00
|
|
|
return self.nodemap.get(nodenum, nodenum)
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def startup(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Start running the script if autostart is enabled.
|
|
|
|
Move node to initial positions when any autostart time is specified.
|
|
|
|
Ignore the script if autostart is an empty string (can still be
|
|
|
|
started via GUI controls).
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-09-10 23:10:24 +01:00
|
|
|
if self.autostart == "":
|
2019-10-18 18:33:31 +01:00
|
|
|
logging.info("not auto-starting ns-2 script for %s", self.wlan.name)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
|
|
|
try:
|
|
|
|
t = float(self.autostart)
|
|
|
|
except ValueError:
|
2019-09-10 23:10:24 +01:00
|
|
|
logging.exception(
|
|
|
|
"Invalid auto-start seconds specified '%s' for %s",
|
|
|
|
self.autostart,
|
|
|
|
self.wlan.name,
|
|
|
|
)
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
|
|
|
self.movenodesinitial()
|
2019-10-18 18:33:31 +01:00
|
|
|
logging.info("scheduling ns-2 script for %s autostart at %s", self.wlan.name, t)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.state = self.STATE_RUNNING
|
2017-08-02 22:07:56 +01:00
|
|
|
self.session.event_loop.add_event(t, self.run)
|
2013-08-29 15:21:13 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def start(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Handle the case when un-paused.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-02-16 17:50:19 +00:00
|
|
|
logging.info("starting script")
|
2013-08-29 15:21:13 +01:00
|
|
|
laststate = self.state
|
2019-10-23 17:51:52 +01:00
|
|
|
super().start()
|
2013-08-29 15:21:13 +01:00
|
|
|
if laststate == self.STATE_PAUSED:
|
|
|
|
self.statescript("unpause")
|
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def run(self) -> None:
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
|
|
|
Start is pressed or autostart is triggered.
|
2017-05-03 17:30:49 +01:00
|
|
|
|
|
|
|
:return: nothing
|
2017-04-25 16:45:34 +01:00
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().run()
|
2013-08-29 15:21:13 +01:00
|
|
|
self.statescript("run")
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def pause(self) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Pause the mobility script.
|
|
|
|
|
|
|
|
:return: nothing
|
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().pause()
|
2013-08-29 15:21:13 +01:00
|
|
|
self.statescript("pause")
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def stop(self, move_initial: bool = True) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
Stop the mobility script.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param move_initial: flag to check if we should move node to initial
|
2020-01-14 06:15:44 +00:00
|
|
|
position
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2019-10-23 17:51:52 +01:00
|
|
|
super().stop(move_initial=move_initial)
|
2013-08-29 15:21:13 +01:00
|
|
|
self.statescript("stop")
|
2017-04-25 16:45:34 +01:00
|
|
|
|
2020-01-14 06:15:44 +00:00
|
|
|
def statescript(self, typestr: str) -> None:
|
2017-05-03 17:30:49 +01:00
|
|
|
"""
|
|
|
|
State of the mobility script.
|
|
|
|
|
2020-01-16 19:00:57 +00:00
|
|
|
:param typestr: state type string
|
2017-05-03 17:30:49 +01:00
|
|
|
:return: nothing
|
|
|
|
"""
|
2013-08-29 15:21:13 +01:00
|
|
|
filename = None
|
|
|
|
if typestr == "run" or typestr == "unpause":
|
|
|
|
filename = self.script_start
|
|
|
|
elif typestr == "pause":
|
|
|
|
filename = self.script_pause
|
|
|
|
elif typestr == "stop":
|
|
|
|
filename = self.script_stop
|
2019-09-10 23:10:24 +01:00
|
|
|
if filename is None or filename == "":
|
2013-08-29 15:21:13 +01:00
|
|
|
return
|
|
|
|
filename = self.findfile(filename)
|
2019-10-18 18:33:31 +01:00
|
|
|
args = f"/bin/sh {filename} {typestr}"
|
2019-10-21 18:32:42 +01:00
|
|
|
utils.cmd(
|
2019-09-10 23:10:24 +01:00
|
|
|
args, cwd=self.session.session_dir, env=self.session.get_environment()
|
|
|
|
)
|