From b3dabbfe05430946d6e6b76e9c3b2259e1c683d3 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Tue, 18 Feb 2020 10:33:49 -0800 Subject: [PATCH 1/6] delete wireless links on canvas during runtime --- daemon/core/gui/graph/node.py | 12 ++++++++++++ daemon/core/location/mobility.py | 5 +---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index e6a84e72..123eb2c3 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -66,6 +66,18 @@ class CanvasNode: def delete(self): logging.debug("Delete canvas node for %s", self.core_node) + # print(self.app.core.client.get_session(self.app.core.session_id)) + # response = self.app.core.client.delete_node(self.app.core.session_id, self.core_node.id) + # for wireless_edge in self.wireless_edges: + # token = wireless_edge.token + # other = token[0] + # if other == self.id: + # other = token[1] + # self.canvas.nodes[other].wireless_edges.discard(wireless_edge) + # wlan_edge = self.canvas.wireless_edges.pop(token, None) + # self.canvas.delete(wlan_edge.id) + + self.wireless_edges.clear() self.canvas.delete(self.id) self.canvas.delete(self.text_id) self.delete_antennas() diff --git a/daemon/core/location/mobility.py b/daemon/core/location/mobility.py index 4689d217..2b6051a4 100644 --- a/daemon/core/location/mobility.py +++ b/daemon/core/location/mobility.py @@ -291,10 +291,7 @@ class BasicRangeModel(WirelessModel): label="transmission delay (usec)", ), Configuration( - _id="error", - _type=ConfigDataTypes.STRING, - default="0", - label="error rate (%)", + _id="error", _type=ConfigDataTypes.STRING, default="0", label="loss (%)" ), ] From 08e652633f8ba1cd457e28aa9b2bd25bc2818476 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Tue, 18 Feb 2020 13:59:23 -0800 Subject: [PATCH 2/6] support wireless link deletion during runtime --- daemon/core/gui/graph/node.py | 74 +++++++++++++++++++++++++++++------ daemon/core/gui/nodeutils.py | 4 ++ 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index 123eb2c3..8210b4f2 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -17,7 +17,7 @@ from core.gui.dialogs.wlanconfig import WlanConfigDialog from core.gui.errors import show_grpc_error from core.gui.graph import tags from core.gui.graph.tooltip import CanvasTooltip -from core.gui.nodeutils import NodeUtils +from core.gui.nodeutils import EdgeUtils, NodeUtils if TYPE_CHECKING: from core.gui.app import Application @@ -66,21 +66,43 @@ class CanvasNode: def delete(self): logging.debug("Delete canvas node for %s", self.core_node) - # print(self.app.core.client.get_session(self.app.core.session_id)) - # response = self.app.core.client.delete_node(self.app.core.session_id, self.core_node.id) - # for wireless_edge in self.wireless_edges: - # token = wireless_edge.token - # other = token[0] - # if other == self.id: - # other = token[1] - # self.canvas.nodes[other].wireless_edges.discard(wireless_edge) - # wlan_edge = self.canvas.wireless_edges.pop(token, None) - # self.canvas.delete(wlan_edge.id) - self.wireless_edges.clear() + # if node is wlan, EMANE type, remove any existing wireless links between nodes connetect to this node + if NodeUtils.is_wireless_node(self.core_node.type): + nodes = [] + for edge in self.edges: + token = edge.token + if self.id == token[0]: + nodes.append(token[1]) + else: + nodes.append(token[0]) + for i in range(len(nodes)): + for j in range(i + 1, len(nodes)): + token = EdgeUtils.get_token(nodes[i], nodes[j]) + wireless_edge = self.canvas.wireless_edges.pop(token, None) + if wireless_edge: + + self.canvas.nodes[nodes[i]].wireless_edges.remove(wireless_edge) + self.canvas.nodes[nodes[j]].wireless_edges.remove(wireless_edge) + self.canvas.delete(wireless_edge.id) + else: + logging.debug("%s is not a wireless edge", token) + # if node is MDR, remove wireless links to other MDRs + elif NodeUtils.is_mdr_node(self.core_node.type, self.core_node.model): + for wireless_edge in self.wireless_edges: + token = wireless_edge.token + other = token[0] + if other == self.id: + other = token[1] + self.canvas.nodes[other].wireless_edges.discard(wireless_edge) + wlan_edge = self.canvas.wireless_edges.pop(token, None) + self.canvas.delete(wlan_edge.id) + self.delete_antennas() + + self.wireless_edges.clear() + self.canvas.delete(self.id) self.canvas.delete(self.text_id) - self.delete_antennas() def add_antenna(self): x, y = self.canvas.coords(self.id) @@ -307,3 +329,29 @@ class CanvasNode: if core_node.type == core_pb2.NodeType.DEFAULT and core_node.model == "mdr": self.canvas.create_edge(self, self.canvas.nodes[canvas_nid]) self.canvas.clear_selection() + + def remove_wireless_links(self): + """ + remove the wireless links between the nodes that are connected to this node, + if this node is a wireless network node (wlan or EMANE) + :return: + """ + if NodeUtils.is_wireless_node(self.core_node.type): + nodes = [] + for edge in self.edges: + token = edge.token + if self.id == token[0]: + nodes.append(token[1]) + else: + nodes.append(token[0]) + for i in range(len(nodes)): + for j in range(i + 1, len(nodes)): + token = EdgeUtils.get_token(nodes[i], nodes[j]) + wireless_edge = self.canvas.wireless_edges.pop(token, None) + if wireless_edge: + + self.canvas.nodes[nodes[i]].wireless_edges.remove(wireless_edge) + self.canvas.nodes[nodes[j]].wireless_edges.remove(wireless_edge) + self.canvas.delete(wireless_edge.id) + else: + logging.debug("%s is not a wireless edge", token) diff --git a/daemon/core/gui/nodeutils.py b/daemon/core/gui/nodeutils.py index c8ddb8fa..f0a3a35d 100644 --- a/daemon/core/gui/nodeutils.py +++ b/daemon/core/gui/nodeutils.py @@ -90,6 +90,10 @@ class NodeUtils: def is_rj45_node(cls, node_type: NodeType) -> bool: return node_type in cls.RJ45_NODES + @classmethod + def is_mdr_node(cls, node_type: NodeType, model: str) -> bool: + return cls.is_container_node(node_type) and model == "mdr" + @classmethod def node_icon( cls, From d8f586bd2be628c4d4bf1ee21529379ee3d0d09b Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Tue, 18 Feb 2020 15:58:18 -0800 Subject: [PATCH 3/6] add wireless network variable to CanvasGraph that maps a wireless/EMANE node to all MDRs connected to it --- daemon/core/gui/dialogs/wlanconfig.py | 10 ++++++++++ daemon/core/gui/graph/edges.py | 11 +++++++++++ daemon/core/gui/graph/graph.py | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/daemon/core/gui/dialogs/wlanconfig.py b/daemon/core/gui/dialogs/wlanconfig.py index d6da667e..eb525f6d 100644 --- a/daemon/core/gui/dialogs/wlanconfig.py +++ b/daemon/core/gui/dialogs/wlanconfig.py @@ -27,6 +27,7 @@ class WlanConfigDialog(Dialog): self.canvas_node = canvas_node self.node = canvas_node.core_node self.config_frame = None + self.range_entry = None self.has_error = False try: self.config = self.app.core.get_wlan_config(self.node.id) @@ -53,6 +54,11 @@ class WlanConfigDialog(Dialog): for i in range(2): frame.columnconfigure(i, weight=1) + self.range_entry = self.config_frame.winfo_children()[0].frame.winfo_children()[ + -1 + ] + self.range_entry.bind("", self.update_range) + button = ttk.Button(frame, text="Apply", command=self.click_apply) button.grid(row=0, column=0, padx=PADX, sticky="ew") @@ -69,3 +75,7 @@ class WlanConfigDialog(Dialog): session_id = self.app.core.session_id self.app.core.client.set_wlan_config(session_id, self.node.id, config) self.destroy() + + def update_range(self, event): + if event.char.isdigit(): + print(self.range_entry.get() + event.char) diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index 1259ffa9..c00fd2aa 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -177,6 +177,17 @@ class CanvasEdge: dst_node_type = dst_node.core_node.type is_src_wireless = NodeUtils.is_wireless_node(src_node_type) is_dst_wireless = NodeUtils.is_wireless_node(dst_node_type) + + # update the wlan/EMANE network + wlan_network = self.canvas.wireless_network + if is_src_wireless and not is_dst_wireless: + if self.src not in wlan_network: + wlan_network[self.src] = set() + wlan_network[self.src].add(self.dst) + elif not is_src_wireless and is_dst_wireless: + if self.dst not in wlan_network: + wlan_network[self.dst] = set() + wlan_network[self.dst].add(self.src) return is_src_wireless or is_dst_wireless def check_wireless(self): diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index a56f1423..77234064 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -42,6 +42,10 @@ class CanvasGraph(tk.Canvas): self.edges = {} self.shapes = {} self.wireless_edges = {} + + # map wireless/EMANE node to the set of MDRs connected to that node + self.wireless_network = {} + self.drawing_edge = None self.grid = None self.shape_drawing = False From 23aeb40f54f7ab5c955cc2d3e9c24dd1b90a2275 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Wed, 19 Feb 2020 13:22:52 -0800 Subject: [PATCH 4/6] display the range while configuring wlan node --- daemon/core/gui/dialogs/wlanconfig.py | 56 +++++++++++++++++++++++---- daemon/core/gui/graph/edges.py | 3 +- daemon/core/gui/graph/node.py | 2 +- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/daemon/core/gui/dialogs/wlanconfig.py b/daemon/core/gui/dialogs/wlanconfig.py index eb525f6d..c0c8c845 100644 --- a/daemon/core/gui/dialogs/wlanconfig.py +++ b/daemon/core/gui/dialogs/wlanconfig.py @@ -1,7 +1,3 @@ -""" -wlan configuration -""" - from tkinter import ttk from typing import TYPE_CHECKING @@ -16,6 +12,9 @@ if TYPE_CHECKING: from core.gui.app import Application from core.gui.graph.node import CanvasNode +RANGE_COLOR = "#009933" +RANGE_WIDTH = 3 + class WlanConfigDialog(Dialog): def __init__( @@ -29,14 +28,27 @@ class WlanConfigDialog(Dialog): self.config_frame = None self.range_entry = None self.has_error = False + self.canvas = app.canvas + self.ranges = {} + self.positive_int = self.app.master.register(self.validate_and_update) try: self.config = self.app.core.get_wlan_config(self.node.id) + self.init_draw_range() self.draw() except grpc.RpcError as e: show_grpc_error(e, self.app, self.app) self.has_error = True self.destroy() + def init_draw_range(self): + if self.canvas_node.id in self.canvas.wireless_network: + for cid in self.canvas.wireless_network[self.canvas_node.id]: + x, y = self.canvas.coords(cid) + range_id = self.canvas.create_oval( + x, y, x, y, width=RANGE_WIDTH, outline=RANGE_COLOR, tags="range" + ) + self.ranges[cid] = range_id + def draw(self): self.top.columnconfigure(0, weight=1) self.top.rowconfigure(0, weight=1) @@ -44,6 +56,7 @@ class WlanConfigDialog(Dialog): self.config_frame.draw_config() self.config_frame.grid(sticky="nsew", pady=PADY) self.draw_apply_buttons() + self.top.bind("", self.remove_ranges) def draw_apply_buttons(self): """ @@ -57,7 +70,7 @@ class WlanConfigDialog(Dialog): self.range_entry = self.config_frame.winfo_children()[0].frame.winfo_children()[ -1 ] - self.range_entry.bind("", self.update_range) + self.range_entry.config(validatecommand=(self.positive_int, "%P")) button = ttk.Button(frame, text="Apply", command=self.click_apply) button.grid(row=0, column=0, padx=PADX, sticky="ew") @@ -74,8 +87,35 @@ class WlanConfigDialog(Dialog): if self.app.core.is_runtime(): session_id = self.app.core.session_id self.app.core.client.set_wlan_config(session_id, self.node.id, config) + self.remove_ranges() self.destroy() - def update_range(self, event): - if event.char.isdigit(): - print(self.range_entry.get() + event.char) + def remove_ranges(self, event=None): + for cid in self.canvas.find_withtag("range"): + self.canvas.delete(cid) + self.ranges.clear() + + def validate_and_update(self, s: str) -> bool: + """ + custom validation to also redraw the mdr ranges when the range value changes + """ + if len(s) == 0: + return True + try: + int_value = int(s) + if int_value >= 0: + net_range = int_value * self.canvas.ratio + if self.canvas_node.id in self.canvas.wireless_network: + for cid in self.canvas.wireless_network[self.canvas_node.id]: + x, y = self.canvas.coords(cid) + self.canvas.coords( + self.ranges[cid], + x - net_range, + y - net_range, + x + net_range, + y + net_range, + ) + return True + return False + except ValueError: + return False diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index c00fd2aa..0ca1c8e6 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -14,6 +14,7 @@ if TYPE_CHECKING: TEXT_DISTANCE = 0.30 EDGE_WIDTH = 3 EDGE_COLOR = "#ff0000" +WIRELESS_COLOR = "#009933" class CanvasWirelessEdge: @@ -31,7 +32,7 @@ class CanvasWirelessEdge: self.dst = dst self.canvas = canvas self.id = self.canvas.create_line( - *position, tags=tags.WIRELESS_EDGE, width=1.5, fill="#009933" + *position, tags=tags.WIRELESS_EDGE, width=EDGE_WIDTH, fill="#009933" ) def delete(self): diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index 8210b4f2..ecd95d58 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -97,7 +97,7 @@ class CanvasNode: self.canvas.nodes[other].wireless_edges.discard(wireless_edge) wlan_edge = self.canvas.wireless_edges.pop(token, None) self.canvas.delete(wlan_edge.id) - self.delete_antennas() + self.delete_antennas() self.wireless_edges.clear() From e90eff578e076bd5c8db05514f80b064e60d8420 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Thu, 20 Feb 2020 11:16:26 -0800 Subject: [PATCH 5/6] reset variable --- daemon/core/gui/graph/graph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index 811bca45..7905ed8c 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -117,6 +117,7 @@ class CanvasGraph(tk.Canvas): self.edges.clear() self.shapes.clear() self.wireless_edges.clear() + self.wireless_network.clear() self.drawing_edge = None self.draw_session(session) From 2a8f689ad52b4c7a93f87bfb4f4b65c6fa53c551 Mon Sep 17 00:00:00 2001 From: Huy Pham <42948410+hpham@users.noreply.github.com> Date: Thu, 20 Feb 2020 11:26:48 -0800 Subject: [PATCH 6/6] remove extra code --- daemon/core/gui/graph/node.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index 96003905..7b4ccf31 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -345,32 +345,6 @@ class CanvasNode: self.canvas.create_edge(self, self.canvas.nodes[canvas_nid]) self.canvas.clear_selection() - def remove_wireless_links(self): - """ - remove the wireless links between the nodes that are connected to this node, - if this node is a wireless network node (wlan or EMANE) - :return: - """ - if NodeUtils.is_wireless_node(self.core_node.type): - nodes = [] - for edge in self.edges: - token = edge.token - if self.id == token[0]: - nodes.append(token[1]) - else: - nodes.append(token[0]) - for i in range(len(nodes)): - for j in range(i + 1, len(nodes)): - token = EdgeUtils.get_token(nodes[i], nodes[j]) - wireless_edge = self.canvas.wireless_edges.pop(token, None) - if wireless_edge: - - self.canvas.nodes[nodes[i]].wireless_edges.remove(wireless_edge) - self.canvas.nodes[nodes[j]].wireless_edges.remove(wireless_edge) - self.canvas.delete(wireless_edge.id) - else: - logging.debug("%s is not a wireless edge", token) - def scale_antennas(self): for i in range(len(self.antennas)): antenna_id = self.antennas[i]