diff --git a/coretk/coretk/canvasaction.py b/coretk/coretk/canvasaction.py deleted file mode 100644 index 509b9723..00000000 --- a/coretk/coretk/canvasaction.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -canvas graph action -""" -from core.api.grpc import core_pb2 -from coretk.dialogs.emaneconfig import EmaneConfiguration -from coretk.dialogs.nodeconfig import NodeConfigDialog -from coretk.dialogs.wlanconfig import WlanConfigDialog -from coretk.nodeutils import NodeUtils - - -class CanvasAction: - def __init__(self, master, canvas): - self.master = master - self.canvas = canvas - self.node_to_show_config = None - - def display_configuration(self, canvas_node): - node_type = canvas_node.core_node.type - self.node_to_show_config = canvas_node - if NodeUtils.is_container_node(node_type): - self.display_node_configuration() - elif node_type == core_pb2.NodeType.WIRELESS_LAN: - self.display_wlan_configuration(canvas_node) - elif node_type == core_pb2.NodeType.EMANE: - self.display_emane_configuration() - - def display_node_configuration(self): - dialog = NodeConfigDialog(self.master, self.master, self.node_to_show_config) - dialog.show() - self.node_to_show_config = None - - def display_wlan_configuration(self, canvas_node): - wlan_config = self.master.core.wlanconfig_management.configurations[ - canvas_node.core_node.id - ] - dialog = WlanConfigDialog( - self.master, self.master, self.node_to_show_config, wlan_config - ) - dialog.show() - self.node_to_show_config = None - - def display_emane_configuration(self): - app = self.canvas.core.app - dialog = EmaneConfiguration(self.master, app, self.node_to_show_config) - dialog.show() diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index f6630a0c..fed8040a 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -12,7 +12,6 @@ from coretk.mobilitynodeconfig import MobilityNodeConfig from coretk.nodeutils import NodeDraw, NodeUtils from coretk.servicefileconfig import ServiceFileConfig from coretk.servicenodeconfig import ServiceNodeConfig -from coretk.wlannodeconfig import WlanNodeConfig OBSERVERS = { "processes": "ps", @@ -70,7 +69,7 @@ class CoreClient: self.reusable = [] self.preexisting = set() self.interfaces_manager = InterfaceManager() - self.wlanconfig_management = WlanNodeConfig() + self.wlan_configs = {} self.mobilityconfig_management = MobilityNodeConfig() self.emaneconfig_management = EmaneModelNodeConfig(app) self.emane_config = None @@ -136,7 +135,7 @@ class CoreClient: self.canvas_nodes.clear() self.links.clear() self.hooks.clear() - self.wlanconfig_management.configurations.clear() + self.wlan_configs.clear() self.mobilityconfig_management.configurations.clear() self.emane_config = None @@ -158,9 +157,7 @@ class CoreClient: if node.type == core_pb2.NodeType.WIRELESS_LAN: response = self.client.get_wlan_config(self.session_id, node.id) logging.debug("wlan config(%s): %s", node.id, response) - node_config = response.config - config = {x: node_config[x].value for x in node_config} - self.wlanconfig_management.configurations[node.id] = config + self.wlan_configs[node.id] = response.config # get mobility configs response = self.client.get_mobility_configs(self.session_id) @@ -458,7 +455,6 @@ class CoreClient: ) # set default configuration for wireless node - self.wlanconfig_management.set_default_config(node_type, node_id) self.mobilityconfig_management.set_default_configuration(node_type, node_id) # set default emane configuration for emane node @@ -525,8 +521,8 @@ class CoreClient: for i in node_ids: if i in self.mobilityconfig_management.configurations: self.mobilityconfig_management.configurations.pop(i) - if i in self.wlanconfig_management.configurations: - self.wlanconfig_management.configurations.pop(i) + if i in self.wlan_configs: + del self.wlan_configs[i] # delete emane configurations for i in node_interface_pairs: @@ -611,11 +607,10 @@ class CoreClient: def get_wlan_configs_proto(self): configs = [] - wlan_configs = self.wlanconfig_management.configurations - for node_id in wlan_configs: - config = wlan_configs[node_id] - config_proto = core_pb2.WlanConfig(node_id=node_id, config=config) - configs.append(config_proto) + for node_id, config in self.wlan_configs.items(): + config = {x: config[x].value for x in config} + wlan_config = core_pb2.WlanConfig(node_id=node_id, config=config) + configs.append(wlan_config) return configs def get_mobility_configs_proto(self): @@ -675,3 +670,11 @@ class CoreClient: def run(self, node_id): logging.info("running node(%s) cmd: %s", node_id, self.observer) return self.client.node_command(self.session_id, node_id, self.observer).output + + def get_wlan_config(self, node_id): + config = self.wlan_configs.get(node_id) + if not config: + response = self.client.get_wlan_config(self.session_id, node_id) + config = response.config + self.wlan_configs[node_id] = config + return config diff --git a/coretk/coretk/dialogs/nodeconfig.py b/coretk/coretk/dialogs/nodeconfig.py index 4ed4baa6..8b9fcfab 100644 --- a/coretk/coretk/dialogs/nodeconfig.py +++ b/coretk/coretk/dialogs/nodeconfig.py @@ -107,8 +107,8 @@ class NodeConfigDialog(Dialog): entry.grid(row=row, column=1, sticky="ew") row += 1 - # server if NodeUtils.is_container_node(self.node.type): + # server frame.grid(sticky="ew") frame.columnconfigure(1, weight=1) label = ttk.Label(frame, text="Server") @@ -121,9 +121,9 @@ class NodeConfigDialog(Dialog): combobox.grid(row=row, column=1, sticky="ew") row += 1 - # services - button = ttk.Button(self.top, text="Services", command=self.click_services) - button.grid(sticky="ew", pady=PAD) + # services + button = ttk.Button(self.top, text="Services", command=self.click_services) + button.grid(sticky="ew", pady=PAD) # interfaces if self.canvas_node.interfaces: diff --git a/coretk/coretk/dialogs/wlanconfig.py b/coretk/coretk/dialogs/wlanconfig.py index 4f213e68..3973efb5 100644 --- a/coretk/coretk/dialogs/wlanconfig.py +++ b/coretk/coretk/dialogs/wlanconfig.py @@ -2,152 +2,33 @@ wlan configuration """ -import tkinter as tk from tkinter import ttk from coretk.dialogs.dialog import Dialog -from coretk.dialogs.icondialog import IconDialog from coretk.dialogs.mobilityconfig import MobilityConfigDialog +from coretk.widgets import ConfigFrame + +PAD = 5 class WlanConfigDialog(Dialog): - def __init__(self, master, app, canvas_node, config): + def __init__(self, master, app, canvas_node): super().__init__( master, app, f"{canvas_node.core_node.name} Wlan Configuration", modal=True ) - self.image = canvas_node.image self.canvas_node = canvas_node self.node = canvas_node.core_node - self.config = config - - self.name = tk.StringVar(value=self.node.name) - self.range_var = tk.StringVar(value=config["range"]) - self.bandwidth_var = tk.StringVar(value=config["bandwidth"]) - self.delay_var = tk.StringVar(value=config["delay"]) - self.loss_var = tk.StringVar(value=config["error"]) - self.jitter_var = tk.StringVar(value=config["jitter"]) - self.ip4_subnet = tk.StringVar() - self.ip6_subnet = tk.StringVar() - self.image_button = None + self.config_frame = None + self.config = self.app.core.get_wlan_config(self.node.id) self.draw() def draw(self): self.top.columnconfigure(0, weight=1) - self.draw_name_config() - self.draw_wlan_config() - self.draw_subnet() - self.draw_wlan_buttons() + self.config_frame = ConfigFrame(self.top, self.app, self.config, borderwidth=0) + self.config_frame.draw_config() + self.config_frame.grid(sticky="nsew", pady=PAD) self.draw_apply_buttons() - def draw_name_config(self): - """ - draw image modification part - - :return: nothing - """ - frame = ttk.Frame(self.top) - frame.grid(pady=2, sticky="ew") - frame.columnconfigure(0, weight=1) - - entry = ttk.Entry(frame, textvariable=self.name) - entry.grid(row=0, column=0, padx=2, sticky="ew") - - self.image_button = ttk.Button(frame, image=self.image, command=self.click_icon) - self.image_button.grid(row=0, column=1, padx=3) - - def draw_wlan_config(self): - """ - create wireless configuration table - - :return: nothing - """ - label = ttk.Label(self.top, text="Wireless") - label.grid(sticky="w", pady=2) - - frame = ttk.Frame(self.top) - frame.grid(pady=2, sticky="ew") - for i in range(2): - frame.columnconfigure(i, weight=1) - - label = ttk.Label( - frame, - text=( - "The basic range model calculates on/off " - "connectivity based on pixel distance between nodes." - ), - ) - label.grid(row=0, columnspan=2, pady=2, sticky="ew") - - label = ttk.Label(frame, text="Range") - label.grid(row=1, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.range_var) - entry.grid(row=1, column=1, sticky="ew") - - label = ttk.Label(frame, text="Bandwidth (bps)") - label.grid(row=2, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.bandwidth_var) - entry.grid(row=2, column=1, sticky="ew") - - label = ttk.Label(frame, text="Delay (us)") - label.grid(row=3, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.delay_var) - entry.grid(row=3, column=1, sticky="ew") - - label = ttk.Label(frame, text="Loss (%)") - label.grid(row=4, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.loss_var) - entry.grid(row=4, column=1, sticky="ew") - - label = ttk.Label(frame, text="Jitter (us)") - label.grid(row=5, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.jitter_var) - entry.grid(row=5, column=1, sticky="ew") - - def draw_subnet(self): - """ - create the entries for ipv4 subnet and ipv6 subnet - - :return: nothing - """ - - frame = ttk.Frame(self.top) - frame.grid(pady=3, sticky="ew") - frame.columnconfigure(1, weight=1) - frame.columnconfigure(3, weight=1) - - label = ttk.Label(frame, text="IPv4 Subnet") - label.grid(row=0, column=0, sticky="w") - entry = ttk.Entry(frame, textvariable=self.ip4_subnet) - entry.grid(row=0, column=1, sticky="ew") - - label = ttk.Label(frame, text="IPv6 Subnet") - label.grid(row=0, column=2, sticky="w") - entry = ttk.Entry(frame, textvariable=self.ip6_subnet) - entry.grid(row=0, column=3, sticky="ew") - - def draw_wlan_buttons(self): - """ - create wireless node options - - :return: - """ - - frame = ttk.Frame(self.top) - frame.grid(pady=2, sticky="ew") - for i in range(3): - frame.columnconfigure(i, weight=1) - - button = ttk.Button( - frame, text="ns-2 mobility script...", command=self.click_mobility - ) - button.grid(row=0, column=0, padx=2, sticky="ew") - - button = ttk.Button(frame, text="Link to all routers") - button.grid(row=0, column=1, padx=2, sticky="ew") - - button = ttk.Button(frame, text="Choose WLAN members") - button.grid(row=0, column=2, padx=2, sticky="ew") - def draw_apply_buttons(self): """ create node configuration options @@ -160,42 +41,21 @@ class WlanConfigDialog(Dialog): frame.columnconfigure(i, weight=1) button = ttk.Button(frame, text="Apply", command=self.click_apply) - button.grid(row=0, column=0, padx=2, sticky="ew") + button.grid(row=0, column=0, padx=PAD, sticky="ew") button = ttk.Button(frame, text="Cancel", command=self.destroy) - button.grid(row=0, column=1, padx=2, sticky="ew") + button.grid(row=0, column=1, sticky="ew") def click_mobility(self): dialog = MobilityConfigDialog(self, self.app, self.canvas_node) dialog.show() - def click_icon(self): - dialog = IconDialog(self, self.app, self.node.name, self.canvas_node.image) - dialog.show() - if dialog.image: - self.image = dialog.image - self.image_button.config(image=self.image) - def click_apply(self): """ retrieve user's wlan configuration and store the new configuration values :return: nothing """ - basic_range = self.range_var.get() - bandwidth = self.bandwidth_var.get() - delay = self.delay_var.get() - loss = self.loss_var.get() - jitter = self.jitter_var.get() - - # set wireless node configuration here - wlanconfig_manager = self.app.core.wlanconfig_management - wlanconfig_manager.set_custom_config( - node_id=self.node.id, - range=basic_range, - bandwidth=bandwidth, - jitter=jitter, - delay=delay, - error=loss, - ) + self.config_frame.parse_config() + self.app.core.wlan_configs[self.node.id] = self.config self.destroy() diff --git a/coretk/coretk/graph.py b/coretk/coretk/graph.py index 3eb002e6..86ce617e 100644 --- a/coretk/coretk/graph.py +++ b/coretk/coretk/graph.py @@ -5,8 +5,11 @@ import tkinter as tk from PIL import ImageTk from core.api.grpc import core_pb2 -from coretk.canvasaction import CanvasAction +from core.api.grpc.core_pb2 import NodeType from coretk.canvastooltip import CanvasTooltip +from coretk.dialogs.mobilityconfig import MobilityConfigDialog +from coretk.dialogs.nodeconfig import NodeConfigDialog +from coretk.dialogs.wlanconfig import WlanConfigDialog from coretk.graph_helper import GraphHelper, WlanAntennaManager from coretk.images import Images from coretk.linkinfo import LinkInfo, Throughput @@ -38,24 +41,21 @@ class CanvasGraph(tk.Canvas): kwargs["highlightthickness"] = 0 super().__init__(master, cnf, **kwargs) self.mode = GraphMode.SELECT - self.node_draw = None self.selected = None - self.node_context = None + self.node_draw = None + self.context = None self.nodes = {} self.edges = {} self.drawing_edge = None self.grid = None self.meters_per_pixel = 1.5 self.canvas_management = CanvasComponentManagement(self, core) - self.canvas_action = CanvasAction(master, self) - self.setup_menus() self.setup_bindings() self.draw_grid() self.core = core self.helper = GraphHelper(self, core) self.throughput_draw = Throughput(self, core) self.wireless_draw = WirelessConnection(self, core) - self.is_node_context_opened = False # background related self.wallpaper_id = None @@ -66,21 +66,28 @@ class CanvasGraph(tk.Canvas): self.show_grid = tk.BooleanVar(value=True) self.adjust_to_dim = tk.BooleanVar(value=False) - def setup_menus(self): - self.node_context = tk.Menu(self.master) - self.node_context.add_command( - label="Configure", command=self.canvas_action.display_node_configuration - ) - self.node_context.add_command(label="Select adjacent") - self.node_context.add_command(label="Create link to") - self.node_context.add_command(label="Assign to") - self.node_context.add_command(label="Move to") - self.node_context.add_command(label="Cut") - self.node_context.add_command(label="Copy") - self.node_context.add_command(label="Paste") - self.node_context.add_command(label="Delete") - self.node_context.add_command(label="Hide") - self.node_context.add_command(label="Services") + def create_node_context(self, canvas_node): + node = canvas_node.core_node + context = tk.Menu(self.master) + context.add_command(label="Configure", command=canvas_node.show_config) + if node.type == NodeType.WIRELESS_LAN: + context.add_command( + label="WLAN Config", command=canvas_node.show_wlan_config + ) + context.add_command( + label="Mobility Config", command=canvas_node.show_mobility_config + ) + context.add_command(label="Select adjacent", state=tk.DISABLED) + context.add_command(label="Create link to", state=tk.DISABLED) + context.add_command(label="Assign to", state=tk.DISABLED) + context.add_command(label="Move to", state=tk.DISABLED) + context.add_command(label="Cut", state=tk.DISABLED) + context.add_command(label="Copy", state=tk.DISABLED) + context.add_command(label="Paste", state=tk.DISABLED) + context.add_command(label="Delete", state=tk.DISABLED) + context.add_command(label="Hide", state=tk.DISABLED) + context.add_command(label="Services", state=tk.DISABLED) + return context def reset_and_redraw(self, session): """ @@ -97,7 +104,6 @@ class CanvasGraph(tk.Canvas): self.mode = GraphMode.SELECT self.node_draw = None self.selected = None - self.node_context = None self.nodes.clear() self.edges.clear() self.drawing_edge = None @@ -112,7 +118,7 @@ class CanvasGraph(tk.Canvas): self.bind("", self.click_press) self.bind("", self.click_release) self.bind("", self.click_motion) - self.bind("", self.context) + self.bind("", self.click_context) self.bind("", self.press_delete) def draw_grid(self, width=1000, height=800): @@ -254,9 +260,9 @@ class CanvasGraph(tk.Canvas): :param event: mouse event :return: nothing """ - if self.is_node_context_opened: - self.node_context.unpost() - self.is_node_context_opened = False + if self.context: + self.context.unpost() + self.context = None else: self.focus_set() self.selected = self.get_selected(event) @@ -350,18 +356,18 @@ class CanvasGraph(tk.Canvas): x1, y1, _, _ = self.coords(self.drawing_edge.id) self.coords(self.drawing_edge.id, x1, y1, x2, y2) - def context(self, event): - if not self.is_node_context_opened: + def click_context(self, event): + logging.info("context event: %s", self.context) + if not self.context: selected = self.get_selected(event) - nodes = self.find_withtag("node") - if selected in nodes: + canvas_node = self.nodes.get(selected) + if canvas_node: logging.debug(f"node context: {selected}") - self.node_context.post(event.x_root, event.y_root) - self.canvas_action.node_to_show_config = self.nodes[selected] - self.is_node_context_opened = True + self.context = self.create_node_context(canvas_node) + self.context.post(event.x_root, event.y_root) else: - self.node_context.unpost() - self.is_node_context_opened = False + self.context.unpost() + self.context = None # TODO rather than delete, might move the data to somewhere else in order to reuse # TODO when the user undo @@ -443,7 +449,6 @@ class CanvasGraph(tk.Canvas): """ place the image at the center of canvas - :param Image img: image object :return: nothing """ tk_img = ImageTk.PhotoImage(self.wallpaper) @@ -609,7 +614,6 @@ class CanvasNode: self.canvas.tag_bind(self.id, "", self.click_press) self.canvas.tag_bind(self.id, "", self.click_release) self.canvas.tag_bind(self.id, "", self.motion) - self.canvas.tag_bind(self.id, "", self.context) self.canvas.tag_bind(self.id, "", self.double_click) self.canvas.tag_bind(self.id, "", self.select_multiple) self.canvas.tag_bind(self.id, "", self.on_enter) @@ -641,7 +645,7 @@ class CanvasNode: if self.app.core.is_runtime(): self.canvas.core.launch_terminal(self.core_node.id) else: - self.canvas.canvas_action.display_configuration(self) + self.show_config() def update_coords(self): x, y = self.canvas.coords(self.id) @@ -693,5 +697,17 @@ class CanvasNode: def select_multiple(self, event): self.canvas.canvas_management.node_select(self, True) - def context(self, event): - logging.debug(f"context click {self.core_node.name}: {event}") + def show_config(self): + self.canvas.context = None + dialog = NodeConfigDialog(self.app, self.app, self) + dialog.show() + + def show_wlan_config(self): + self.canvas.context = None + dialog = WlanConfigDialog(self.app, self.app, self) + dialog.show() + + def show_mobility_config(self): + self.canvas.context = None + dialog = MobilityConfigDialog(self.app, self.app, self) + dialog.show() diff --git a/coretk/coretk/widgets.py b/coretk/coretk/widgets.py index e825fdb4..bd6904b2 100644 --- a/coretk/coretk/widgets.py +++ b/coretk/coretk/widgets.py @@ -15,6 +15,7 @@ INT_TYPES = { core_pb2.ConfigOptionType.INT32, core_pb2.ConfigOptionType.INT64, } +PAD = 5 class FrameScroll(ttk.LabelFrame): @@ -72,7 +73,7 @@ class ConfigFrame(FrameScroll): for group_name in sorted(group_mapping): group = group_mapping[group_name] - frame = ttk.Frame(self.frame) + frame = ttk.Frame(self.frame, padding=PAD) frame.columnconfigure(1, weight=1) self.frame.add(frame, text=group_name) for index, option in enumerate(sorted(group, key=lambda x: x.name)): diff --git a/coretk/coretk/wlannodeconfig.py b/coretk/coretk/wlannodeconfig.py deleted file mode 100644 index 1ebfb771..00000000 --- a/coretk/coretk/wlannodeconfig.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -wireless node configuration for all the wireless node -""" -from collections import OrderedDict - -from core.api.grpc import core_pb2 - - -class WlanNodeConfig: - def __init__(self): - # maps node id to wlan configuration - self.configurations = {} - - def set_default_config(self, node_type, node_id): - if node_type == core_pb2.NodeType.WIRELESS_LAN: - config = OrderedDict() - config["range"] = "275" - config["bandwidth"] = "54000000" - config["jitter"] = "0" - config["delay"] = "20000" - config["error"] = "0" - self.configurations[node_id] = config - - def set_custom_config(self, node_id, range, bandwidth, jitter, delay, error): - self.configurations[node_id]["range"] = range - self.configurations[node_id]["bandwidth"] = bandwidth - self.configurations[node_id]["jitter"] = jitter - self.configurations[node_id]["delay"] = delay - self.configurations[node_id]["error"] = error - - def delete_node_config(self, node_id): - """ - not implemented - :param node_id: - :return: - """ - return