From 2be0713ed107991445a55149f40fa1bcce884bbf Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Fri, 27 Dec 2019 00:32:10 -0800 Subject: [PATCH] updated so that throughputs will update link color/width based on threshold --- daemon/core/gui/coreclient.py | 5 +- daemon/core/gui/graph/edges.py | 48 +++++++++- daemon/core/gui/graph/graph.py | 33 ++++++- daemon/core/gui/graph/linkinfo.py | 152 ------------------------------ daemon/core/gui/graph/node.py | 1 - daemon/core/gui/graph/tags.py | 1 + daemon/core/gui/toolbar.py | 2 - 7 files changed, 75 insertions(+), 167 deletions(-) delete mode 100644 daemon/core/gui/graph/linkinfo.py diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py index 0952af66..2f1488fd 100644 --- a/daemon/core/gui/coreclient.py +++ b/daemon/core/gui/coreclient.py @@ -212,9 +212,7 @@ class CoreClient: ) return logging.info("handling throughputs event: %s", event) - self.app.canvas.throughput_draw.process_grpc_throughput_event( - event.interface_throughputs - ) + self.app.canvas.set_throughputs(event) def handle_exception_event(self, event): logging.info("exception event: %s", event) @@ -511,6 +509,7 @@ class CoreClient: start = time.perf_counter() try: response = self.client.stop_session(session_id) + self.app.canvas.stopped_session() logging.debug( "stopped session(%s), result: %s", session_id, response.result ) diff --git a/daemon/core/gui/graph/edges.py b/daemon/core/gui/graph/edges.py index ebcda49e..323309de 100644 --- a/daemon/core/gui/graph/edges.py +++ b/daemon/core/gui/graph/edges.py @@ -8,6 +8,8 @@ from core.gui.graph import tags from core.gui.nodeutils import NodeUtils TEXT_DISTANCE = 0.30 +EDGE_WIDTH = 3 +EDGE_COLOR = "#ff0000" class CanvasWirelessEdge: @@ -29,8 +31,6 @@ class CanvasEdge: Canvas edge class """ - width = 3 - def __init__(self, x1, y1, x2, y2, src, canvas): """ Create an instance of canvas edge object @@ -47,10 +47,11 @@ class CanvasEdge: self.dst_interface = None self.canvas = canvas self.id = self.canvas.create_line( - x1, y1, x2, y2, tags=tags.EDGE, width=self.width, fill="#ff0000" + x1, y1, x2, y2, tags=tags.EDGE, width=EDGE_WIDTH, fill=EDGE_COLOR ) self.text_src = None self.text_dst = None + self.text_middle = None self.token = None self.font = Font(size=8) self.link = None @@ -77,6 +78,12 @@ class CanvasEdge: y2 = y2 - uy return x1, y1, x2, y2 + def get_midpoint(self): + x1, y1, x2, y2 = self.canvas.coords(self.id) + x = (x1 + x2) / 2 + y = (y1 + y2) / 2 + return x, y + def draw_labels(self): x1, y1, x2, y2 = self.get_coordinates() label_one = None @@ -117,6 +124,28 @@ class CanvasEdge: x1, y1, x2, y2 = self.get_coordinates() self.canvas.coords(self.text_src, x1, y1) self.canvas.coords(self.text_dst, x2, y2) + if self.text_middle is not None: + x, y = self.get_midpoint() + self.canvas.coords(self.text_middle, x, y) + + def set_throughput(self, throughput): + throughput = 0.001 * throughput + value = f"{throughput:.3f} kbps" + if self.text_middle is None: + x, y = self.get_midpoint() + self.text_middle = self.canvas.create_text( + x, y, tags=tags.THROUGHPUT, font=self.font, text=value + ) + else: + self.canvas.itemconfig(self.text_middle, text=value) + + if throughput > self.canvas.throughput_threshold: + color = self.canvas.throughput_color + width = self.canvas.throughput_width + else: + color = EDGE_COLOR + width = EDGE_WIDTH + self.canvas.itemconfig(self.id, fill=color, width=width) def complete(self, dst): self.dst = dst @@ -128,14 +157,17 @@ class CanvasEdge: self.canvas.tag_raise(self.src) self.canvas.tag_raise(self.dst) - def check_wireless(self): + def is_wireless(self): src_node = self.canvas.nodes[self.src] dst_node = self.canvas.nodes[self.dst] src_node_type = src_node.core_node.type 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) - if is_src_wireless or is_dst_wireless: + return is_src_wireless or is_dst_wireless + + def check_wireless(self): + if self.is_wireless(): self.canvas.itemconfig(self.id, state=tk.HIDDEN) self._check_antenna() @@ -160,6 +192,12 @@ class CanvasEdge: if self.link: self.canvas.delete(self.text_src) self.canvas.delete(self.text_dst) + self.canvas.delete(self.text_middle) + + def reset(self): + self.canvas.delete(self.text_middle) + self.text_middle = None + self.canvas.itemconfig(self.id, fill=EDGE_COLOR, width=EDGE_WIDTH) def create_context(self, event): logging.debug("create link context") diff --git a/daemon/core/gui/graph/graph.py b/daemon/core/gui/graph/graph.py index 586f99b2..b10fe0e9 100644 --- a/daemon/core/gui/graph/graph.py +++ b/daemon/core/gui/graph/graph.py @@ -9,7 +9,6 @@ from core.gui.dialogs.shapemod import ShapeDialog from core.gui.graph import tags from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge from core.gui.graph.enums import GraphMode, ScaleOption -from core.gui.graph.linkinfo import Throughput from core.gui.graph.node import CanvasNode from core.gui.graph.shape import Shape from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker @@ -38,7 +37,6 @@ class CanvasGraph(tk.Canvas): self.wireless_edges = {} self.drawing_edge = None self.grid = None - self.throughput_draw = Throughput(self, core) self.shape_drawing = False self.default_dimensions = (width, height) self.current_dimensions = self.default_dimensions @@ -158,6 +156,21 @@ class CanvasGraph(tk.Canvas): valid_bottomright = self.inside_canvas(x2, y2) return valid_topleft and valid_bottomright + def set_throughputs(self, throughputs_event): + for interface_throughput in throughputs_event.interface_throughputs: + node_id = interface_throughput.node_id + interface_id = interface_throughput.interface_id + throughput = interface_throughput.throughput + interface_to_edge_id = (node_id, interface_id) + token = self.core.interface_to_edge.get(interface_to_edge_id) + if not token: + continue + edge = self.edges.get(token) + if edge: + edge.set_throughput(throughput) + else: + del self.core.interface_to_edge[interface_to_edge_id] + def draw_grid(self): """ Create grid. @@ -269,6 +282,20 @@ class CanvasGraph(tk.Canvas): # raise the nodes so they on top of the links self.tag_raise(tags.NODE) + def stopped_session(self): + # clear wireless edges + for edge in self.wireless_edges.values(): + edge.delete() + src_node = self.nodes[edge.src] + src_node.wireless_edges.remove(edge) + dst_node = self.nodes[edge.dst] + dst_node.wireless_edges.remove(edge) + self.wireless_edges.clear() + + # clear all middle edge labels + for edge in self.edges.values(): + edge.reset() + def canvas_xy(self, event): """ Convert window coordinate to canvas coordinate @@ -446,9 +473,7 @@ class CanvasGraph(tk.Canvas): if edge in edges: continue edges.add(edge) - self.throughput_draw.delete(edge) self.edges.pop(edge.token, None) - # del self.edges[edge.token] edge.delete() # update node connected to edge being deleted diff --git a/daemon/core/gui/graph/linkinfo.py b/daemon/core/gui/graph/linkinfo.py deleted file mode 100644 index de4b2c6e..00000000 --- a/daemon/core/gui/graph/linkinfo.py +++ /dev/null @@ -1,152 +0,0 @@ -""" -Link information, such as IPv4, IPv6 and throughput drawn in the canvas -""" - -from core.api.grpc import core_pb2 - - -class Throughput: - def __init__(self, canvas, core): - self.canvas = canvas - self.core = core - # edge canvas id mapped to throughput value - self.tracker = {} - # map an edge canvas id to a throughput canvas id - self.map = {} - # map edge canvas id to token - self.edge_id_to_token = {} - - def load_throughput_info(self, interface_throughputs): - """ - load all interface throughouts from an event - - :param repeated core_bp2.InterfaceThroughputinterface_throughputs: interface - throughputs - :return: nothing - """ - for throughput in interface_throughputs: - nid = throughput.node_id - iid = throughput.interface_id - tp = throughput.throughput - token = self.core.interface_to_edge.get((nid, iid)) - if token: - edge = self.canvas.edges.get(token) - if edge: - edge_id = edge.id - self.edge_id_to_token[edge_id] = token - if edge_id not in self.tracker: - self.tracker[edge_id] = tp - else: - temp = self.tracker[edge_id] - self.tracker[edge_id] = (temp + tp) / 2 - else: - self.core.interface_to_edge.pop((nid, iid), None) - - def edge_is_wired(self, token): - """ - determine whether link is a WIRED link - - :param token: - :return: - """ - canvas_edge = self.canvas.edges[token] - canvas_src_id = canvas_edge.src - canvas_dst_id = canvas_edge.dst - src = self.canvas.nodes[canvas_src_id].core_node - dst = self.canvas.nodes[canvas_dst_id].core_node - return not ( - src.type == core_pb2.NodeType.WIRELESS_LAN - and dst.model == "mdr" - or src.model == "mdr" - and dst.type == core_pb2.NodeType.WIRELESS_LAN - ) - - def draw_wired_throughput(self, edge_id): - - x0, y0, x1, y1 = self.canvas.coords(edge_id) - x = (x0 + x1) / 2 - y = (y0 + y1) / 2 - if edge_id not in self.map: - tpid = self.canvas.create_text( - x, - y, - tags="throughput", - font=("Arial", 8), - text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]), - ) - self.map[edge_id] = tpid - else: - tpid = self.map[edge_id] - self.canvas.coords(tpid, x, y) - self.canvas.itemconfig( - tpid, text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]) - ) - - def draw_wireless_throughput(self, edge_id): - token = self.edge_id_to_token[edge_id] - canvas_edge = self.canvas.edges[token] - canvas_src_id = canvas_edge.src - canvas_dst_id = canvas_edge.dst - src_node = self.canvas.nodes[canvas_src_id] - dst_node = self.canvas.nodes[canvas_dst_id] - - not_wlan = ( - dst_node - if src_node.core_node.type == core_pb2.NodeType.WIRELESS_LAN - else src_node - ) - - x, y = self.canvas.coords(not_wlan.id) - if edge_id not in self.map: - tp_id = self.canvas.create_text( - x + 50, - y + 25, - font=("Arial", 8), - tags="throughput", - text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]), - ) - self.map[edge_id] = tp_id - - # redraw throughput - else: - self.canvas.itemconfig( - self.map[edge_id], - text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]), - ) - - def draw_throughputs(self): - for edge_id in self.tracker: - if self.edge_is_wired(self.edge_id_to_token[edge_id]): - self.draw_wired_throughput(edge_id) - else: - self.draw_wireless_throughput(edge_id) - - def process_grpc_throughput_event(self, interface_throughputs): - self.load_throughput_info(interface_throughputs) - self.draw_throughputs() - - def move(self, edge): - tpid = self.map.get(edge.id) - if tpid: - if self.edge_is_wired(edge.token): - x0, y0, x1, y1 = self.canvas.coords(edge.id) - self.canvas.coords(tpid, (x0 + x1) / 2, (y0 + y1) / 2) - else: - if ( - self.canvas.nodes[edge.src].core_node.type - == core_pb2.NodeType.WIRELESS_LAN - ): - x, y = self.canvas.coords(edge.dst) - self.canvas.coords(tpid, x + 50, y + 20) - else: - x, y = self.canvas.coords(edge.src) - self.canvas.coords(tpid, x + 50, y + 25) - - def delete(self, edge): - tpid = self.map.get(edge.id) - if tpid: - eid = edge.id - self.canvas.delete(tpid) - self.tracker.pop(eid) - self.map.pop(eid) - self.edge_id_to_token.pop(eid) diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py index b348fe15..c43cbe9c 100644 --- a/daemon/core/gui/graph/node.py +++ b/daemon/core/gui/graph/node.py @@ -128,7 +128,6 @@ class CanvasNode: self.canvas.coords(edge.id, x, y, x2, y2) else: self.canvas.coords(edge.id, x1, y1, x, y) - self.canvas.throughput_draw.move(edge) edge.update_labels() for edge in self.wireless_edges: diff --git a/daemon/core/gui/graph/tags.py b/daemon/core/gui/graph/tags.py index 42f4ff5f..763465b5 100644 --- a/daemon/core/gui/graph/tags.py +++ b/daemon/core/gui/graph/tags.py @@ -9,6 +9,7 @@ NODE_NAME = "nodename" NODE = "node" WALLPAPER = "wallpaper" SELECTION = "selectednodes" +THROUGHPUT = "throughput" ABOVE_WALLPAPER_TAGS = [ GRIDLINE, SHAPE, diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py index a4229ddd..9229f661 100644 --- a/daemon/core/gui/toolbar.py +++ b/daemon/core/gui/toolbar.py @@ -7,7 +7,6 @@ from tkinter.font import Font from core.gui.dialogs.customnodes import CustomNodesDialog from core.gui.dialogs.marker import MarkerDialog -from core.gui.graph import tags from core.gui.graph.enums import GraphMode from core.gui.graph.shapeutils import ShapeType, is_marker from core.gui.images import ImageEnum, Images @@ -404,7 +403,6 @@ class Toolbar(ttk.Frame): self.app.statusbar.progress_bar.start(5) thread = threading.Thread(target=self.app.core.stop_session) thread.start() - self.app.canvas.delete(tags.WIRELESS_EDGE) self.design_frame.tkraise() self.click_selection()