updated so that throughputs will update link color/width based on threshold
This commit is contained in:
parent
5dd08c283a
commit
2be0713ed1
7 changed files with 75 additions and 167 deletions
|
@ -212,9 +212,7 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
logging.info("handling throughputs event: %s", event)
|
logging.info("handling throughputs event: %s", event)
|
||||||
self.app.canvas.throughput_draw.process_grpc_throughput_event(
|
self.app.canvas.set_throughputs(event)
|
||||||
event.interface_throughputs
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle_exception_event(self, event):
|
def handle_exception_event(self, event):
|
||||||
logging.info("exception event: %s", event)
|
logging.info("exception event: %s", event)
|
||||||
|
@ -511,6 +509,7 @@ class CoreClient:
|
||||||
start = time.perf_counter()
|
start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
response = self.client.stop_session(session_id)
|
response = self.client.stop_session(session_id)
|
||||||
|
self.app.canvas.stopped_session()
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"stopped session(%s), result: %s", session_id, response.result
|
"stopped session(%s), result: %s", session_id, response.result
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,6 +8,8 @@ from core.gui.graph import tags
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
|
||||||
TEXT_DISTANCE = 0.30
|
TEXT_DISTANCE = 0.30
|
||||||
|
EDGE_WIDTH = 3
|
||||||
|
EDGE_COLOR = "#ff0000"
|
||||||
|
|
||||||
|
|
||||||
class CanvasWirelessEdge:
|
class CanvasWirelessEdge:
|
||||||
|
@ -29,8 +31,6 @@ class CanvasEdge:
|
||||||
Canvas edge class
|
Canvas edge class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
width = 3
|
|
||||||
|
|
||||||
def __init__(self, x1, y1, x2, y2, src, canvas):
|
def __init__(self, x1, y1, x2, y2, src, canvas):
|
||||||
"""
|
"""
|
||||||
Create an instance of canvas edge object
|
Create an instance of canvas edge object
|
||||||
|
@ -47,10 +47,11 @@ class CanvasEdge:
|
||||||
self.dst_interface = None
|
self.dst_interface = None
|
||||||
self.canvas = canvas
|
self.canvas = canvas
|
||||||
self.id = self.canvas.create_line(
|
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_src = None
|
||||||
self.text_dst = None
|
self.text_dst = None
|
||||||
|
self.text_middle = None
|
||||||
self.token = None
|
self.token = None
|
||||||
self.font = Font(size=8)
|
self.font = Font(size=8)
|
||||||
self.link = None
|
self.link = None
|
||||||
|
@ -77,6 +78,12 @@ class CanvasEdge:
|
||||||
y2 = y2 - uy
|
y2 = y2 - uy
|
||||||
return x1, y1, x2, y2
|
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):
|
def draw_labels(self):
|
||||||
x1, y1, x2, y2 = self.get_coordinates()
|
x1, y1, x2, y2 = self.get_coordinates()
|
||||||
label_one = None
|
label_one = None
|
||||||
|
@ -117,6 +124,28 @@ class CanvasEdge:
|
||||||
x1, y1, x2, y2 = self.get_coordinates()
|
x1, y1, x2, y2 = self.get_coordinates()
|
||||||
self.canvas.coords(self.text_src, x1, y1)
|
self.canvas.coords(self.text_src, x1, y1)
|
||||||
self.canvas.coords(self.text_dst, x2, y2)
|
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):
|
def complete(self, dst):
|
||||||
self.dst = dst
|
self.dst = dst
|
||||||
|
@ -128,14 +157,17 @@ class CanvasEdge:
|
||||||
self.canvas.tag_raise(self.src)
|
self.canvas.tag_raise(self.src)
|
||||||
self.canvas.tag_raise(self.dst)
|
self.canvas.tag_raise(self.dst)
|
||||||
|
|
||||||
def check_wireless(self):
|
def is_wireless(self):
|
||||||
src_node = self.canvas.nodes[self.src]
|
src_node = self.canvas.nodes[self.src]
|
||||||
dst_node = self.canvas.nodes[self.dst]
|
dst_node = self.canvas.nodes[self.dst]
|
||||||
src_node_type = src_node.core_node.type
|
src_node_type = src_node.core_node.type
|
||||||
dst_node_type = dst_node.core_node.type
|
dst_node_type = dst_node.core_node.type
|
||||||
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
|
is_src_wireless = NodeUtils.is_wireless_node(src_node_type)
|
||||||
is_dst_wireless = NodeUtils.is_wireless_node(dst_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.canvas.itemconfig(self.id, state=tk.HIDDEN)
|
||||||
self._check_antenna()
|
self._check_antenna()
|
||||||
|
|
||||||
|
@ -160,6 +192,12 @@ class CanvasEdge:
|
||||||
if self.link:
|
if self.link:
|
||||||
self.canvas.delete(self.text_src)
|
self.canvas.delete(self.text_src)
|
||||||
self.canvas.delete(self.text_dst)
|
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):
|
def create_context(self, event):
|
||||||
logging.debug("create link context")
|
logging.debug("create link context")
|
||||||
|
|
|
@ -9,7 +9,6 @@ from core.gui.dialogs.shapemod import ShapeDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||||
from core.gui.graph.enums import GraphMode, ScaleOption
|
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.node import CanvasNode
|
||||||
from core.gui.graph.shape import Shape
|
from core.gui.graph.shape import Shape
|
||||||
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||||
|
@ -38,7 +37,6 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.wireless_edges = {}
|
self.wireless_edges = {}
|
||||||
self.drawing_edge = None
|
self.drawing_edge = None
|
||||||
self.grid = None
|
self.grid = None
|
||||||
self.throughput_draw = Throughput(self, core)
|
|
||||||
self.shape_drawing = False
|
self.shape_drawing = False
|
||||||
self.default_dimensions = (width, height)
|
self.default_dimensions = (width, height)
|
||||||
self.current_dimensions = self.default_dimensions
|
self.current_dimensions = self.default_dimensions
|
||||||
|
@ -158,6 +156,21 @@ class CanvasGraph(tk.Canvas):
|
||||||
valid_bottomright = self.inside_canvas(x2, y2)
|
valid_bottomright = self.inside_canvas(x2, y2)
|
||||||
return valid_topleft and valid_bottomright
|
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):
|
def draw_grid(self):
|
||||||
"""
|
"""
|
||||||
Create grid.
|
Create grid.
|
||||||
|
@ -269,6 +282,20 @@ class CanvasGraph(tk.Canvas):
|
||||||
# raise the nodes so they on top of the links
|
# raise the nodes so they on top of the links
|
||||||
self.tag_raise(tags.NODE)
|
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):
|
def canvas_xy(self, event):
|
||||||
"""
|
"""
|
||||||
Convert window coordinate to canvas coordinate
|
Convert window coordinate to canvas coordinate
|
||||||
|
@ -446,9 +473,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
if edge in edges:
|
if edge in edges:
|
||||||
continue
|
continue
|
||||||
edges.add(edge)
|
edges.add(edge)
|
||||||
self.throughput_draw.delete(edge)
|
|
||||||
self.edges.pop(edge.token, None)
|
self.edges.pop(edge.token, None)
|
||||||
# del self.edges[edge.token]
|
|
||||||
edge.delete()
|
edge.delete()
|
||||||
|
|
||||||
# update node connected to edge being deleted
|
# update node connected to edge being deleted
|
||||||
|
|
|
@ -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)
|
|
|
@ -128,7 +128,6 @@ class CanvasNode:
|
||||||
self.canvas.coords(edge.id, x, y, x2, y2)
|
self.canvas.coords(edge.id, x, y, x2, y2)
|
||||||
else:
|
else:
|
||||||
self.canvas.coords(edge.id, x1, y1, x, y)
|
self.canvas.coords(edge.id, x1, y1, x, y)
|
||||||
self.canvas.throughput_draw.move(edge)
|
|
||||||
edge.update_labels()
|
edge.update_labels()
|
||||||
|
|
||||||
for edge in self.wireless_edges:
|
for edge in self.wireless_edges:
|
||||||
|
|
|
@ -9,6 +9,7 @@ NODE_NAME = "nodename"
|
||||||
NODE = "node"
|
NODE = "node"
|
||||||
WALLPAPER = "wallpaper"
|
WALLPAPER = "wallpaper"
|
||||||
SELECTION = "selectednodes"
|
SELECTION = "selectednodes"
|
||||||
|
THROUGHPUT = "throughput"
|
||||||
ABOVE_WALLPAPER_TAGS = [
|
ABOVE_WALLPAPER_TAGS = [
|
||||||
GRIDLINE,
|
GRIDLINE,
|
||||||
SHAPE,
|
SHAPE,
|
||||||
|
|
|
@ -7,7 +7,6 @@ from tkinter.font import Font
|
||||||
|
|
||||||
from core.gui.dialogs.customnodes import CustomNodesDialog
|
from core.gui.dialogs.customnodes import CustomNodesDialog
|
||||||
from core.gui.dialogs.marker import MarkerDialog
|
from core.gui.dialogs.marker import MarkerDialog
|
||||||
from core.gui.graph import tags
|
|
||||||
from core.gui.graph.enums import GraphMode
|
from core.gui.graph.enums import GraphMode
|
||||||
from core.gui.graph.shapeutils import ShapeType, is_marker
|
from core.gui.graph.shapeutils import ShapeType, is_marker
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
|
@ -404,7 +403,6 @@ class Toolbar(ttk.Frame):
|
||||||
self.app.statusbar.progress_bar.start(5)
|
self.app.statusbar.progress_bar.start(5)
|
||||||
thread = threading.Thread(target=self.app.core.stop_session)
|
thread = threading.Thread(target=self.app.core.stop_session)
|
||||||
thread.start()
|
thread.start()
|
||||||
self.app.canvas.delete(tags.WIRELESS_EDGE)
|
|
||||||
self.design_frame.tkraise()
|
self.design_frame.tkraise()
|
||||||
self.click_selection()
|
self.click_selection()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue