changes to move sdt calls internal to core interactions, which allows it to work with both guis
This commit is contained in:
parent
20e3fbc7d9
commit
67da3e5c22
3 changed files with 156 additions and 295 deletions
|
@ -526,11 +526,6 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"%s handling message:\n%s", threading.currentThread().getName(), message
|
"%s handling message:\n%s", threading.currentThread().getName(), message
|
||||||
)
|
)
|
||||||
|
|
||||||
# provide to sdt, if enabled
|
|
||||||
if self.session and self.session.sdt.is_enabled():
|
|
||||||
self.session.sdt.handle_distributed(message)
|
|
||||||
|
|
||||||
if message.message_type not in self.message_handlers:
|
if message.message_type not in self.message_handlers:
|
||||||
logging.error("no handler for message type: %s", message.type_str())
|
logging.error("no handler for message type: %s", message.type_str())
|
||||||
return
|
return
|
||||||
|
@ -2042,7 +2037,6 @@ class CoreUdpHandler(CoreHandler):
|
||||||
logging.debug("session handling message: %s", session.session_id)
|
logging.debug("session handling message: %s", session.session_id)
|
||||||
self.session = session
|
self.session = session
|
||||||
self.handle_message(message)
|
self.handle_message(message)
|
||||||
self.session.sdt.handle_distributed(message)
|
|
||||||
self.broadcast(message)
|
self.broadcast(message)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logging.error(
|
||||||
|
@ -2067,7 +2061,6 @@ class CoreUdpHandler(CoreHandler):
|
||||||
if session or message.message_type == MessageTypes.REGISTER.value:
|
if session or message.message_type == MessageTypes.REGISTER.value:
|
||||||
self.session = session
|
self.session = session
|
||||||
self.handle_message(message)
|
self.handle_message(message)
|
||||||
self.session.sdt.handle_distributed(message)
|
|
||||||
self.broadcast(message)
|
self.broadcast(message)
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logging.error(
|
||||||
|
|
|
@ -432,6 +432,7 @@ class Session:
|
||||||
if node_two:
|
if node_two:
|
||||||
node_two.lock.release()
|
node_two.lock.release()
|
||||||
|
|
||||||
|
self.sdt.add_link(node_one_id, node_two_id, is_wireless=False)
|
||||||
return node_one_interface, node_two_interface
|
return node_one_interface, node_two_interface
|
||||||
|
|
||||||
def delete_link(
|
def delete_link(
|
||||||
|
@ -540,6 +541,8 @@ class Session:
|
||||||
if node_two:
|
if node_two:
|
||||||
node_two.lock.release()
|
node_two.lock.release()
|
||||||
|
|
||||||
|
self.sdt.delete_link(node_one_id, node_two_id)
|
||||||
|
|
||||||
def update_link(
|
def update_link(
|
||||||
self,
|
self,
|
||||||
node_one_id: int,
|
node_one_id: int,
|
||||||
|
@ -757,6 +760,7 @@ class Session:
|
||||||
self.add_remove_control_interface(node=node, remove=False)
|
self.add_remove_control_interface(node=node, remove=False)
|
||||||
self.services.boot_services(node)
|
self.services.boot_services(node)
|
||||||
|
|
||||||
|
self.sdt.add_node(node)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def edit_node(self, node_id: int, options: NodeOptions) -> None:
|
def edit_node(self, node_id: int, options: NodeOptions) -> None:
|
||||||
|
@ -765,7 +769,7 @@ class Session:
|
||||||
|
|
||||||
:param node_id: id of node to update
|
:param node_id: id of node to update
|
||||||
:param options: data to update node with
|
:param options: data to update node with
|
||||||
:return: True if node updated, False otherwise
|
:return: nothing
|
||||||
:raises core.CoreError: when node to update does not exist
|
:raises core.CoreError: when node to update does not exist
|
||||||
"""
|
"""
|
||||||
# get node to update
|
# get node to update
|
||||||
|
@ -778,6 +782,8 @@ class Session:
|
||||||
node.canvas = options.canvas
|
node.canvas = options.canvas
|
||||||
node.icon = options.icon
|
node.icon = options.icon
|
||||||
|
|
||||||
|
self.sdt.edit_node(node)
|
||||||
|
|
||||||
def set_node_position(self, node: NodeBase, options: NodeOptions) -> None:
|
def set_node_position(self, node: NodeBase, options: NodeOptions) -> None:
|
||||||
"""
|
"""
|
||||||
Set position for a node, use lat/lon/alt if needed.
|
Set position for a node, use lat/lon/alt if needed.
|
||||||
|
@ -1402,6 +1408,7 @@ class Session:
|
||||||
if node:
|
if node:
|
||||||
node.shutdown()
|
node.shutdown()
|
||||||
self.check_shutdown()
|
self.check_shutdown()
|
||||||
|
self.sdt.delete_node(_id)
|
||||||
|
|
||||||
return node is not None
|
return node is not None
|
||||||
|
|
||||||
|
@ -1413,6 +1420,7 @@ class Session:
|
||||||
funcs = []
|
funcs = []
|
||||||
while self.nodes:
|
while self.nodes:
|
||||||
_, node = self.nodes.popitem()
|
_, node = self.nodes.popitem()
|
||||||
|
self.sdt.delete_node(node.id)
|
||||||
funcs.append((node.shutdown, [], {}))
|
funcs.append((node.shutdown, [], {}))
|
||||||
utils.threadpool(funcs)
|
utils.threadpool(funcs)
|
||||||
self.node_id_gen.id = 0
|
self.node_id_gen.id = 0
|
||||||
|
|
|
@ -4,22 +4,15 @@ sdt.py: Scripted Display Tool (SDT3D) helper
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from typing import TYPE_CHECKING, Any, Optional
|
import threading
|
||||||
|
from typing import TYPE_CHECKING, Optional, Tuple
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from core import constants
|
from core import constants
|
||||||
from core.api.tlv.coreapi import CoreLinkMessage, CoreMessage, CoreNodeMessage
|
|
||||||
from core.constants import CORE_DATA_DIR
|
from core.constants import CORE_DATA_DIR
|
||||||
from core.emane.nodes import EmaneNet
|
from core.emane.nodes import EmaneNet
|
||||||
from core.emulator.data import LinkData, NodeData
|
from core.emulator.data import LinkData, NodeData
|
||||||
from core.emulator.enumerations import (
|
from core.emulator.enumerations import EventTypes, LinkTypes, MessageFlags
|
||||||
EventTypes,
|
|
||||||
LinkTlvs,
|
|
||||||
LinkTypes,
|
|
||||||
MessageFlags,
|
|
||||||
NodeTlvs,
|
|
||||||
NodeTypes,
|
|
||||||
)
|
|
||||||
from core.errors import CoreError
|
from core.errors import CoreError
|
||||||
from core.nodes.base import CoreNetworkBase, NodeBase
|
from core.nodes.base import CoreNetworkBase, NodeBase
|
||||||
from core.nodes.network import WlanNode
|
from core.nodes.network import WlanNode
|
||||||
|
@ -28,19 +21,11 @@ if TYPE_CHECKING:
|
||||||
from core.emulator.session import Session
|
from core.emulator.session import Session
|
||||||
|
|
||||||
|
|
||||||
# TODO: A named tuple may be more appropriate, than abusing a class dict like this
|
def link_data_params(link_data: LinkData) -> Tuple[int, int, bool]:
|
||||||
class Bunch:
|
node_one = link_data.node1_id
|
||||||
"""
|
node_two = link_data.node2_id
|
||||||
Helper class for recording a collection of attributes.
|
is_wireless = link_data.link_type == LinkTypes.WIRELESS.value
|
||||||
"""
|
return node_one, node_two, is_wireless
|
||||||
|
|
||||||
def __init__(self, **kwargs: Any) -> None:
|
|
||||||
"""
|
|
||||||
Create a Bunch instance.
|
|
||||||
|
|
||||||
:param kwargs: keyword arguments
|
|
||||||
"""
|
|
||||||
self.__dict__.update(kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class Sdt:
|
class Sdt:
|
||||||
|
@ -74,53 +59,16 @@ class Sdt:
|
||||||
:param session: session this manager is tied to
|
:param session: session this manager is tied to
|
||||||
"""
|
"""
|
||||||
self.session = session
|
self.session = session
|
||||||
|
self.lock = threading.Lock()
|
||||||
self.sock = None
|
self.sock = None
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.showerror = True
|
self.showerror = True
|
||||||
self.url = self.DEFAULT_SDT_URL
|
self.url = self.DEFAULT_SDT_URL
|
||||||
# node information for remote nodes not in session._objs
|
self.address = None
|
||||||
# local nodes also appear here since their obj may not exist yet
|
self.protocol = None
|
||||||
self.remotes = {}
|
|
||||||
|
|
||||||
# add handler for node updates
|
|
||||||
self.session.node_handlers.append(self.handle_node_update)
|
self.session.node_handlers.append(self.handle_node_update)
|
||||||
|
|
||||||
# add handler for link updates
|
|
||||||
self.session.link_handlers.append(self.handle_link_update)
|
self.session.link_handlers.append(self.handle_link_update)
|
||||||
|
|
||||||
def handle_node_update(self, node_data: NodeData) -> None:
|
|
||||||
"""
|
|
||||||
Handler for node updates, specifically for updating their location.
|
|
||||||
|
|
||||||
:param node_data: node data being updated
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
x = node_data.x_position
|
|
||||||
y = node_data.y_position
|
|
||||||
lat = node_data.latitude
|
|
||||||
lon = node_data.longitude
|
|
||||||
alt = node_data.altitude
|
|
||||||
if all([lat is not None, lon is not None, alt is not None]):
|
|
||||||
self.updatenodegeo(node_data.id, lat, lon, alt)
|
|
||||||
elif node_data.message_type == 0:
|
|
||||||
# TODO: z is not currently supported by node messages
|
|
||||||
self.updatenode(node_data.id, 0, x, y, 0)
|
|
||||||
|
|
||||||
def handle_link_update(self, link_data: LinkData) -> None:
|
|
||||||
"""
|
|
||||||
Handler for link updates, checking for wireless link/unlink messages.
|
|
||||||
|
|
||||||
:param link_data: link data being updated
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if link_data.link_type == LinkTypes.WIRELESS.value:
|
|
||||||
self.updatelink(
|
|
||||||
link_data.node1_id,
|
|
||||||
link_data.node2_id,
|
|
||||||
link_data.message_type,
|
|
||||||
wireless=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_enabled(self) -> bool:
|
def is_enabled(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check for "enablesdt" session option. Return False by default if
|
Check for "enablesdt" session option. Return False by default if
|
||||||
|
@ -137,9 +85,7 @@ class Sdt:
|
||||||
|
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
url = self.session.options.get_config("stdurl")
|
url = self.session.options.get_config("stdurl", default=self.DEFAULT_SDT_URL)
|
||||||
if not url:
|
|
||||||
url = self.DEFAULT_SDT_URL
|
|
||||||
self.url = urlparse(url)
|
self.url = urlparse(url)
|
||||||
self.address = (self.url.hostname, self.url.port)
|
self.address = (self.url.hostname, self.url.port)
|
||||||
self.protocol = self.url.scheme
|
self.protocol = self.url.scheme
|
||||||
|
@ -178,7 +124,6 @@ class Sdt:
|
||||||
# refresh all objects in SDT3D when connecting after session start
|
# refresh all objects in SDT3D when connecting after session start
|
||||||
if not flags & MessageFlags.ADD.value and not self.sendobjs():
|
if not flags & MessageFlags.ADD.value and not self.sendobjs():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def initialize(self) -> bool:
|
def initialize(self) -> bool:
|
||||||
|
@ -234,8 +179,10 @@ class Sdt:
|
||||||
"""
|
"""
|
||||||
if self.sock is None:
|
if self.sock is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cmd = f"{cmdstr}\n".encode()
|
cmd = f"{cmdstr}\n".encode()
|
||||||
|
logging.debug("sdt cmd: %s", cmd)
|
||||||
self.sock.sendall(cmd)
|
self.sock.sendall(cmd)
|
||||||
return True
|
return True
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -244,91 +191,6 @@ class Sdt:
|
||||||
self.connected = False
|
self.connected = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def updatenode(
|
|
||||||
self,
|
|
||||||
nodenum: int,
|
|
||||||
flags: int,
|
|
||||||
x: Optional[float],
|
|
||||||
y: Optional[float],
|
|
||||||
z: Optional[float],
|
|
||||||
name: str = None,
|
|
||||||
node_type: str = None,
|
|
||||||
icon: str = None,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Node is updated from a Node Message or mobility script.
|
|
||||||
|
|
||||||
:param nodenum: node id to update
|
|
||||||
:param flags: update flags
|
|
||||||
:param x: x position
|
|
||||||
:param y: y position
|
|
||||||
:param z: z position
|
|
||||||
:param name: node name
|
|
||||||
:param node_type: node type
|
|
||||||
:param icon: node icon
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if not self.connect():
|
|
||||||
return
|
|
||||||
if flags & MessageFlags.DELETE.value:
|
|
||||||
self.cmd(f"delete node,{nodenum}")
|
|
||||||
return
|
|
||||||
if x is None or y is None:
|
|
||||||
return
|
|
||||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
|
||||||
pos = f"pos {lon:.6f},{lat:.6f},{alt:.6f}"
|
|
||||||
if flags & MessageFlags.ADD.value:
|
|
||||||
if icon is not None:
|
|
||||||
node_type = name
|
|
||||||
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
|
||||||
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
|
||||||
self.cmd(f"sprite {node_type} image {icon}")
|
|
||||||
self.cmd(f'node {nodenum} type {node_type} label on,"{name}" {pos}')
|
|
||||||
else:
|
|
||||||
self.cmd(f"node {nodenum} {pos}")
|
|
||||||
|
|
||||||
def updatenodegeo(self, nodenum: int, lat: float, lon: float, alt: float) -> None:
|
|
||||||
"""
|
|
||||||
Node is updated upon receiving an EMANE Location Event.
|
|
||||||
|
|
||||||
:param nodenum: node id to update geospatial for
|
|
||||||
:param lat: latitude
|
|
||||||
:param lon: longitude
|
|
||||||
:param alt: altitude
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: received Node Message with lat/long/alt.
|
|
||||||
if not self.connect():
|
|
||||||
return
|
|
||||||
pos = f"pos {lon:.6f},{lat:.6f},{alt:.6f}"
|
|
||||||
self.cmd(f"node {nodenum} {pos}")
|
|
||||||
|
|
||||||
def updatelink(
|
|
||||||
self, node1num: int, node2num: int, flags: int, wireless: bool = False
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Link is updated from a Link Message or by a wireless model.
|
|
||||||
|
|
||||||
:param node1num: node one id
|
|
||||||
:param node2num: node two id
|
|
||||||
:param flags: link flags
|
|
||||||
:param wireless: flag to check if wireless or not
|
|
||||||
:return: nothing
|
|
||||||
"""
|
|
||||||
if node1num is None or node2num is None:
|
|
||||||
return
|
|
||||||
if not self.connect():
|
|
||||||
return
|
|
||||||
if flags & MessageFlags.DELETE.value:
|
|
||||||
self.cmd(f"delete link,{node1num},{node2num}")
|
|
||||||
elif flags & MessageFlags.ADD.value:
|
|
||||||
if wireless:
|
|
||||||
attr = " line green,2"
|
|
||||||
else:
|
|
||||||
attr = " line red,2"
|
|
||||||
self.cmd(f"link {node1num},{node2num}{attr}")
|
|
||||||
|
|
||||||
def sendobjs(self) -> None:
|
def sendobjs(self) -> None:
|
||||||
"""
|
"""
|
||||||
Session has already started, and the SDT3D GUI later connects.
|
Session has already started, and the SDT3D GUI later connects.
|
||||||
|
@ -345,171 +207,169 @@ class Sdt:
|
||||||
nets.append(node)
|
nets.append(node)
|
||||||
if not isinstance(node, NodeBase):
|
if not isinstance(node, NodeBase):
|
||||||
continue
|
continue
|
||||||
(x, y, z) = node.getposition()
|
self.add_node(node)
|
||||||
if x is None or y is None:
|
|
||||||
continue
|
|
||||||
self.updatenode(
|
|
||||||
node.id,
|
|
||||||
MessageFlags.ADD.value,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
z,
|
|
||||||
node.name,
|
|
||||||
node.type,
|
|
||||||
node.icon,
|
|
||||||
)
|
|
||||||
for nodenum in sorted(self.remotes.keys()):
|
|
||||||
r = self.remotes[nodenum]
|
|
||||||
x, y, z = r.pos
|
|
||||||
self.updatenode(
|
|
||||||
nodenum, MessageFlags.ADD.value, x, y, z, r.name, r.type, r.icon
|
|
||||||
)
|
|
||||||
|
|
||||||
for net in nets:
|
for net in nets:
|
||||||
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
all_links = net.all_link_data(flags=MessageFlags.ADD.value)
|
||||||
for link_data in all_links:
|
for link_data in all_links:
|
||||||
is_wireless = isinstance(net, (WlanNode, EmaneNet))
|
is_wireless = isinstance(net, (WlanNode, EmaneNet))
|
||||||
wireless_link = link_data.message_type == LinkTypes.WIRELESS.value
|
|
||||||
if is_wireless and link_data.node1_id == net.id:
|
if is_wireless and link_data.node1_id == net.id:
|
||||||
continue
|
continue
|
||||||
|
params = link_data_params(link_data)
|
||||||
|
self.add_link(*params)
|
||||||
|
|
||||||
self.updatelink(
|
def get_node_position(self, node: NodeBase) -> Optional[str]:
|
||||||
link_data.node1_id,
|
|
||||||
link_data.node2_id,
|
|
||||||
MessageFlags.ADD.value,
|
|
||||||
wireless_link,
|
|
||||||
)
|
|
||||||
|
|
||||||
for n1num in sorted(self.remotes.keys()):
|
|
||||||
r = self.remotes[n1num]
|
|
||||||
for n2num, wireless_link in r.links:
|
|
||||||
self.updatelink(n1num, n2num, MessageFlags.ADD.value, wireless_link)
|
|
||||||
|
|
||||||
def handle_distributed(self, message: CoreMessage) -> None:
|
|
||||||
"""
|
"""
|
||||||
Broker handler for processing CORE API messages as they are
|
Convenience to generate an SDT position string, given a node.
|
||||||
received. This is used to snoop the Node messages and update
|
|
||||||
node positions.
|
|
||||||
|
|
||||||
:param message: message to handle
|
:param node:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
x, y, z = node.position.get()
|
||||||
|
if x is None or y is None:
|
||||||
|
return None
|
||||||
|
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||||
|
return f"pos {lon:.6f},{lat:.6f},{alt:.6f}"
|
||||||
|
|
||||||
|
def add_node(self, node: NodeBase) -> None:
|
||||||
|
"""
|
||||||
|
Handle adding a node in SDT.
|
||||||
|
|
||||||
|
:param node: node to add
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if isinstance(message, CoreLinkMessage):
|
logging.debug("sdt add node: %s - %s", node.id, node.name)
|
||||||
self.handlelinkmsg(message)
|
if not self.connect():
|
||||||
elif isinstance(message, CoreNodeMessage):
|
return
|
||||||
self.handlenodemsg(message)
|
pos = self.get_node_position(node)
|
||||||
|
if not pos:
|
||||||
|
return
|
||||||
|
node_type = node.type
|
||||||
|
if node_type is None:
|
||||||
|
node_type = type(node).type
|
||||||
|
icon = node.icon
|
||||||
|
if icon:
|
||||||
|
node_type = node.name
|
||||||
|
icon = icon.replace("$CORE_DATA_DIR", constants.CORE_DATA_DIR)
|
||||||
|
icon = icon.replace("$CORE_CONF_DIR", constants.CORE_CONF_DIR)
|
||||||
|
self.cmd(f"sprite {node_type} image {icon}")
|
||||||
|
self.cmd(f'node {node.id} type {node_type} label on,"{node.name}" {pos}')
|
||||||
|
|
||||||
def handlenodemsg(self, msg: CoreNodeMessage) -> None:
|
def edit_node(self, node: NodeBase) -> None:
|
||||||
"""
|
"""
|
||||||
Process a Node Message to add/delete or move a node on
|
Handle updating a node in SDT.
|
||||||
the SDT display. Node properties are found in a session or
|
|
||||||
self.remotes for remote nodes (or those not yet instantiated).
|
|
||||||
|
|
||||||
:param msg: node message to handle
|
:param node: node to update
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
# for distributed sessions to work properly, the SDT option should be
|
logging.debug("sdt update node: %s - %s", node.id, node.name)
|
||||||
# enabled prior to starting the session
|
if not self.connect():
|
||||||
if not self.is_enabled():
|
|
||||||
return
|
return
|
||||||
# node.(_id, type, icon, name) are used.
|
pos = self.get_node_position(node)
|
||||||
nodenum = msg.get_tlv(NodeTlvs.NUMBER.value)
|
if not pos:
|
||||||
if not nodenum:
|
|
||||||
return
|
return
|
||||||
x = msg.get_tlv(NodeTlvs.X_POSITION.value)
|
self.cmd(f"node {node.id} {pos}")
|
||||||
y = msg.get_tlv(NodeTlvs.Y_POSITION.value)
|
|
||||||
z = None
|
|
||||||
name = msg.get_tlv(NodeTlvs.NAME.value)
|
|
||||||
|
|
||||||
nodetype = msg.get_tlv(NodeTlvs.TYPE.value)
|
def delete_node(self, node_id: int) -> None:
|
||||||
model = msg.get_tlv(NodeTlvs.MODEL.value)
|
"""
|
||||||
icon = msg.get_tlv(NodeTlvs.ICON.value)
|
Handle deleting a node in SDT.
|
||||||
|
|
||||||
net = False
|
:param node_id: node id to delete
|
||||||
if nodetype == NodeTypes.DEFAULT.value or nodetype == NodeTypes.PHYSICAL.value:
|
:return: nothing
|
||||||
if model is None:
|
"""
|
||||||
model = "router"
|
logging.debug("sdt delete node: %s", node_id)
|
||||||
nodetype = model
|
if not self.connect():
|
||||||
elif nodetype is not None:
|
return
|
||||||
nodetype = NodeTypes(nodetype)
|
self.cmd(f"delete node,{node_id}")
|
||||||
nodetype = self.session.get_node_class(nodetype).type
|
|
||||||
net = True
|
def handle_node_update(self, node_data: NodeData) -> None:
|
||||||
|
"""
|
||||||
|
Handler for node updates, specifically for updating their location.
|
||||||
|
|
||||||
|
:param node_data: node data being updated
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.debug("sdt handle node update: %s - %s", node_data.id, node_data.name)
|
||||||
|
if not self.connect():
|
||||||
|
return
|
||||||
|
|
||||||
|
# delete node
|
||||||
|
if node_data.message_type == MessageFlags.DELETE.value:
|
||||||
|
self.cmd(f"delete node,{node_data.id}")
|
||||||
else:
|
else:
|
||||||
nodetype = None
|
x = node_data.x_position
|
||||||
|
y = node_data.y_position
|
||||||
|
lat = node_data.latitude
|
||||||
|
lon = node_data.longitude
|
||||||
|
alt = node_data.altitude
|
||||||
|
if all([lat is not None, lon is not None, alt is not None]):
|
||||||
|
pos = f"pos {lon:.6f},{lat:.6f},{alt:.6f}"
|
||||||
|
self.cmd(f"node {node_data.id} {pos}")
|
||||||
|
elif node_data.message_type == 0:
|
||||||
|
lat, lon, alt = self.session.location.getgeo(x, y, 0)
|
||||||
|
pos = f"pos {lon:.6f},{lat:.6f},{alt:.6f}"
|
||||||
|
self.cmd(f"node {node_data.id} {pos}")
|
||||||
|
|
||||||
|
def wireless_net_check(self, node_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Determines if a node is either a wireless node type.
|
||||||
|
|
||||||
|
:param node_id: node id to check
|
||||||
|
:return: True is a wireless node type, False otherwise
|
||||||
|
"""
|
||||||
|
result = False
|
||||||
try:
|
try:
|
||||||
node = self.session.get_node(nodenum)
|
node = self.session.get_node(node_id)
|
||||||
|
result = isinstance(node, (WlanNode, EmaneNet))
|
||||||
except CoreError:
|
except CoreError:
|
||||||
node = None
|
pass
|
||||||
if node:
|
return result
|
||||||
self.updatenode(
|
|
||||||
node.id, msg.flags, x, y, z, node.name, node.type, node.icon
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if nodenum in self.remotes:
|
|
||||||
remote = self.remotes[nodenum]
|
|
||||||
if name is None:
|
|
||||||
name = remote.name
|
|
||||||
if nodetype is None:
|
|
||||||
nodetype = remote.type
|
|
||||||
if icon is None:
|
|
||||||
icon = remote.icon
|
|
||||||
else:
|
|
||||||
remote = Bunch(
|
|
||||||
_id=nodenum,
|
|
||||||
type=nodetype,
|
|
||||||
icon=icon,
|
|
||||||
name=name,
|
|
||||||
net=net,
|
|
||||||
links=set(),
|
|
||||||
)
|
|
||||||
self.remotes[nodenum] = remote
|
|
||||||
remote.pos = (x, y, z)
|
|
||||||
self.updatenode(nodenum, msg.flags, x, y, z, name, nodetype, icon)
|
|
||||||
|
|
||||||
def handlelinkmsg(self, msg: CoreLinkMessage) -> None:
|
def add_link(self, node_one: int, node_two: int, is_wireless: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Process a Link Message to add/remove links on the SDT display.
|
Handle adding a link in SDT.
|
||||||
Links are recorded in the remotes[nodenum1].links set for updating
|
|
||||||
the SDT display at a later time.
|
|
||||||
|
|
||||||
:param msg: link message to handle
|
:param node_one: node one id
|
||||||
|
:param node_two: node two id
|
||||||
|
:param is_wireless: True if link is wireless, False otherwise
|
||||||
:return: nothing
|
:return: nothing
|
||||||
"""
|
"""
|
||||||
if not self.is_enabled():
|
logging.debug("sdt add link: %s, %s, %s", node_one, node_two, is_wireless)
|
||||||
|
if not self.connect():
|
||||||
return
|
return
|
||||||
nodenum1 = msg.get_tlv(LinkTlvs.N1_NUMBER.value)
|
if self.wireless_net_check(node_one) or self.wireless_net_check(node_two):
|
||||||
nodenum2 = msg.get_tlv(LinkTlvs.N2_NUMBER.value)
|
|
||||||
link_msg_type = msg.get_tlv(LinkTlvs.TYPE.value)
|
|
||||||
# this filters out links to WLAN and EMANE nodes which are not drawn
|
|
||||||
if self.wlancheck(nodenum1):
|
|
||||||
return
|
return
|
||||||
wl = link_msg_type == LinkTypes.WIRELESS.value
|
if is_wireless:
|
||||||
if nodenum1 in self.remotes:
|
attr = "green,2"
|
||||||
r = self.remotes[nodenum1]
|
|
||||||
if msg.flags & MessageFlags.DELETE.value:
|
|
||||||
if (nodenum2, wl) in r.links:
|
|
||||||
r.links.remove((nodenum2, wl))
|
|
||||||
else:
|
|
||||||
r.links.add((nodenum2, wl))
|
|
||||||
self.updatelink(nodenum1, nodenum2, msg.flags, wireless=wl)
|
|
||||||
|
|
||||||
def wlancheck(self, nodenum: int) -> bool:
|
|
||||||
"""
|
|
||||||
Helper returns True if a node number corresponds to a WLAN or EMANE node.
|
|
||||||
|
|
||||||
:param nodenum: node id to check
|
|
||||||
:return: True if node is wlan or emane, False otherwise
|
|
||||||
"""
|
|
||||||
if nodenum in self.remotes:
|
|
||||||
node_type = self.remotes[nodenum].type
|
|
||||||
if node_type in ("wlan", "emane"):
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
try:
|
attr = "red,2"
|
||||||
n = self.session.get_node(nodenum)
|
self.cmd(f"link {node_one},{node_two} line {attr}")
|
||||||
except CoreError:
|
|
||||||
return False
|
def delete_link(self, node_one: int, node_two: int) -> None:
|
||||||
if isinstance(n, (WlanNode, EmaneNet)):
|
"""
|
||||||
return True
|
Handle deleting a node in SDT.
|
||||||
return False
|
|
||||||
|
:param node_one: node one id
|
||||||
|
:param node_two: node two id
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
logging.debug("sdt delete link: %s, %s", node_one, node_two)
|
||||||
|
if not self.connect():
|
||||||
|
return
|
||||||
|
if self.wireless_net_check(node_one) or self.wireless_net_check(node_two):
|
||||||
|
return
|
||||||
|
self.cmd(f"delete link,{node_one},{node_two}")
|
||||||
|
|
||||||
|
def handle_link_update(self, link_data: LinkData) -> None:
|
||||||
|
"""
|
||||||
|
Handle link broadcast messages and push changes to SDT.
|
||||||
|
|
||||||
|
:param link_data: link data to handle
|
||||||
|
:return: nothing
|
||||||
|
"""
|
||||||
|
if link_data.message_type == MessageFlags.ADD.value:
|
||||||
|
params = link_data_params(link_data)
|
||||||
|
self.add_link(*params)
|
||||||
|
elif link_data.message_type == MessageFlags.DELETE.value:
|
||||||
|
params = link_data_params(link_data)
|
||||||
|
self.delete_link(*params[:2])
|
||||||
|
|
Loading…
Reference in a new issue