daemon/gui: added support to configure wireless network for position calculations or not
This commit is contained in:
parent
d124820a86
commit
d20cb1ef58
10 changed files with 216 additions and 15 deletions
|
@ -20,6 +20,7 @@ from core.api.grpc.configservices_pb2 import (
|
||||||
from core.api.grpc.core_pb2 import (
|
from core.api.grpc.core_pb2 import (
|
||||||
ExecuteScriptRequest,
|
ExecuteScriptRequest,
|
||||||
GetConfigRequest,
|
GetConfigRequest,
|
||||||
|
GetWirelessConfigRequest,
|
||||||
LinkedRequest,
|
LinkedRequest,
|
||||||
WirelessConfigRequest,
|
WirelessConfigRequest,
|
||||||
WirelessLinkedRequest,
|
WirelessLinkedRequest,
|
||||||
|
@ -1142,6 +1143,13 @@ class CoreGrpcClient:
|
||||||
)
|
)
|
||||||
self.stub.WirelessConfig(request)
|
self.stub.WirelessConfig(request)
|
||||||
|
|
||||||
|
def get_wireless_config(
|
||||||
|
self, session_id: int, node_id: int
|
||||||
|
) -> Dict[str, wrappers.ConfigOption]:
|
||||||
|
request = GetWirelessConfigRequest(session_id=session_id, node_id=node_id)
|
||||||
|
response = self.stub.GetWirelessConfig(request)
|
||||||
|
return wrappers.ConfigOption.from_dict(response.config)
|
||||||
|
|
||||||
def connect(self) -> None:
|
def connect(self) -> None:
|
||||||
"""
|
"""
|
||||||
Open connection to server, must be closed manually.
|
Open connection to server, must be closed manually.
|
||||||
|
|
|
@ -29,6 +29,7 @@ from core.nodes.docker import DockerNode
|
||||||
from core.nodes.interface import CoreInterface
|
from core.nodes.interface import CoreInterface
|
||||||
from core.nodes.lxd import LxcNode
|
from core.nodes.lxd import LxcNode
|
||||||
from core.nodes.network import CoreNetwork, CtrlNet, PtpNet, WlanNode
|
from core.nodes.network import CoreNetwork, CtrlNet, PtpNet, WlanNode
|
||||||
|
from core.nodes.wireless import WirelessNode
|
||||||
from core.services.coreservices import CoreService
|
from core.services.coreservices import CoreService
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -827,6 +828,9 @@ def configure_node(
|
||||||
if node.mobility_config:
|
if node.mobility_config:
|
||||||
config = {k: v.value for k, v in node.mobility_config.items()}
|
config = {k: v.value for k, v in node.mobility_config.items()}
|
||||||
session.mobility.set_model_config(node.id, Ns2ScriptedMobility.name, config)
|
session.mobility.set_model_config(node.id, Ns2ScriptedMobility.name, config)
|
||||||
|
if isinstance(core_node, WirelessNode) and node.wireless_config:
|
||||||
|
config = {k: v.value for k, v in node.wireless_config.items()}
|
||||||
|
core_node.set_config(config)
|
||||||
for service_name, service_config in node.service_configs.items():
|
for service_name, service_config in node.service_configs.items():
|
||||||
data = service_config.data
|
data = service_config.data
|
||||||
config = ServiceConfig(
|
config = ServiceConfig(
|
||||||
|
|
|
@ -30,6 +30,8 @@ from core.api.grpc.configservices_pb2 import (
|
||||||
)
|
)
|
||||||
from core.api.grpc.core_pb2 import (
|
from core.api.grpc.core_pb2 import (
|
||||||
ExecuteScriptResponse,
|
ExecuteScriptResponse,
|
||||||
|
GetWirelessConfigRequest,
|
||||||
|
GetWirelessConfigResponse,
|
||||||
LinkedRequest,
|
LinkedRequest,
|
||||||
LinkedResponse,
|
LinkedResponse,
|
||||||
WirelessConfigRequest,
|
WirelessConfigRequest,
|
||||||
|
@ -1382,3 +1384,25 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
options2 = grpcutils.convert_options_proto(options2)
|
options2 = grpcutils.convert_options_proto(options2)
|
||||||
wireless.link_config(request.node1_id, request.node2_id, options1, options2)
|
wireless.link_config(request.node1_id, request.node2_id, options1, options2)
|
||||||
return WirelessConfigResponse()
|
return WirelessConfigResponse()
|
||||||
|
|
||||||
|
def GetWirelessConfig(
|
||||||
|
self, request: GetWirelessConfigRequest, context: ServicerContext
|
||||||
|
) -> GetWirelessConfigResponse:
|
||||||
|
session = self.get_session(request.session_id, context)
|
||||||
|
try:
|
||||||
|
wireless = session.get_node(request.node_id, WirelessNode)
|
||||||
|
configs = wireless.get_config()
|
||||||
|
except CoreError:
|
||||||
|
configs = {x.id: x for x in WirelessNode.options}
|
||||||
|
config_options = {}
|
||||||
|
for config in configs.values():
|
||||||
|
config_option = common_pb2.ConfigOption(
|
||||||
|
label=config.label,
|
||||||
|
name=config.id,
|
||||||
|
value=config.default,
|
||||||
|
type=config.type.value,
|
||||||
|
select=config.options,
|
||||||
|
group=config.group,
|
||||||
|
)
|
||||||
|
config_options[config.id] = config_option
|
||||||
|
return GetWirelessConfigResponse(config=config_options)
|
||||||
|
|
|
@ -737,6 +737,7 @@ class Node:
|
||||||
Tuple[str, Optional[int]], Dict[str, ConfigOption]
|
Tuple[str, Optional[int]], Dict[str, ConfigOption]
|
||||||
] = field(default_factory=dict, repr=False)
|
] = field(default_factory=dict, repr=False)
|
||||||
wlan_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
wlan_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
||||||
|
wireless_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
||||||
mobility_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
mobility_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False)
|
||||||
service_configs: Dict[str, NodeServiceData] = field(
|
service_configs: Dict[str, NodeServiceData] = field(
|
||||||
default_factory=dict, repr=False
|
default_factory=dict, repr=False
|
||||||
|
@ -789,6 +790,7 @@ class Node:
|
||||||
service_file_configs=service_file_configs,
|
service_file_configs=service_file_configs,
|
||||||
config_service_configs=config_service_configs,
|
config_service_configs=config_service_configs,
|
||||||
emane_model_configs=emane_configs,
|
emane_model_configs=emane_configs,
|
||||||
|
wireless_config=ConfigOption.from_dict(proto.wireless_config),
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_proto(self) -> core_pb2.Node:
|
def to_proto(self) -> core_pb2.Node:
|
||||||
|
@ -840,6 +842,7 @@ class Node:
|
||||||
service_configs=service_configs,
|
service_configs=service_configs,
|
||||||
config_service_configs=config_service_configs,
|
config_service_configs=config_service_configs,
|
||||||
emane_configs=emane_configs,
|
emane_configs=emane_configs,
|
||||||
|
wireless_config={k: v.to_proto() for k, v in self.wireless_config.items()},
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_wlan(self, config: Dict[str, str]) -> None:
|
def set_wlan(self, config: Dict[str, str]) -> None:
|
||||||
|
|
|
@ -44,6 +44,7 @@ class Configuration:
|
||||||
label: str = None
|
label: str = None
|
||||||
default: str = ""
|
default: str = ""
|
||||||
options: List[str] = field(default_factory=list)
|
options: List[str] = field(default_factory=list)
|
||||||
|
group: str = "Configuration"
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
self.label = self.label if self.label else self.id
|
self.label = self.label if self.label else self.id
|
||||||
|
@ -78,6 +79,7 @@ class ConfigBool(Configuration):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: ConfigDataTypes = ConfigDataTypes.BOOL
|
type: ConfigDataTypes = ConfigDataTypes.BOOL
|
||||||
|
value: bool = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -87,6 +89,7 @@ class ConfigFloat(Configuration):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: ConfigDataTypes = ConfigDataTypes.FLOAT
|
type: ConfigDataTypes = ConfigDataTypes.FLOAT
|
||||||
|
value: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -96,6 +99,7 @@ class ConfigInt(Configuration):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: ConfigDataTypes = ConfigDataTypes.INT32
|
type: ConfigDataTypes = ConfigDataTypes.INT32
|
||||||
|
value: int = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -105,6 +109,7 @@ class ConfigString(Configuration):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: ConfigDataTypes = ConfigDataTypes.STRING
|
type: ConfigDataTypes = ConfigDataTypes.STRING
|
||||||
|
value: str = ""
|
||||||
|
|
||||||
|
|
||||||
class ConfigurableOptions:
|
class ConfigurableOptions:
|
||||||
|
|
|
@ -779,6 +779,9 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
def get_wireless_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||||
|
return self.client.get_wireless_config(self.session.id, node_id)
|
||||||
|
|
||||||
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||||
config = self.client.get_mobility_config(self.session.id, node_id)
|
config = self.client.get_mobility_config(self.session.id, node_id)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|
55
daemon/core/gui/dialogs/wirelessconfig.py
Normal file
55
daemon/core/gui/dialogs/wirelessconfig.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
|
import grpc
|
||||||
|
|
||||||
|
from core.api.grpc.wrappers import ConfigOption, Node
|
||||||
|
from core.gui.dialogs.dialog import Dialog
|
||||||
|
from core.gui.themes import PADX, PADY
|
||||||
|
from core.gui.widgets import ConfigFrame
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from core.gui.app import Application
|
||||||
|
from core.gui.graph.node import CanvasNode
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessConfigDialog(Dialog):
|
||||||
|
def __init__(self, app: "Application", canvas_node: "CanvasNode"):
|
||||||
|
super().__init__(app, f"Wireless Configuration - {canvas_node.core_node.name}")
|
||||||
|
self.node: Node = canvas_node.core_node
|
||||||
|
self.config_frame: Optional[ConfigFrame] = None
|
||||||
|
self.config: Dict[str, ConfigOption] = {}
|
||||||
|
try:
|
||||||
|
config = self.node.wireless_config
|
||||||
|
if not config:
|
||||||
|
config = self.app.core.get_wireless_config(self.node.id)
|
||||||
|
self.config: Dict[str, ConfigOption] = config
|
||||||
|
self.draw()
|
||||||
|
except grpc.RpcError as e:
|
||||||
|
self.app.show_grpc_exception("Wireless Config Error", e)
|
||||||
|
self.has_error: bool = True
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def draw(self) -> None:
|
||||||
|
self.top.columnconfigure(0, weight=1)
|
||||||
|
self.top.rowconfigure(0, weight=1)
|
||||||
|
self.config_frame = ConfigFrame(self.top, self.app, self.config)
|
||||||
|
self.config_frame.draw_config()
|
||||||
|
self.config_frame.grid(sticky=tk.NSEW, pady=PADY)
|
||||||
|
self.draw_buttons()
|
||||||
|
|
||||||
|
def draw_buttons(self) -> None:
|
||||||
|
frame = ttk.Frame(self.top)
|
||||||
|
frame.grid(sticky=tk.EW)
|
||||||
|
for i in range(2):
|
||||||
|
frame.columnconfigure(i, weight=1)
|
||||||
|
button = ttk.Button(frame, text="Apply", command=self.click_apply)
|
||||||
|
button.grid(row=0, column=0, padx=PADX, sticky=tk.EW)
|
||||||
|
button = ttk.Button(frame, text="Cancel", command=self.destroy)
|
||||||
|
button.grid(row=0, column=1, sticky=tk.EW)
|
||||||
|
|
||||||
|
def click_apply(self) -> None:
|
||||||
|
self.config_frame.parse_config()
|
||||||
|
self.node.wireless_config = self.config
|
||||||
|
self.destroy()
|
|
@ -16,6 +16,7 @@ from core.gui.dialogs.mobilityconfig import MobilityConfigDialog
|
||||||
from core.gui.dialogs.nodeconfig import NodeConfigDialog
|
from core.gui.dialogs.nodeconfig import NodeConfigDialog
|
||||||
from core.gui.dialogs.nodeconfigservice import NodeConfigServiceDialog
|
from core.gui.dialogs.nodeconfigservice import NodeConfigServiceDialog
|
||||||
from core.gui.dialogs.nodeservice import NodeServiceDialog
|
from core.gui.dialogs.nodeservice import NodeServiceDialog
|
||||||
|
from core.gui.dialogs.wirelessconfig import WirelessConfigDialog
|
||||||
from core.gui.dialogs.wlanconfig import WlanConfigDialog
|
from core.gui.dialogs.wlanconfig import WlanConfigDialog
|
||||||
from core.gui.frames.node import NodeInfoFrame
|
from core.gui.frames.node import NodeInfoFrame
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
|
@ -219,6 +220,7 @@ class CanvasNode:
|
||||||
# clear existing menu
|
# clear existing menu
|
||||||
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_wireless = self.core_node.type == NodeType.WIRELESS
|
||||||
is_emane = self.core_node.type == NodeType.EMANE
|
is_emane = self.core_node.type == NodeType.EMANE
|
||||||
is_mobility = is_wlan or is_emane
|
is_mobility = is_wlan or is_emane
|
||||||
if self.app.core.is_runtime():
|
if self.app.core.is_runtime():
|
||||||
|
@ -231,6 +233,10 @@ 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_wireless:
|
||||||
|
self.context.add_command(
|
||||||
|
label="Wireless Config", command=self.show_wireless_config
|
||||||
|
)
|
||||||
if is_mobility 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
|
||||||
|
@ -268,6 +274,10 @@ 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_wireless:
|
||||||
|
self.context.add_command(
|
||||||
|
label="Wireless Config", command=self.show_wireless_config
|
||||||
|
)
|
||||||
if is_mobility:
|
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
|
||||||
|
@ -346,6 +356,10 @@ class CanvasNode:
|
||||||
dialog = NodeConfigDialog(self.app, self)
|
dialog = NodeConfigDialog(self.app, self)
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
|
def show_wireless_config(self) -> None:
|
||||||
|
dialog = WirelessConfigDialog(self.app, self)
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
def show_wlan_config(self) -> None:
|
def show_wlan_config(self) -> None:
|
||||||
dialog = WlanConfigDialog(self.app, self)
|
dialog = WlanConfigDialog(self.app, self)
|
||||||
if not dialog.has_error:
|
if not dialog.has_error:
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
Defines a wireless node that allows programmatic link connectivity and
|
Defines a wireless node that allows programmatic link connectivity and
|
||||||
configuration between pairs of nodes.
|
configuration between pairs of nodes.
|
||||||
"""
|
"""
|
||||||
|
import copy
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, Dict, Tuple
|
from typing import TYPE_CHECKING, Dict, List, Tuple
|
||||||
|
|
||||||
|
from core.config import ConfigBool, ConfigFloat, Configuration
|
||||||
from core.emulator.data import LinkData, LinkOptions
|
from core.emulator.data import LinkData, LinkOptions
|
||||||
from core.emulator.enumerations import LinkTypes, MessageFlags
|
from core.emulator.enumerations import LinkTypes, MessageFlags
|
||||||
from core.errors import CoreError
|
from core.errors import CoreError
|
||||||
|
@ -20,6 +21,18 @@ if TYPE_CHECKING:
|
||||||
from core.emulator.distributed import DistributedServer
|
from core.emulator.distributed import DistributedServer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
CONFIG_ENABLED: bool = True
|
||||||
|
CONFIG_RANGE: float = 400.0
|
||||||
|
CONFIG_LOSS_RANGE: float = 300.0
|
||||||
|
CONFIG_LOSS_FACTOR: float = 1.0
|
||||||
|
CONFIG_DELAY_RANGE: float = 200.0
|
||||||
|
CONFIG_DELAY_FACTOR: float = 1.0
|
||||||
|
KEY_ENABLED: str = "movement"
|
||||||
|
KEY_RANGE: str = "max-range"
|
||||||
|
KEY_LOSS_RANGE: str = "loss-range"
|
||||||
|
KEY_LOSS_FACTOR: str = "loss-factor"
|
||||||
|
KEY_DELAY_RANGE: str = "delay-range"
|
||||||
|
KEY_DELAY_FACTOR: str = "delay-factor"
|
||||||
|
|
||||||
|
|
||||||
def calc_distance(
|
def calc_distance(
|
||||||
|
@ -47,6 +60,33 @@ class WirelessLink:
|
||||||
|
|
||||||
|
|
||||||
class WirelessNode(CoreNetworkBase):
|
class WirelessNode(CoreNetworkBase):
|
||||||
|
options: List[Configuration] = [
|
||||||
|
ConfigBool(
|
||||||
|
id=KEY_ENABLED,
|
||||||
|
default="1" if CONFIG_ENABLED else "0",
|
||||||
|
label="Movement Enabled?",
|
||||||
|
),
|
||||||
|
ConfigFloat(
|
||||||
|
id=KEY_RANGE, default=str(CONFIG_RANGE), label="Max Range (pixels)"
|
||||||
|
),
|
||||||
|
ConfigFloat(
|
||||||
|
id=KEY_LOSS_RANGE,
|
||||||
|
default=str(CONFIG_LOSS_RANGE),
|
||||||
|
label="Loss Start Range (pixels)",
|
||||||
|
),
|
||||||
|
ConfigFloat(
|
||||||
|
id=KEY_LOSS_FACTOR, default=str(CONFIG_LOSS_FACTOR), label="Loss Factor"
|
||||||
|
),
|
||||||
|
ConfigFloat(
|
||||||
|
id=KEY_DELAY_RANGE,
|
||||||
|
default=str(CONFIG_DELAY_RANGE),
|
||||||
|
label="Delay Start Range (pixels)",
|
||||||
|
),
|
||||||
|
ConfigFloat(
|
||||||
|
id=KEY_DELAY_FACTOR, default=str(CONFIG_DELAY_FACTOR), label="Delay Factor"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
session: "Session",
|
session: "Session",
|
||||||
|
@ -57,6 +97,12 @@ class WirelessNode(CoreNetworkBase):
|
||||||
super().__init__(session, _id, name, server)
|
super().__init__(session, _id, name, server)
|
||||||
self.bridges: Dict[int, Tuple[CoreInterface, str]] = {}
|
self.bridges: Dict[int, Tuple[CoreInterface, str]] = {}
|
||||||
self.links: Dict[Tuple[int, int], WirelessLink] = {}
|
self.links: Dict[Tuple[int, int], WirelessLink] = {}
|
||||||
|
self.position_enabled: bool = CONFIG_ENABLED
|
||||||
|
self.max_range: float = CONFIG_RANGE
|
||||||
|
self.loss_range: float = CONFIG_LOSS_RANGE
|
||||||
|
self.loss_factor: float = CONFIG_LOSS_FACTOR
|
||||||
|
self.delay_range: float = CONFIG_DELAY_RANGE
|
||||||
|
self.delay_factor: float = CONFIG_DELAY_FACTOR
|
||||||
|
|
||||||
def startup(self) -> None:
|
def startup(self) -> None:
|
||||||
if self.up:
|
if self.up:
|
||||||
|
@ -94,8 +140,9 @@ class WirelessNode(CoreNetworkBase):
|
||||||
)
|
)
|
||||||
# associate node iface with bridge
|
# associate node iface with bridge
|
||||||
iface.net_client.set_iface_master(bridge_name, iface.localname)
|
iface.net_client.set_iface_master(bridge_name, iface.localname)
|
||||||
# assign position callback
|
# assign position callback, when enabled
|
||||||
iface.poshook = self.position_callback
|
if self.position_enabled:
|
||||||
|
iface.poshook = self.position_callback
|
||||||
# save created bridge
|
# save created bridge
|
||||||
self.bridges[iface.node.id] = (iface, bridge_name)
|
self.bridges[iface.node.id] = (iface, bridge_name)
|
||||||
|
|
||||||
|
@ -226,21 +273,47 @@ class WirelessNode(CoreNetworkBase):
|
||||||
for oiface, bridge_name in self.bridges.values():
|
for oiface, bridge_name in self.bridges.values():
|
||||||
if iface == oiface:
|
if iface == oiface:
|
||||||
continue
|
continue
|
||||||
key = get_key(iface.node.id, oiface.node.id)
|
self.calc_link(iface, oiface)
|
||||||
link = self.links.get(key)
|
|
||||||
if link.linked:
|
|
||||||
self.calc_link(iface, oiface)
|
|
||||||
|
|
||||||
def calc_link(self, iface1: CoreInterface, iface2: CoreInterface) -> None:
|
def calc_link(self, iface1: CoreInterface, iface2: CoreInterface) -> None:
|
||||||
|
key = get_key(iface1.node.id, iface2.node.id)
|
||||||
|
link = self.links.get(key)
|
||||||
point1 = iface1.node.position.get()
|
point1 = iface1.node.position.get()
|
||||||
point2 = iface2.node.position.get()
|
point2 = iface2.node.position.get()
|
||||||
distance = calc_distance(point1, point2) - 250
|
distance = calc_distance(point1, point2)
|
||||||
distance = max(distance, 0.0)
|
if distance >= self.max_range:
|
||||||
loss = min((distance / 500) * 100.0, 100.0)
|
if link.linked:
|
||||||
node1_id = iface1.node.id
|
self.link_control(iface1.node.id, iface2.node.id, False)
|
||||||
node2_id = iface2.node.id
|
else:
|
||||||
options = LinkOptions(loss=loss, delay=0)
|
if not link.linked:
|
||||||
self.link_config(node1_id, node2_id, options, options)
|
self.link_control(iface1.node.id, iface2.node.id, True)
|
||||||
|
loss_distance = max(distance - self.loss_range, 0.0)
|
||||||
|
loss = min(
|
||||||
|
(loss_distance / self.max_range) * 100.0 * self.loss_factor, 100.0
|
||||||
|
)
|
||||||
|
delay_distance = max(distance - self.delay_range, 0.0)
|
||||||
|
delay = (delay_distance / self.max_range) * 100.0 * self.delay_factor
|
||||||
|
options = LinkOptions(loss=loss, delay=int(delay))
|
||||||
|
self.link_config(iface1.node.id, iface2.node.id, options, options)
|
||||||
|
|
||||||
def adopt_iface(self, iface: CoreInterface, name: str) -> None:
|
def adopt_iface(self, iface: CoreInterface, name: str) -> None:
|
||||||
raise CoreError(f"{type(self)} does not support adopt interface")
|
raise CoreError(f"{type(self)} does not support adopt interface")
|
||||||
|
|
||||||
|
def get_config(self) -> Dict[str, Configuration]:
|
||||||
|
config = {x.id: x for x in copy.copy(self.options)}
|
||||||
|
config[KEY_ENABLED].default = "1" if self.position_enabled else "0"
|
||||||
|
config[KEY_RANGE].default = str(self.max_range)
|
||||||
|
config[KEY_LOSS_RANGE].default = str(self.loss_range)
|
||||||
|
config[KEY_LOSS_FACTOR].default = str(self.loss_factor)
|
||||||
|
config[KEY_DELAY_RANGE].default = str(self.delay_range)
|
||||||
|
config[KEY_DELAY_FACTOR].default = str(self.delay_factor)
|
||||||
|
return config
|
||||||
|
|
||||||
|
def set_config(self, config: Dict[str, str]) -> None:
|
||||||
|
logger.info("wireless config: %s", config)
|
||||||
|
self.position_enabled = config[KEY_ENABLED] == "1"
|
||||||
|
self.max_range = float(config[KEY_RANGE])
|
||||||
|
self.loss_range = float(config[KEY_LOSS_RANGE])
|
||||||
|
self.loss_factor = float(config[KEY_LOSS_FACTOR])
|
||||||
|
self.delay_range = float(config[KEY_DELAY_RANGE])
|
||||||
|
self.delay_factor = float(config[KEY_DELAY_FACTOR])
|
||||||
|
|
|
@ -107,6 +107,8 @@ service CoreApi {
|
||||||
}
|
}
|
||||||
rpc WirelessConfig (WirelessConfigRequest) returns (WirelessConfigResponse) {
|
rpc WirelessConfig (WirelessConfigRequest) returns (WirelessConfigResponse) {
|
||||||
}
|
}
|
||||||
|
rpc GetWirelessConfig (GetWirelessConfigRequest) returns (GetWirelessConfigResponse) {
|
||||||
|
}
|
||||||
|
|
||||||
// emane rpc
|
// emane rpc
|
||||||
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
|
rpc GetEmaneModelConfig (emane.GetEmaneModelConfigRequest) returns (emane.GetEmaneModelConfigResponse) {
|
||||||
|
@ -624,6 +626,7 @@ message Node {
|
||||||
map<string, services.NodeServiceConfig> service_configs = 18;
|
map<string, services.NodeServiceConfig> service_configs = 18;
|
||||||
map<string, configservices.ConfigServiceConfig> config_service_configs= 19;
|
map<string, configservices.ConfigServiceConfig> config_service_configs= 19;
|
||||||
repeated emane.NodeEmaneConfig emane_configs = 20;
|
repeated emane.NodeEmaneConfig emane_configs = 20;
|
||||||
|
map<string, common.ConfigOption> wireless_config = 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Link {
|
message Link {
|
||||||
|
@ -728,3 +731,12 @@ message WirelessConfigRequest {
|
||||||
|
|
||||||
message WirelessConfigResponse {
|
message WirelessConfigResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetWirelessConfigRequest {
|
||||||
|
int32 session_id = 1;
|
||||||
|
int32 node_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetWirelessConfigResponse {
|
||||||
|
map<string, common.ConfigOption> config = 1;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue