Merge branch 'coretk' into coretk-painttool
This commit is contained in:
commit
62afbbae56
12 changed files with 397 additions and 220 deletions
|
@ -445,13 +445,16 @@ class CoreClient:
|
|||
|
||||
def start_session(self):
|
||||
nodes = [x.core_node for x in self.canvas_nodes.values()]
|
||||
links = list(self.links.values())
|
||||
links = [x.link for x in self.links.values()]
|
||||
wlan_configs = self.get_wlan_configs_proto()
|
||||
mobility_configs = self.get_mobility_configs_proto()
|
||||
emane_model_configs = self.get_emane_model_configs_proto()
|
||||
hooks = list(self.hooks.values())
|
||||
service_configs = self.get_service_configs_proto()
|
||||
file_configs = self.get_service_file_configs_proto()
|
||||
asymmetric_links = [
|
||||
x.asymmetric_link for x in self.links.values() if x.asymmetric_link
|
||||
]
|
||||
if self.emane_config:
|
||||
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
||||
else:
|
||||
|
@ -471,6 +474,7 @@ class CoreClient:
|
|||
mobility_configs,
|
||||
service_configs,
|
||||
file_configs,
|
||||
asymmetric_links,
|
||||
)
|
||||
self.set_metadata()
|
||||
process_time = time.perf_counter() - start
|
||||
|
@ -602,7 +606,7 @@ class CoreClient:
|
|||
:return: nothing
|
||||
"""
|
||||
node_protos = [x.core_node for x in self.canvas_nodes.values()]
|
||||
link_protos = list(self.links.values())
|
||||
link_protos = [x.link for x in self.links.values()]
|
||||
if self.state != core_pb2.SessionState.DEFINITION:
|
||||
self.client.set_session_state(
|
||||
self.session_id, core_pb2.SessionState.DEFINITION
|
||||
|
@ -813,8 +817,8 @@ class CoreClient:
|
|||
interface_one=src_interface,
|
||||
interface_two=dst_interface,
|
||||
)
|
||||
self.links[edge.token] = link
|
||||
return link
|
||||
edge.set_link(link)
|
||||
self.links[edge.token] = edge
|
||||
|
||||
def get_wlan_configs_proto(self):
|
||||
configs = []
|
||||
|
|
|
@ -5,33 +5,54 @@ import logging
|
|||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.dialogs.colorpicker import ColorPicker
|
||||
from coretk.dialogs.dialog import Dialog
|
||||
from coretk.themes import PADX, PADY
|
||||
|
||||
|
||||
def get_int(var):
|
||||
value = var.get()
|
||||
if value != "":
|
||||
return int(value)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_float(var):
|
||||
value = var.get()
|
||||
if value != "":
|
||||
return float(value)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class LinkConfiguration(Dialog):
|
||||
def __init__(self, master, app, edge):
|
||||
super().__init__(master, app, "link configuration", modal=True)
|
||||
super().__init__(master, app, "Link Configuration", modal=True)
|
||||
self.app = app
|
||||
self.edge = edge
|
||||
self.is_symmetric = True
|
||||
self.is_symmetric = edge.link.options.unidirectional is False
|
||||
if self.is_symmetric:
|
||||
self.symmetry_var = tk.StringVar(value=">>")
|
||||
else:
|
||||
self.symmetry_var = tk.StringVar(value="<<")
|
||||
|
||||
self.bandwidth = tk.DoubleVar()
|
||||
self.delay = tk.DoubleVar()
|
||||
self.jitter = tk.DoubleVar()
|
||||
self.loss = tk.DoubleVar()
|
||||
self.duplicate = tk.DoubleVar()
|
||||
self.color = tk.StringVar(value="#000000")
|
||||
self.width = tk.DoubleVar()
|
||||
self.bandwidth = tk.StringVar()
|
||||
self.delay = tk.StringVar()
|
||||
self.jitter = tk.StringVar()
|
||||
self.loss = tk.StringVar()
|
||||
self.duplicate = tk.StringVar()
|
||||
|
||||
self.down_bandwidth = tk.DoubleVar()
|
||||
self.down_delay = tk.DoubleVar()
|
||||
self.down_jitter = tk.DoubleVar()
|
||||
self.down_loss = tk.DoubleVar()
|
||||
self.down_duplicate = tk.DoubleVar()
|
||||
self.down_bandwidth = tk.StringVar()
|
||||
self.down_delay = tk.StringVar()
|
||||
self.down_jitter = tk.StringVar()
|
||||
self.down_loss = tk.StringVar()
|
||||
self.down_duplicate = tk.StringVar()
|
||||
|
||||
self.color = tk.StringVar(value="#000000")
|
||||
self.color_button = None
|
||||
self.width = tk.DoubleVar()
|
||||
|
||||
self.load_link_config()
|
||||
self.symmetric_frame = None
|
||||
|
@ -44,17 +65,16 @@ class LinkConfiguration(Dialog):
|
|||
source_name = self.app.canvas.nodes[self.edge.src].core_node.name
|
||||
dest_name = self.app.canvas.nodes[self.edge.dst].core_node.name
|
||||
label = ttk.Label(
|
||||
self.top,
|
||||
text="Link from %s to %s" % (source_name, dest_name),
|
||||
anchor=tk.CENTER,
|
||||
self.top, text=f"Link from {source_name} to {dest_name}", anchor=tk.CENTER
|
||||
)
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
label.grid(row=0, column=0, sticky="ew", pady=PADY)
|
||||
|
||||
frame = ttk.Frame(self.top)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
frame.grid(row=1, column=0, sticky="nsew")
|
||||
button = ttk.Button(frame, text="unlimited")
|
||||
button.grid(row=0, column=0, sticky="nsew")
|
||||
frame.grid(row=1, column=0, sticky="ew", pady=PADY)
|
||||
button = ttk.Button(frame, text="Unlimited")
|
||||
button.grid(row=0, column=0, sticky="ew", padx=PADX)
|
||||
if self.is_symmetric:
|
||||
button = ttk.Button(
|
||||
frame, textvariable=self.symmetry_var, command=self.change_symmetry
|
||||
|
@ -63,128 +83,239 @@ class LinkConfiguration(Dialog):
|
|||
button = ttk.Button(
|
||||
frame, textvariable=self.symmetry_var, command=self.change_symmetry
|
||||
)
|
||||
button.grid(row=0, column=1, sticky="nsew")
|
||||
button.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
if self.is_symmetric:
|
||||
self.symmetric_frame = self.get_frame()
|
||||
self.symmetric_frame.grid(row=2, column=0, sticky="nsew")
|
||||
self.symmetric_frame.grid(row=2, column=0, sticky="ew", pady=PADY)
|
||||
else:
|
||||
self.asymmetric_frame = self.get_frame()
|
||||
self.asymmetric_frame.grid(row=2, column=0, sticky="nsew")
|
||||
self.asymmetric_frame.grid(row=2, column=0, sticky="ew", pady=PADY)
|
||||
|
||||
self.draw_spacer(row=3)
|
||||
|
||||
frame = ttk.Frame(self.top)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
frame.grid(row=3, column=0, sticky="nsew")
|
||||
|
||||
button = ttk.Button(frame, text="Apply", command=self.apply)
|
||||
button.grid(row=0, column=0, sticky="nsew")
|
||||
frame.grid(row=4, column=0, sticky="ew")
|
||||
button = ttk.Button(frame, text="Apply", command=self.click_apply)
|
||||
button.grid(row=0, column=0, sticky="ew", padx=PADX)
|
||||
button = ttk.Button(frame, text="Cancel", command=self.destroy)
|
||||
button.grid(row=0, column=1, sticky="nsew")
|
||||
button.grid(row=0, column=1, sticky="ew")
|
||||
|
||||
def get_frame(self):
|
||||
main_frame = ttk.Frame(self.top)
|
||||
main_frame.columnconfigure(0, weight=1)
|
||||
frame = ttk.Frame(self.top)
|
||||
frame.columnconfigure(1, weight=1)
|
||||
if self.is_symmetric:
|
||||
label_name = "Symmetric link effects: "
|
||||
label_name = "Symmetric Link Effects"
|
||||
else:
|
||||
label_name = "Asymmetric effects: downstream / upstream "
|
||||
label_name = "Asymmetric Effects: Downstream / Upstream "
|
||||
row = 0
|
||||
label = ttk.Label(main_frame, text=label_name, anchor=tk.CENTER)
|
||||
label.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text=label_name, anchor=tk.CENTER)
|
||||
label.grid(row=row, column=0, columnspan=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Bandwidth (bps): ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.bandwidth)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Bandwidth (bps)")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.bandwidth,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = ttk.Entry(frame, textvariable=self.down_bandwidth)
|
||||
entry.grid(row=0, column=2, sticky="nsew")
|
||||
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.down_bandwidth,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Delay (us): ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.delay)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Delay (us)")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.delay,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = ttk.Entry(frame, textvariable=self.down_delay)
|
||||
entry.grid(row=0, column=2, sticky="nsew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.down_delay,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Jitter (us): ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.jitter)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Jitter (us)")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.jitter,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = ttk.Entry(frame, textvariable=self.down_jitter)
|
||||
entry.grid(row=0, column=2, sticky="nsew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.down_jitter,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Loss (%): ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.loss)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Loss (%)")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.loss,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_float, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = ttk.Entry(frame, textvariable=self.down_loss)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.down_loss,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_float, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Duplicate (%): ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.duplicate)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Duplicate (%)")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.duplicate,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
if not self.is_symmetric:
|
||||
entry = ttk.Entry(frame, textvariable=self.down_duplicate)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.down_duplicate,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_int, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=2, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Color: ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
button = ttk.Button(frame, textvariable=self.color)
|
||||
button.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Color")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
self.color_button = tk.Button(
|
||||
frame,
|
||||
textvariable=self.color,
|
||||
background=self.color.get(),
|
||||
bd=0,
|
||||
relief=tk.FLAT,
|
||||
highlightthickness=0,
|
||||
command=self.click_color,
|
||||
)
|
||||
self.color_button.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
row = row + 1
|
||||
|
||||
frame = ttk.Frame(main_frame)
|
||||
frame.columnconfigure(0, weight=1)
|
||||
frame.columnconfigure(1, weight=4)
|
||||
frame.grid(row=row, column=0, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Width: ")
|
||||
label.grid(row=0, column=0, sticky="nsew")
|
||||
entry = ttk.Entry(frame, textvariable=self.width)
|
||||
entry.grid(row=0, column=1, sticky="nsew")
|
||||
label = ttk.Label(frame, text="Width")
|
||||
label.grid(row=row, column=0, sticky="ew")
|
||||
entry = ttk.Entry(
|
||||
frame,
|
||||
textvariable=self.width,
|
||||
validate="key",
|
||||
validatecommand=(self.app.validation.positive_float, "%P"),
|
||||
)
|
||||
entry.grid(row=row, column=1, sticky="ew", pady=PADY)
|
||||
|
||||
return main_frame
|
||||
return frame
|
||||
|
||||
def apply(self):
|
||||
def click_color(self):
|
||||
dialog = ColorPicker(self, self.app, self.color.get())
|
||||
color = dialog.askcolor()
|
||||
self.color.set(color)
|
||||
self.color_button.config(background=color)
|
||||
|
||||
def click_apply(self):
|
||||
logging.debug("click apply")
|
||||
width = self.width.get()
|
||||
self.app.canvas.itemconfigure(self.edge.id, width=width)
|
||||
self.app.canvas.itemconfigure(self.edge.id, width=self.width.get())
|
||||
self.app.canvas.itemconfigure(self.edge.id, fill=self.color.get())
|
||||
link = self.edge.link
|
||||
bandwidth = get_int(self.bandwidth)
|
||||
jitter = get_int(self.jitter)
|
||||
delay = get_int(self.delay)
|
||||
duplicate = get_int(self.duplicate)
|
||||
loss = get_float(self.loss)
|
||||
options = core_pb2.LinkOptions(
|
||||
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, per=loss
|
||||
)
|
||||
link.options.CopyFrom(options)
|
||||
|
||||
interface_one = None
|
||||
if link.HasField("interface_one"):
|
||||
interface_one = link.interface_one.id
|
||||
interface_two = None
|
||||
if link.HasField("interface_two"):
|
||||
interface_two = link.interface_two.id
|
||||
|
||||
if not self.is_symmetric:
|
||||
link.options.unidirectional = True
|
||||
asym_interface_one = None
|
||||
if interface_one:
|
||||
asym_interface_one = core_pb2.Interface(id=interface_one)
|
||||
asym_interface_two = None
|
||||
if interface_two:
|
||||
asym_interface_two = core_pb2.Interface(id=interface_two)
|
||||
down_bandwidth = get_int(self.down_bandwidth)
|
||||
down_jitter = get_int(self.down_jitter)
|
||||
down_delay = get_int(self.down_delay)
|
||||
down_duplicate = get_int(self.down_duplicate)
|
||||
down_loss = get_float(self.down_loss)
|
||||
options = core_pb2.LinkOptions(
|
||||
bandwidth=down_bandwidth,
|
||||
jitter=down_jitter,
|
||||
delay=down_delay,
|
||||
dup=down_duplicate,
|
||||
per=down_loss,
|
||||
unidirectional=True,
|
||||
)
|
||||
self.edge.asymmetric_link = core_pb2.Link(
|
||||
node_one_id=link.node_two_id,
|
||||
node_two_id=link.node_one_id,
|
||||
interface_one=asym_interface_one,
|
||||
interface_two=asym_interface_two,
|
||||
options=options,
|
||||
)
|
||||
else:
|
||||
link.options.unidirectional = False
|
||||
self.edge.asymmetric_link = None
|
||||
|
||||
if self.app.core.is_runtime() and link.HasField("options"):
|
||||
session_id = self.app.core.session_id
|
||||
self.app.core.client.edit_link(
|
||||
session_id,
|
||||
link.node_one_id,
|
||||
link.node_two_id,
|
||||
link.options,
|
||||
interface_one,
|
||||
interface_two,
|
||||
)
|
||||
if self.edge.asymmetric_link:
|
||||
self.app.core.client.edit_link(
|
||||
session_id,
|
||||
link.node_two_id,
|
||||
link.node_one_id,
|
||||
self.edge.asymmetric_link.options,
|
||||
interface_one,
|
||||
interface_two,
|
||||
)
|
||||
|
||||
self.destroy()
|
||||
|
||||
def change_symmetry(self):
|
||||
|
@ -212,6 +343,20 @@ class LinkConfiguration(Dialog):
|
|||
:return: nothing
|
||||
"""
|
||||
width = self.app.canvas.itemcget(self.edge.id, "width")
|
||||
# color = self.app.canvas.itemcget(self.edge.id, "fill")
|
||||
self.width.set(width)
|
||||
# self.color
|
||||
color = self.app.canvas.itemcget(self.edge.id, "fill")
|
||||
self.color.set(color)
|
||||
link = self.edge.link
|
||||
if link.HasField("options"):
|
||||
self.bandwidth.set(str(link.options.bandwidth))
|
||||
self.jitter.set(str(link.options.jitter))
|
||||
self.duplicate.set(str(link.options.dup))
|
||||
self.loss.set(str(link.options.per))
|
||||
self.delay.set(str(link.options.delay))
|
||||
if not self.is_symmetric:
|
||||
asym_link = self.edge.asymmetric_link
|
||||
self.down_bandwidth.set(str(asym_link.options.bandwidth))
|
||||
self.down_jitter.set(str(asym_link.options.jitter))
|
||||
self.down_duplicate.set(str(asym_link.options.dup))
|
||||
self.down_loss.set(str(asym_link.options.per))
|
||||
self.down_delay.set(str(asym_link.options.delay))
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import logging
|
||||
import tkinter as tk
|
||||
from tkinter.font import Font
|
||||
|
||||
from coretk import themes
|
||||
from coretk.dialogs.linkconfig import LinkConfiguration
|
||||
from coretk.graph import tags
|
||||
from coretk.nodeutils import NodeUtils
|
||||
|
||||
TEXT_DISTANCE = 0.30
|
||||
|
||||
|
||||
class CanvasWirelessEdge:
|
||||
def __init__(self, token, position, src, dst, canvas):
|
||||
|
@ -46,14 +49,75 @@ class CanvasEdge:
|
|||
self.id = self.canvas.create_line(
|
||||
x1, y1, x2, y2, tags=tags.EDGE, width=self.width, fill="#ff0000"
|
||||
)
|
||||
self.text_src = None
|
||||
self.text_dst = None
|
||||
self.token = None
|
||||
self.link_info = None
|
||||
self.font = Font(size=8)
|
||||
self.link = None
|
||||
self.asymmetric_link = None
|
||||
self.throughput = None
|
||||
self.set_binding()
|
||||
|
||||
def set_binding(self):
|
||||
self.canvas.tag_bind(self.id, "<ButtonRelease-3>", self.create_context)
|
||||
|
||||
def set_link(self, link):
|
||||
self.link = link
|
||||
self.draw_labels()
|
||||
|
||||
def get_coordinates(self):
|
||||
x1, y1, x2, y2 = self.canvas.coords(self.id)
|
||||
v1 = x2 - x1
|
||||
v2 = y2 - y1
|
||||
ux = TEXT_DISTANCE * v1
|
||||
uy = TEXT_DISTANCE * v2
|
||||
x1 = x1 + ux
|
||||
y1 = y1 + uy
|
||||
x2 = x2 - ux
|
||||
y2 = y2 - uy
|
||||
return x1, y1, x2, y2
|
||||
|
||||
def draw_labels(self):
|
||||
x1, y1, x2, y2 = self.get_coordinates()
|
||||
label_one = None
|
||||
if self.link.HasField("interface_one"):
|
||||
label_one = (
|
||||
f"{self.link.interface_one.ip4}/{self.link.interface_one.ip4mask}\n"
|
||||
f"{self.link.interface_one.ip6}/{self.link.interface_one.ip6mask}\n"
|
||||
)
|
||||
label_two = None
|
||||
if self.link.HasField("interface_two"):
|
||||
label_two = (
|
||||
f"{self.link.interface_two.ip4}/{self.link.interface_two.ip4mask}\n"
|
||||
f"{self.link.interface_two.ip6}/{self.link.interface_two.ip6mask}\n"
|
||||
)
|
||||
self.text_src = self.canvas.create_text(
|
||||
x1,
|
||||
y1,
|
||||
text=label_one,
|
||||
justify=tk.CENTER,
|
||||
font=self.font,
|
||||
tags=tags.LINK_INFO,
|
||||
)
|
||||
self.text_dst = self.canvas.create_text(
|
||||
x2,
|
||||
y2,
|
||||
text=label_two,
|
||||
justify=tk.CENTER,
|
||||
font=self.font,
|
||||
tags=tags.LINK_INFO,
|
||||
)
|
||||
|
||||
def update_labels(self):
|
||||
"""
|
||||
Move edge labels based on current position.
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
x1, y1, x2, y2 = self.get_coordinates()
|
||||
self.canvas.coords(self.text_src, x1, y1)
|
||||
self.canvas.coords(self.text_dst, x2, y2)
|
||||
|
||||
def complete(self, dst):
|
||||
self.dst = dst
|
||||
self.token = tuple(sorted((self.src, self.dst)))
|
||||
|
@ -93,9 +157,9 @@ class CanvasEdge:
|
|||
|
||||
def delete(self):
|
||||
self.canvas.delete(self.id)
|
||||
if self.link_info:
|
||||
self.canvas.delete(self.link_info.id1)
|
||||
self.canvas.delete(self.link_info.id2)
|
||||
if self.link:
|
||||
self.canvas.delete(self.text_src)
|
||||
self.canvas.delete(self.text_dst)
|
||||
|
||||
def create_context(self, event):
|
||||
logging.debug("create link context")
|
||||
|
|
|
@ -9,7 +9,7 @@ from coretk.dialogs.shapemod import ShapeDialog
|
|||
from coretk.graph import tags
|
||||
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||
from coretk.graph.enums import GraphMode, ScaleOption
|
||||
from coretk.graph.linkinfo import LinkInfo, Throughput
|
||||
from coretk.graph.linkinfo import Throughput
|
||||
from coretk.graph.node import CanvasNode
|
||||
from coretk.graph.shape import Shape
|
||||
from coretk.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||
|
@ -202,12 +202,12 @@ class CanvasGraph(tk.Canvas):
|
|||
"""
|
||||
# draw existing nodes
|
||||
for core_node in session.nodes:
|
||||
logging.info("drawing core node: %s", core_node)
|
||||
# peer to peer node is not drawn on the GUI
|
||||
if NodeUtils.is_ignore_node(core_node.type):
|
||||
continue
|
||||
|
||||
# draw nodes on the canvas
|
||||
logging.info("drawing core node: %s", core_node)
|
||||
image = NodeUtils.node_icon(core_node.type, core_node.model)
|
||||
if core_node.icon:
|
||||
try:
|
||||
|
@ -223,13 +223,17 @@ class CanvasGraph(tk.Canvas):
|
|||
|
||||
# draw existing links
|
||||
for link in session.links:
|
||||
logging.info("drawing link: %s", link)
|
||||
canvas_node_one = self.core.canvas_nodes[link.node_one_id]
|
||||
node_one = canvas_node_one.core_node
|
||||
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
|
||||
node_two = canvas_node_two.core_node
|
||||
token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
|
||||
|
||||
if link.type == core_pb2.LinkType.WIRELESS:
|
||||
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
||||
else:
|
||||
if token not in self.edges:
|
||||
edge = CanvasEdge(
|
||||
node_one.position.x,
|
||||
node_one.position.y,
|
||||
|
@ -238,18 +242,23 @@ class CanvasGraph(tk.Canvas):
|
|||
canvas_node_one.id,
|
||||
self,
|
||||
)
|
||||
edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
|
||||
edge.token = token
|
||||
edge.dst = canvas_node_two.id
|
||||
edge.set_link(link)
|
||||
edge.check_wireless()
|
||||
canvas_node_one.edges.add(edge)
|
||||
canvas_node_two.edges.add(edge)
|
||||
self.edges[edge.token] = edge
|
||||
self.core.links[edge.token] = link
|
||||
edge.link_info = LinkInfo(self, edge, link)
|
||||
self.core.links[edge.token] = edge
|
||||
if link.HasField("interface_one"):
|
||||
canvas_node_one.interfaces.append(link.interface_one)
|
||||
if link.HasField("interface_two"):
|
||||
canvas_node_two.interfaces.append(link.interface_two)
|
||||
elif link.options.unidirectional:
|
||||
edge = self.edges[token]
|
||||
edge.asymmetric_link = link
|
||||
else:
|
||||
logging.error("duplicate link received: %s", link)
|
||||
|
||||
# raise the nodes so they on top of the links
|
||||
self.tag_raise(tags.NODE)
|
||||
|
@ -373,8 +382,7 @@ class CanvasGraph(tk.Canvas):
|
|||
node_src.edges.add(edge)
|
||||
node_dst = self.nodes[edge.dst]
|
||||
node_dst.edges.add(edge)
|
||||
link = self.core.create_link(edge, node_src, node_dst)
|
||||
edge.link_info = LinkInfo(self, edge, link)
|
||||
self.core.create_link(edge, node_src, node_dst)
|
||||
|
||||
def select_object(self, object_id, choose_multiple=False):
|
||||
"""
|
||||
|
@ -819,7 +827,7 @@ class CanvasGraph(tk.Canvas):
|
|||
:param CanvasNode dest: destination node
|
||||
:return: nothing
|
||||
"""
|
||||
if tuple([source.id, dest.id]) not in self.edges:
|
||||
if (source.id, dest.id) not in self.edges:
|
||||
pos0 = source.core_node.position
|
||||
x0 = pos0.x
|
||||
y0 = pos0.y
|
||||
|
@ -828,5 +836,4 @@ class CanvasGraph(tk.Canvas):
|
|||
self.edges[edge.token] = edge
|
||||
self.nodes[source.id].edges.add(edge)
|
||||
self.nodes[dest.id].edges.add(edge)
|
||||
link = self.core.create_link(edge, source, dest)
|
||||
edge.link_info = LinkInfo(self, edge, link)
|
||||
self.core.create_link(edge, source, dest)
|
||||
|
|
|
@ -1,83 +1,8 @@
|
|||
"""
|
||||
Link information, such as IPv4, IPv6 and throughput drawn in the canvas
|
||||
"""
|
||||
import tkinter as tk
|
||||
from tkinter import font
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.graph import tags
|
||||
|
||||
TEXT_DISTANCE = 0.30
|
||||
|
||||
|
||||
class LinkInfo:
|
||||
def __init__(self, canvas, edge, link):
|
||||
"""
|
||||
create an instance of LinkInfo object
|
||||
:param coretk.graph.Graph canvas: canvas object
|
||||
:param coretk.graph.CanvasEdge edge: canvas edge onject
|
||||
:param link: core link to draw info for
|
||||
"""
|
||||
self.canvas = canvas
|
||||
self.edge = edge
|
||||
self.link = link
|
||||
self.id1 = None
|
||||
self.id2 = None
|
||||
self.font = font.Font(size=8)
|
||||
self.draw_labels()
|
||||
|
||||
def get_coordinates(self):
|
||||
x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
|
||||
v1 = x2 - x1
|
||||
v2 = y2 - y1
|
||||
ux = TEXT_DISTANCE * v1
|
||||
uy = TEXT_DISTANCE * v2
|
||||
x1 = x1 + ux
|
||||
y1 = y1 + uy
|
||||
x2 = x2 - ux
|
||||
y2 = y2 - uy
|
||||
return x1, y1, x2, y2
|
||||
|
||||
def draw_labels(self):
|
||||
x1, y1, x2, y2 = self.get_coordinates()
|
||||
label_one = None
|
||||
if self.link.HasField("interface_one"):
|
||||
label_one = (
|
||||
f"{self.link.interface_one.ip4}/{self.link.interface_one.ip4mask}\n"
|
||||
f"{self.link.interface_one.ip6}/{self.link.interface_one.ip6mask}\n"
|
||||
)
|
||||
label_two = None
|
||||
if self.link.HasField("interface_two"):
|
||||
label_two = (
|
||||
f"{self.link.interface_two.ip4}/{self.link.interface_two.ip4mask}\n"
|
||||
f"{self.link.interface_two.ip6}/{self.link.interface_two.ip6mask}\n"
|
||||
)
|
||||
self.id1 = self.canvas.create_text(
|
||||
x1,
|
||||
y1,
|
||||
text=label_one,
|
||||
justify=tk.CENTER,
|
||||
font=self.font,
|
||||
tags=tags.LINK_INFO,
|
||||
)
|
||||
self.id2 = self.canvas.create_text(
|
||||
x2,
|
||||
y2,
|
||||
text=label_two,
|
||||
justify=tk.CENTER,
|
||||
font=self.font,
|
||||
tags=tags.LINK_INFO,
|
||||
)
|
||||
|
||||
def recalculate_info(self):
|
||||
"""
|
||||
move the node info when the canvas node move
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
x1, y1, x2, y2 = self.get_coordinates()
|
||||
self.canvas.coords(self.id1, x1, y1)
|
||||
self.canvas.coords(self.id2, x2, y2)
|
||||
|
||||
|
||||
class Throughput:
|
||||
|
|
|
@ -129,7 +129,7 @@ class CanvasNode:
|
|||
else:
|
||||
self.canvas.coords(edge.id, x1, y1, x, y)
|
||||
self.canvas.throughput_draw.move(edge)
|
||||
edge.link_info.recalculate_info()
|
||||
edge.update_labels()
|
||||
|
||||
for edge in self.wireless_edges:
|
||||
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
||||
|
|
|
@ -161,6 +161,7 @@ class CoreGrpcClient:
|
|||
mobility_configs=None,
|
||||
service_configs=None,
|
||||
service_file_configs=None,
|
||||
asymmetric_links=None,
|
||||
):
|
||||
"""
|
||||
Start a session.
|
||||
|
@ -176,6 +177,7 @@ class CoreGrpcClient:
|
|||
:param list mobility_configs: node mobility configurations
|
||||
:param list service_configs: node service configurations
|
||||
:param list service_file_configs: node service file configurations
|
||||
:param list asymmetric_links: asymmetric links to edit
|
||||
:return: start session response
|
||||
:rtype: core_pb2.StartSessionResponse
|
||||
"""
|
||||
|
@ -191,6 +193,7 @@ class CoreGrpcClient:
|
|||
mobility_configs=mobility_configs,
|
||||
service_configs=service_configs,
|
||||
service_file_configs=service_file_configs,
|
||||
asymmetric_links=asymmetric_links,
|
||||
)
|
||||
return self.stub.StartSession(request)
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ def create_nodes(session, node_protos):
|
|||
|
||||
def create_links(session, link_protos):
|
||||
"""
|
||||
Create nodes using a thread pool and wait for completion.
|
||||
Create links using a thread pool and wait for completion.
|
||||
|
||||
:param core.emulator.session.Session session: session to create nodes in
|
||||
:param list[core_pb2.Link] link_protos: link proto messages
|
||||
|
@ -149,6 +149,29 @@ def create_links(session, link_protos):
|
|||
return results, exceptions
|
||||
|
||||
|
||||
def edit_links(session, link_protos):
|
||||
"""
|
||||
Edit links using a thread pool and wait for completion.
|
||||
|
||||
:param core.emulator.session.Session session: session to create nodes in
|
||||
:param list[core_pb2.Link] link_protos: link proto messages
|
||||
:return: results and exceptions for created links
|
||||
:rtype: tuple
|
||||
"""
|
||||
funcs = []
|
||||
for link_proto in link_protos:
|
||||
node_one_id = link_proto.node_one_id
|
||||
node_two_id = link_proto.node_two_id
|
||||
interface_one, interface_two, options = add_link_data(link_proto)
|
||||
args = (node_one_id, node_two_id, interface_one.id, interface_two.id, options)
|
||||
funcs.append((session.update_link, args, {}))
|
||||
start = time.monotonic()
|
||||
results, exceptions = utils.threadpool(funcs)
|
||||
total = time.monotonic() - start
|
||||
logging.debug("grpc edit links time: %s", total)
|
||||
return results, exceptions
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
"""
|
||||
Convert value into string.
|
||||
|
|
|
@ -158,6 +158,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
# create links
|
||||
grpcutils.create_links(session, request.links)
|
||||
|
||||
# asymmetric links
|
||||
grpcutils.edit_links(session, request.asymmetric_links)
|
||||
|
||||
# set to instantiation and start
|
||||
session.set_state(EventTypes.INSTANTIATION_STATE)
|
||||
session.instantiate()
|
||||
|
|
|
@ -1053,6 +1053,7 @@ class CoreNetworkBase(NodeBase):
|
|||
message_type=0,
|
||||
node1_id=linked_node.id,
|
||||
node2_id=self.id,
|
||||
link_type=self.linktype,
|
||||
unidirectional=1,
|
||||
delay=netif.getparam("delay"),
|
||||
bandwidth=netif.getparam("bw"),
|
||||
|
|
|
@ -965,6 +965,7 @@ class PtpNet(CoreNetwork):
|
|||
if unidirectional:
|
||||
link_data = LinkData(
|
||||
message_type=0,
|
||||
link_type=self.linktype,
|
||||
node1_id=if2.node.id,
|
||||
node2_id=if1.node.id,
|
||||
delay=if2.getparam("delay"),
|
||||
|
|
|
@ -150,6 +150,7 @@ message StartSessionRequest {
|
|||
repeated MobilityConfig mobility_configs = 9;
|
||||
repeated ServiceConfig service_configs = 10;
|
||||
repeated ServiceFileConfig service_file_configs = 11;
|
||||
repeated Link asymmetric_links = 12;
|
||||
}
|
||||
|
||||
message StartSessionResponse {
|
||||
|
|
Loading…
Reference in a new issue