From 97cb1444f3e60ffb0f410e310e3f04789b3281d5 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 16:41:38 -0800 Subject: [PATCH 1/7] updates to emane model config storage, fixes to reconnecting to a wlan session --- coretk/coretk/coreclient.py | 86 +++++++++++---------------- coretk/coretk/dialogs/emaneconfig.py | 13 ++--- coretk/coretk/emaneodelnodeconfig.py | 78 ------------------------- coretk/coretk/graph.py | 87 +++++++++++++++------------- coretk/coretk/graph_helper.py | 30 +++++----- coretk/coretk/linkinfo.py | 1 - coretk/coretk/nodeutils.py | 7 +++ coretk/coretk/wirelessconnection.py | 10 ++-- daemon/core/nodes/network.py | 2 +- 9 files changed, 111 insertions(+), 203 deletions(-) delete mode 100644 coretk/coretk/emaneodelnodeconfig.py diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index dbcbcf18..229f59a5 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -6,7 +6,6 @@ import os from core.api.grpc import client, core_pb2 from coretk.dialogs.sessions import SessionsDialog -from coretk.emaneodelnodeconfig import EmaneModelNodeConfig from coretk.interface import InterfaceManager from coretk.nodeutils import NodeDraw, NodeUtils from coretk.servicefileconfig import ServiceFileConfig @@ -72,7 +71,6 @@ class CoreClient: self.wlan_configs = {} self.mobility_configs = {} self.emane_model_configs = {} - self.emaneconfig_management = EmaneModelNodeConfig(app) self.emane_config = None self.serviceconfig_manager = ServiceNodeConfig(app) self.servicefileconfig_manager = ServiceFileConfig() @@ -104,7 +102,7 @@ class CoreClient: def handle_events(self, event): logging.info("event: %s", event) if event.HasField("link_event"): - self.app.canvas.wireless_draw.hangle_link_event(event.link_event) + self.app.canvas.wireless_draw.handle_link_event(event.link_event) elif event.HasField("session_event"): if event.session_event.event <= core_pb2.SessionState.SHUTDOWN: self.state = event.session_event.event @@ -153,7 +151,6 @@ class CoreClient: # get hooks response = self.client.get_hooks(self.session_id) - logging.info("joined session hooks: %s", response) for hook in response.hooks: self.hooks[hook.file] = hook @@ -161,19 +158,16 @@ class CoreClient: for node in session.nodes: 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) self.wlan_configs[node.id] = response.config # get mobility configs response = self.client.get_mobility_configs(self.session_id) - logging.debug("mobility configs: %s", response) for node_id in response.configs: node_config = response.configs[node_id].config self.mobility_configs[node_id] = node_config # get emane config response = self.client.get_emane_config(self.session_id) - logging.debug("emane config: %s", response) self.emane_config = response.config # get emane model config @@ -462,10 +456,6 @@ class CoreClient: emane=emane, ) - # set default emane configuration for emane node - if node_type == core_pb2.NodeType.EMANE: - self.emaneconfig_management.set_default_config(node_id) - # set default service configurations # TODO: need to deal with this and custom node cases if node_type == core_pb2.NodeType.DEFAULT: @@ -492,50 +482,28 @@ class CoreClient: :return: nothing """ # delete the nodes - for node_id in node_ids: + for i in node_ids: try: - del self.canvas_nodes[node_id] - self.reusable.append(node_id) + del self.canvas_nodes[i] + self.reusable.append(i) + if i in self.mobility_configs: + del self.mobility_configs[i] + if i in self.wlan_configs: + del self.wlan_configs[i] + for key in list(self.emane_model_configs): + node_id, _, _ = key + if node_id == i: + del self.emane_model_configs[key] except KeyError: - logging.error("invalid canvas id: %s", node_id) + logging.error("invalid canvas id: %s", i) self.reusable.sort() # delete the edges and interfaces - node_interface_pairs = [] for i in edge_tokens: try: - link = self.links.pop(i) - if link.interface_one is not None: - node_interface_pairs.append( - (link.node_one_id, link.interface_one.id) - ) - if link.interface_two is not None: - node_interface_pairs.append( - (link.node_two_id, link.interface_two.id) - ) + self.links.pop(i) except KeyError: - logging.error("coreclient.py invalid edge token ") - - # delete global emane config if there no longer exist any emane cloud - # TODO: should not need to worry about this - node_types = [x.core_node.type for x in self.canvas_nodes.values()] - if core_pb2.NodeType.EMANE not in node_types: - self.emane_config = None - - # delete any mobility configuration, wlan configuration - for i in node_ids: - if i in self.mobility_configs: - del self.mobility_configs[i] - if i in self.wlan_configs: - del self.wlan_configs[i] - - # delete emane configurations - for i in node_interface_pairs: - if i in self.emaneconfig_management.configurations: - self.emaneconfig_management.configurations.pop(i) - for i in node_ids: - if tuple([i, None]) in self.emaneconfig_management.configurations: - self.emaneconfig_management.configurations.pop(tuple([i, None])) + logging.error("invalid edge token: %s", i) def create_interface(self, canvas_node): interface = None @@ -609,13 +577,11 @@ class CoreClient: def get_emane_model_configs_proto(self): configs = [] - emane_configs = self.emaneconfig_management.configurations - for key, value in emane_configs.items(): - node_id, interface_id = key - model, options = value - config = {x: options[x].value for x in options} + for key, config in self.emane_model_configs.items(): + node_id, model, interface = key + config = {x: config[x].value for x in config} config_proto = core_pb2.EmaneModelConfig( - node_id=node_id, interface_id=interface_id, model=model, config=config + node_id=node_id, interface_id=interface, model=model, config=config ) configs.append(config_proto) return configs @@ -669,3 +635,17 @@ class CoreClient: response = self.client.get_mobility_config(self.session_id, node_id) config = response.config return config + + def get_emane_model_config(self, node_id, model, interface=None): + config = self.emane_model_configs.get((node_id, model, interface)) + if not config: + if interface is None: + interface = -1 + response = self.client.get_emane_model_config( + self.session_id, node_id, model, interface + ) + config = response.config + return config + + def set_emane_model_config(self, node_id, model, config, interface=None): + self.emane_model_configs[(node_id, model, interface)] = config diff --git a/coretk/coretk/dialogs/emaneconfig.py b/coretk/coretk/dialogs/emaneconfig.py index 046b0d62..bb6d8f1b 100644 --- a/coretk/coretk/dialogs/emaneconfig.py +++ b/coretk/coretk/dialogs/emaneconfig.py @@ -46,16 +46,15 @@ class GlobalEmaneDialog(Dialog): class EmaneModelDialog(Dialog): - def __init__(self, master, app, node, model): + def __init__(self, master, app, node, model, interface=None): super().__init__(master, app, f"{node.name} {model} Configuration", modal=True) self.node = node self.model = f"emane_{model}" + self.interface = interface self.config_frame = None - session_id = self.app.core.session_id - response = self.app.core.client.get_emane_model_config( - session_id, self.node.id, self.model + self.config = self.app.core.get_emane_model_config( + self.node.id, self.model, self.interface ) - self.config = response.config self.draw() def draw(self): @@ -79,8 +78,8 @@ class EmaneModelDialog(Dialog): def click_apply(self): self.config_frame.parse_config() - self.app.core.emaneconfig_management.set_custom_emane_cloud_config( - self.node.id, self.model + self.app.core.set_emane_model_config( + self.node.id, self.model, self.config, self.interface ) self.destroy() diff --git a/coretk/coretk/emaneodelnodeconfig.py b/coretk/coretk/emaneodelnodeconfig.py deleted file mode 100644 index e1aaadbb..00000000 --- a/coretk/coretk/emaneodelnodeconfig.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -emane model configurations -""" -import logging - - -class EmaneModelNodeConfig: - def __init__(self, app): - """ - create an instance for EmaneModelNodeConfig - - :param app: application - """ - # dict(tuple(node_id, interface_id, model) : config) - self.configurations = {} - - # dict(int, list(int)) stores emane node maps to mdr nodes that are linked to that emane node - self.links = {} - - self.app = app - - def set_default_config(self, node_id): - """ - set a default emane configuration for a newly created emane - - :param int node_id: node id - :return: nothing - """ - session_id = self.app.core.session_id - client = self.app.core.client - default_emane_model = self.app.core.emane_models[0] - response = client.get_emane_model_config( - session_id, node_id, default_emane_model - ) - logging.info( - "emanemodelnodeconfig.py get emane model config (%s), result: %s", - node_id, - response, - ) - self.configurations[tuple([node_id, None])] = tuple( - [default_emane_model, response.config] - ) - self.links[node_id] = [] - - def set_default_for_mdr(self, emane_node_id, mdr_node_id, interface_id): - """ - set emane configuration of an mdr node on the correct interface - - :param int emane_node_id: emane node id - :param int mdr_node_id: mdr node id - :param int interface_id: interface id - :return: nothing - """ - self.configurations[tuple([mdr_node_id, interface_id])] = self.configurations[ - tuple([emane_node_id, None]) - ] - self.links[emane_node_id].append(tuple([mdr_node_id, interface_id])) - - def set_custom_emane_cloud_config(self, emane_node_id, model_name): - """ - set custom configuration for an emane node, if model is changed, update the nodes connected to that emane node - - :param int emane_node_id: emane node id - :param str model_name: model name - :return: nothing - """ - prev_model_name = self.configurations[tuple([emane_node_id, None])][0] - session_id = self.app.core.session_id - response = self.app.core.client.get_emane_model_config( - session_id, emane_node_id, model_name - ) - self.configurations[tuple([emane_node_id, None])] = tuple( - [model_name, response.config] - ) - - if prev_model_name != model_name: - for k in self.links[emane_node_id]: - self.configurations[k] = tuple([model_name, response.config]) diff --git a/coretk/coretk/graph.py b/coretk/coretk/graph.py index e2722c77..43b8a830 100644 --- a/coretk/coretk/graph.py +++ b/coretk/coretk/graph.py @@ -176,48 +176,53 @@ class CanvasGraph(tk.Canvas): 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 - is_wired = link.type == core_pb2.LinkType.WIRED - edge = CanvasEdge( - node_one.position.x, - node_one.position.y, - node_two.position.x, - node_two.position.y, - canvas_node_one.id, - self, - is_wired=is_wired, - ) - edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) - edge.dst = canvas_node_two.id - canvas_node_one.edges.add(edge) - canvas_node_two.edges.add(edge) - self.edges[edge.token] = edge - self.core.links[edge.token] = link - self.helper.redraw_antenna(link, canvas_node_one, canvas_node_two) + if link.type == core_pb2.LinkType.WIRELESS: + self.wireless_draw.add_connection(link.node_one_id, link.node_two_id) + else: + is_node_one_wireless = NodeUtils.is_wireless_node(node_one.type) + is_node_two_wireless = NodeUtils.is_wireless_node(node_two.type) + has_no_wireless = not (is_node_one_wireless or is_node_two_wireless) + edge = CanvasEdge( + node_one.position.x, + node_one.position.y, + node_two.position.x, + node_two.position.y, + canvas_node_one.id, + self, + is_wired=has_no_wireless, + ) + edge.token = tuple(sorted((canvas_node_one.id, canvas_node_two.id))) + edge.dst = canvas_node_two.id + canvas_node_one.edges.add(edge) + canvas_node_two.edges.add(edge) + self.edges[edge.token] = edge + self.core.links[edge.token] = link + self.helper.redraw_antenna(canvas_node_one, canvas_node_two) - # TODO add back the link info to grpc manager also redraw - # TODO will include throughput and ipv6 in the future - interface_one = link.interface_one - interface_two = link.interface_two - ip4_src = None - ip4_dst = None - ip6_src = None - ip6_dst = None - if interface_one is not None: - ip4_src = interface_one.ip4 - ip6_src = interface_one.ip6 - if interface_two is not None: - ip4_dst = interface_two.ip4 - ip6_dst = interface_two.ip6 - edge.link_info = LinkInfo( - canvas=self, - edge=edge, - ip4_src=ip4_src, - ip6_src=ip6_src, - ip4_dst=ip4_dst, - ip6_dst=ip6_dst, - ) - canvas_node_one.interfaces.append(interface_one) - canvas_node_two.interfaces.append(interface_two) + # TODO add back the link info to grpc manager also redraw + # TODO will include throughput and ipv6 in the future + interface_one = link.interface_one + interface_two = link.interface_two + ip4_src = None + ip4_dst = None + ip6_src = None + ip6_dst = None + if interface_one is not None: + ip4_src = interface_one.ip4 + ip6_src = interface_one.ip6 + if interface_two is not None: + ip4_dst = interface_two.ip4 + ip6_dst = interface_two.ip6 + edge.link_info = LinkInfo( + canvas=self, + edge=edge, + ip4_src=ip4_src, + ip6_src=ip6_src, + ip4_dst=ip4_dst, + ip6_dst=ip6_dst, + ) + canvas_node_one.interfaces.append(interface_one) + canvas_node_two.interfaces.append(interface_two) # raise the nodes so they on top of the links self.tag_raise("node") diff --git a/coretk/coretk/graph_helper.py b/coretk/coretk/graph_helper.py index 476c0182..e691667b 100644 --- a/coretk/coretk/graph_helper.py +++ b/coretk/coretk/graph_helper.py @@ -4,8 +4,8 @@ Some graph helper functions import logging import tkinter as tk -from core.api.grpc import core_pb2 from coretk.images import ImageEnum, Images +from coretk.nodeutils import NodeUtils CANVAS_COMPONENT_TAGS = ["edge", "node", "nodename", "wallpaper", "linkinfo"] @@ -31,34 +31,30 @@ class GraphHelper: def draw_wireless_case(self, src_id, dst_id, edge): src_node_type = self.canvas.nodes[src_id].core_node.type dst_node_type = self.canvas.nodes[dst_id].core_node.type - is_src_wlan = src_node_type == core_pb2.NodeType.WIRELESS_LAN - is_dst_wlan = dst_node_type == core_pb2.NodeType.WIRELESS_LAN - if is_src_wlan or is_dst_wlan: + 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: self.canvas.itemconfig(edge.id, state=tk.HIDDEN) edge.wired = False if edge.token not in self.canvas.edges: - if is_src_wlan and is_dst_wlan: + if is_src_wireless and is_dst_wireless: self.canvas.nodes[src_id].antenna_draw.add_antenna() - elif is_src_wlan: + elif is_src_wireless: self.canvas.nodes[dst_id].antenna_draw.add_antenna() else: self.canvas.nodes[src_id].antenna_draw.add_antenna() edge.wired = True - def redraw_antenna(self, link, node_one, node_two): - is_node_one_wlan = node_one.core_node.type == core_pb2.NodeType.WIRELESS_LAN - is_node_two_wlan = node_two.core_node.type == core_pb2.NodeType.WIRELESS_LAN - if link.type == core_pb2.LinkType.WIRELESS: - if is_node_one_wlan and is_node_two_wlan: - node_one.antenna_draw.add_antenna() - elif is_node_one_wlan and not is_node_two_wlan: + def redraw_antenna(self, node_one, node_two): + is_node_one_wireless = NodeUtils.is_wireless_node(node_one.core_node.type) + is_node_two_wireless = NodeUtils.is_wireless_node(node_two.core_node.type) + if is_node_one_wireless or is_node_two_wireless: + if is_node_one_wireless and not is_node_two_wireless: node_two.antenna_draw.add_antenna() - elif not is_node_one_wlan and is_node_two_wlan: + elif not is_node_one_wireless and is_node_two_wireless: node_one.antenna_draw.add_antenna() else: - logging.error( - "graph_helper.py WIRELESS link but both nodes are non-wireless node" - ) + logging.error("bad link between two wireless nodes") def update_wlan_connection(self, old_x, old_y, new_x, new_y, edge_ids): for eid in edge_ids: diff --git a/coretk/coretk/linkinfo.py b/coretk/coretk/linkinfo.py index 1e0363cf..00df0913 100644 --- a/coretk/coretk/linkinfo.py +++ b/coretk/coretk/linkinfo.py @@ -18,7 +18,6 @@ class LinkInfo: """ self.canvas = canvas self.edge = edge - # self.edge_id = edge.id self.radius = 37 self.core = self.canvas.core diff --git a/coretk/coretk/nodeutils.py b/coretk/coretk/nodeutils.py index 380c9a0b..fcc29953 100644 --- a/coretk/coretk/nodeutils.py +++ b/coretk/coretk/nodeutils.py @@ -46,6 +46,7 @@ class NodeUtils: NODE_ICONS = {} CONTAINER_NODES = {NodeType.DEFAULT, NodeType.DOCKER, NodeType.LXC} IMAGE_NODES = {NodeType.DOCKER, NodeType.LXC} + WIRELESS_NODES = {NodeType.WIRELESS_LAN, NodeType.EMANE} NODE_MODELS = {"router", "host", "PC", "mdr", "prouter"} @classmethod @@ -60,8 +61,14 @@ class NodeUtils: def is_image_node(cls, node_type): return node_type in cls.IMAGE_NODES + @classmethod + def is_wireless_node(cls, node_type): + return node_type in cls.WIRELESS_NODES + @classmethod def node_icon(cls, node_type, model): + if model == "": + model = None return cls.NODE_ICONS[(node_type, model)] @classmethod diff --git a/coretk/coretk/wirelessconnection.py b/coretk/coretk/wirelessconnection.py index e8d03126..2e0e0af9 100644 --- a/coretk/coretk/wirelessconnection.py +++ b/coretk/coretk/wirelessconnection.py @@ -11,7 +11,7 @@ class WirelessConnection: # map a (node_one_id, node_two_id) to a wlan canvas id self.map = {} - def add_wlan_connection(self, node_one_id, node_two_id): + def add_connection(self, node_one_id, node_two_id): canvas_node_one = self.core.canvas_nodes[node_one_id] canvas_node_two = self.core.canvas_nodes[node_two_id] key = tuple(sorted((node_one_id, node_two_id))) @@ -25,7 +25,7 @@ class WirelessConnection: canvas_node_one.wlans.append(wlan_canvas_id) canvas_node_two.wlans.append(wlan_canvas_id) - def delete_wlan_connection(self, node_one_id, node_two_id): + def delete_connection(self, node_one_id, node_two_id): canvas_node_one = self.core.canvas_nodes[node_one_id] canvas_node_two = self.core.canvas_nodes[node_two_id] key = tuple(sorted((node_one_id, node_two_id))) @@ -35,13 +35,13 @@ class WirelessConnection: self.canvas.delete(wlan_canvas_id) self.map.pop(key, None) - def hangle_link_event(self, link_event): + def handle_link_event(self, link_event): if link_event.message_type == core_pb2.MessageType.ADD: - self.add_wlan_connection( + self.add_connection( link_event.link.node_one_id, link_event.link.node_two_id ) if link_event.message_type == core_pb2.MessageType.DELETE: - self.delete_wlan_connection( + self.delete_connection( link_event.link.node_one_id, link_event.link.node_two_id ) diff --git a/daemon/core/nodes/network.py b/daemon/core/nodes/network.py index 80a730e2..6fe291dd 100644 --- a/daemon/core/nodes/network.py +++ b/daemon/core/nodes/network.py @@ -1017,7 +1017,7 @@ class WlanNode(CoreNetwork): """ apitype = NodeTypes.WIRELESS_LAN.value - linktype = LinkTypes.WIRELESS.value + linktype = LinkTypes.WIRED.value policy = "DROP" type = "wlan" From df9c7308dbb6457bd434cb21da86048026cfac71 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 17:03:18 -0800 Subject: [PATCH 2/7] update to avoid issue when old gui creates emane nodes without emane models --- daemon/core/emulator/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daemon/core/emulator/session.py b/daemon/core/emulator/session.py index d2d841e4..fee2bde4 100644 --- a/daemon/core/emulator/session.py +++ b/daemon/core/emulator/session.py @@ -704,7 +704,7 @@ class Session: self.services.add_services(node, node.type, options.services) # ensure default emane configuration - if isinstance(node, EmaneNet): + if isinstance(node, EmaneNet) and options.emane: self.emane.set_model_config(_id, options.emane) # set default wlan config if needed if isinstance(node, WlanNode): From bb7bad89d32086dae383626cda4891cae2e932b6 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 17:14:23 -0800 Subject: [PATCH 3/7] added a default location for now to get emane working, until session location is supported --- coretk/coretk/coreclient.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 229f59a5..49489798 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -60,6 +60,9 @@ class CoreClient: # data for managing the current session self.canvas_nodes = {} + self.location = core_pb2.SessionLocation( + x=0, y=0, z=0, lat=47.5791667, lon=-122.132322, alt=2.0, scale=150.0 + ) self.interface_to_edge = {} self.state = None self.links = {} @@ -308,12 +311,13 @@ class CoreClient: self.session_id, nodes, links, - hooks=hooks, - wlan_configs=wlan_configs, - emane_config=emane_config, - emane_model_configs=emane_model_configs, - mobility_configs=mobility_configs, - service_configs=service_configs, + self.location, + hooks, + emane_config, + emane_model_configs, + wlan_configs, + mobility_configs, + service_configs, ) logging.debug("Start session %s, result: %s", self.session_id, response.result) From fbbf31f4fa09fbee28895919171ffc34554f87d6 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 21:56:28 -0800 Subject: [PATCH 4/7] added saving session location to config, and query location when joining a session --- coretk/coretk/appconfig.py | 9 ++ coretk/coretk/coreclient.py | 23 ++++-- coretk/coretk/dialogs/canvassizeandscale.py | 92 ++++++++++++--------- coretk/coretk/graph.py | 1 - 4 files changed, 79 insertions(+), 46 deletions(-) diff --git a/coretk/coretk/appconfig.py b/coretk/coretk/appconfig.py index e8f5db6e..2af1047c 100644 --- a/coretk/coretk/appconfig.py +++ b/coretk/coretk/appconfig.py @@ -75,6 +75,15 @@ def check_directory(): "terminal": terminal, "gui3d": "/usr/local/bin/std3d.sh", }, + "location": { + "x": 0.0, + "y": 0.0, + "z": 0.0, + "lat": 47.5791667, + "lon": -122.132322, + "alt": 2.0, + "scale": 150.0, + }, "servers": [{"name": "example", "address": "127.0.0.1", "port": 50051}], "nodes": [], "observers": [{"name": "hello", "cmd": "echo hello"}], diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 49489798..d801875e 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -60,9 +60,7 @@ class CoreClient: # data for managing the current session self.canvas_nodes = {} - self.location = core_pb2.SessionLocation( - x=0, y=0, z=0, lat=47.5791667, lon=-122.132322, alt=2.0, scale=150.0 - ) + self.location = None self.interface_to_edge = {} self.state = None self.links = {} @@ -123,7 +121,7 @@ class CoreClient: throughputs_belong_to_session ) - def join_session(self, session_id): + def join_session(self, session_id, query_location=True): self.master.config(cursor="watch") self.master.update() @@ -148,6 +146,11 @@ class CoreClient: self.state = session.state self.client.events(self.session_id, self.handle_events) + # get location + if query_location: + response = self.client.get_session_location(self.session_id) + self.location = response.location + # get emane models response = self.client.get_emane_models(self.session_id) self.emane_models = response.models @@ -207,7 +210,17 @@ class CoreClient: """ response = self.client.create_session() logging.info("created session: %s", response) - self.join_session(response.session_id) + location_config = self.app.config["location"] + self.location = core_pb2.SessionLocation( + x=location_config["x"], + y=location_config["y"], + z=location_config["z"], + lat=location_config["lat"], + lon=location_config["lon"], + alt=location_config["alt"], + scale=location_config["scale"], + ) + self.join_session(response.session_id, query_location=False) def delete_session(self, custom_sid=None): if custom_sid is None: diff --git a/coretk/coretk/dialogs/canvassizeandscale.py b/coretk/coretk/dialogs/canvassizeandscale.py index 3a72389d..0d88b11f 100644 --- a/coretk/coretk/dialogs/canvassizeandscale.py +++ b/coretk/coretk/dialogs/canvassizeandscale.py @@ -6,8 +6,8 @@ from tkinter import font, ttk from coretk.dialogs.dialog import Dialog -FRAME_PAD = 5 -PADX = 5 +PAD = 5 +PIXEL_SCALE = 100 class SizeAndScaleDialog(Dialog): @@ -19,9 +19,7 @@ class SizeAndScaleDialog(Dialog): """ super().__init__(master, app, "Canvas Size and Scale", modal=True) self.canvas = self.app.canvas - self.meter_per_pixel = self.canvas.meters_per_pixel self.section_font = font.Font(weight="bold") - # get current canvas dimensions plot = self.canvas.find_withtag("rectangle") x0, y0, x1, y1 = self.canvas.bbox(plot[0]) @@ -29,14 +27,15 @@ class SizeAndScaleDialog(Dialog): height = abs(y0 - y1) - 2 self.pixel_width = tk.IntVar(value=width) self.pixel_height = tk.IntVar(value=height) - self.meters_width = tk.IntVar(value=width * self.meter_per_pixel) - self.meters_height = tk.IntVar(value=height * self.meter_per_pixel) - self.scale = tk.IntVar(value=self.meter_per_pixel * 100) - self.x = tk.IntVar(value=0) - self.y = tk.IntVar(value=0) - self.lat = tk.DoubleVar(value=47.5791667) - self.lon = tk.DoubleVar(value=-122.132322) - self.alt = tk.DoubleVar(value=2.0) + location = self.app.core.location + self.x = tk.DoubleVar(value=location.x) + self.y = tk.DoubleVar(value=location.y) + self.lat = tk.DoubleVar(value=location.lat) + self.lon = tk.DoubleVar(value=location.lon) + self.alt = tk.DoubleVar(value=location.alt) + self.scale = tk.DoubleVar(value=location.scale) + self.meters_width = tk.IntVar(value=width / PIXEL_SCALE * location.scale) + self.meters_height = tk.IntVar(value=height / PIXEL_SCALE * location.scale) self.save_default = tk.BooleanVar(value=False) self.draw() @@ -49,7 +48,7 @@ class SizeAndScaleDialog(Dialog): self.draw_buttons() def draw_size(self): - label_frame = ttk.Labelframe(self.top, text="Size", padding=FRAME_PAD) + label_frame = ttk.Labelframe(self.top, text="Size", padding=PAD) label_frame.grid(sticky="ew") label_frame.columnconfigure(0, weight=1) @@ -59,13 +58,13 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(1, weight=1) frame.columnconfigure(3, weight=1) label = ttk.Label(frame, text="Width") - label.grid(row=0, column=0, sticky="w", padx=PADX) + label.grid(row=0, column=0, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.pixel_width) - entry.grid(row=0, column=1, sticky="ew", padx=PADX) + entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="x Height") - label.grid(row=0, column=2, sticky="w", padx=PADX) + label.grid(row=0, column=2, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.pixel_height) - entry.grid(row=0, column=3, sticky="ew", padx=PADX) + entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Pixels") label.grid(row=0, column=4, sticky="w") @@ -75,35 +74,33 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(1, weight=1) frame.columnconfigure(3, weight=1) label = ttk.Label(frame, text="Width") - label.grid(row=0, column=0, sticky="w", padx=PADX) + label.grid(row=0, column=0, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.meters_width) - entry.grid(row=0, column=1, sticky="ew", padx=PADX) + entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="x Height") - label.grid(row=0, column=2, sticky="w", padx=PADX) + label.grid(row=0, column=2, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.meters_height) - entry.grid(row=0, column=3, sticky="ew", padx=PADX) + entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Meters") label.grid(row=0, column=4, sticky="w") def draw_scale(self): - label_frame = ttk.Labelframe(self.top, text="Scale", padding=FRAME_PAD) + label_frame = ttk.Labelframe(self.top, text="Scale", padding=PAD) label_frame.grid(sticky="ew") label_frame.columnconfigure(0, weight=1) frame = ttk.Frame(label_frame) frame.grid(sticky="ew") frame.columnconfigure(1, weight=1) - label = ttk.Label(frame, text="100 Pixels =") - label.grid(row=0, column=0, sticky="w", padx=PADX) + label = ttk.Label(frame, text=f"{PIXEL_SCALE} Pixels =") + label.grid(row=0, column=0, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.scale) - entry.grid(row=0, column=1, sticky="ew", padx=PADX) + entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Meters") label.grid(row=0, column=2, sticky="w") def draw_reference_point(self): - label_frame = ttk.Labelframe( - self.top, text="Reference Point", padding=FRAME_PAD - ) + label_frame = ttk.Labelframe(self.top, text="Reference Point", padding=PAD) label_frame.grid(sticky="ew") label_frame.columnconfigure(0, weight=1) @@ -118,14 +115,14 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(3, weight=1) label = ttk.Label(frame, text="X") - label.grid(row=0, column=0, sticky="w", padx=PADX) + label.grid(row=0, column=0, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.x) - entry.grid(row=0, column=1, sticky="ew", padx=PADX) + entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Y") - label.grid(row=0, column=2, sticky="w", padx=PADX) + label.grid(row=0, column=2, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.y) - entry.grid(row=0, column=3, sticky="ew", padx=PADX) + entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(label_frame, text="Translates To") label.grid() @@ -137,17 +134,17 @@ class SizeAndScaleDialog(Dialog): frame.columnconfigure(5, weight=1) label = ttk.Label(frame, text="Lat") - label.grid(row=0, column=0, sticky="w", padx=PADX) + label.grid(row=0, column=0, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.lat) - entry.grid(row=0, column=1, sticky="ew", padx=PADX) + entry.grid(row=0, column=1, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Lon") - label.grid(row=0, column=2, sticky="w", padx=PADX) + label.grid(row=0, column=2, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.lon) - entry.grid(row=0, column=3, sticky="ew", padx=PADX) + entry.grid(row=0, column=3, sticky="ew", padx=PAD) label = ttk.Label(frame, text="Alt") - label.grid(row=0, column=4, sticky="w", padx=PADX) + label.grid(row=0, column=4, sticky="w", padx=PAD) entry = ttk.Entry(frame, textvariable=self.alt) entry.grid(row=0, column=5, sticky="ew") @@ -164,16 +161,31 @@ class SizeAndScaleDialog(Dialog): frame.grid(sticky="ew") button = ttk.Button(frame, text="Apply", command=self.click_apply) - button.grid(row=0, column=0, sticky="ew", padx=PADX) + button.grid(row=0, column=0, sticky="ew", padx=PAD) button = ttk.Button(frame, text="Cancel", command=self.destroy) button.grid(row=0, column=1, sticky="ew") def click_apply(self): - meter_per_pixel = float(self.scale.get()) / 100 width, height = self.pixel_width.get(), self.pixel_height.get() - self.canvas.meters_per_pixel = meter_per_pixel self.canvas.redraw_grid(width, height) if self.canvas.wallpaper: self.canvas.redraw() + location = self.app.core.location + location.x = self.x.get() + location.y = self.y.get() + location.lat = self.lat.get() + location.lon = self.lon.get() + location.alt = self.alt.get() + location.scale = self.scale.get() + if self.save_default.get(): + location_config = self.app.config["location"] + location_config["x"] = location.x + location_config["y"] = location.y + location_config["z"] = location.z + location_config["lat"] = location.lat + location_config["lon"] = location.lon + location_config["alt"] = location.alt + location_config["scale"] = location.scale + self.app.save_config() self.destroy() diff --git a/coretk/coretk/graph.py b/coretk/coretk/graph.py index 43b8a830..2c874f34 100644 --- a/coretk/coretk/graph.py +++ b/coretk/coretk/graph.py @@ -49,7 +49,6 @@ class CanvasGraph(tk.Canvas): self.edges = {} self.drawing_edge = None self.grid = None - self.meters_per_pixel = 1.5 self.canvas_management = CanvasComponentManagement(self, core) self.setup_bindings() self.draw_grid() From a6cdd63570ec65d3d9e3c618c12c7c00c68484f2 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 22:03:07 -0800 Subject: [PATCH 5/7] fixed name issue with app config, renamed to guiconfig --- coretk/coretk/app.py | 6 +++--- coretk/coretk/coreclient.py | 8 ++++---- coretk/coretk/dialogs/canvassizeandscale.py | 2 +- coretk/coretk/dialogs/customnodes.py | 6 +++--- coretk/coretk/dialogs/observers.py | 2 +- coretk/coretk/dialogs/preferences.py | 4 ++-- coretk/coretk/dialogs/servers.py | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/coretk/coretk/app.py b/coretk/coretk/app.py index fbce02e1..68060927 100644 --- a/coretk/coretk/app.py +++ b/coretk/coretk/app.py @@ -26,7 +26,7 @@ class Application(tk.Frame): self.statusbar = None # setup - self.config = appconfig.read() + self.guiconfig = appconfig.read() self.style = ttk.Style() self.setup_theme() self.core = CoreClient(self) @@ -36,7 +36,7 @@ class Application(tk.Frame): def setup_theme(self): themes.load(self.style) - self.style.theme_use(self.config["preferences"]["theme"]) + self.style.theme_use(self.guiconfig["preferences"]["theme"]) func = partial(themes.update_menu, self.style) self.master.bind_class("Menu", "<>", func) @@ -88,7 +88,7 @@ class Application(tk.Frame): menu_action.on_quit() def save_config(self): - appconfig.save(self.config) + appconfig.save(self.guiconfig) if __name__ == "__main__": diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index d801875e..b64aab98 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -83,12 +83,12 @@ class CoreClient: def read_config(self): # read distributed server - for config in self.app.config.get("servers", []): + for config in self.app.guiconfig.get("servers", []): server = CoreServer(config["name"], config["address"], config["port"]) self.servers[server.name] = server # read custom nodes - for config in self.app.config.get("nodes", []): + for config in self.app.guiconfig.get("nodes", []): name = config["name"] image_file = config["image"] services = set(config["services"]) @@ -96,7 +96,7 @@ class CoreClient: self.custom_nodes[name] = node_draw # read observers - for config in self.app.config.get("observers", []): + for config in self.app.guiconfig.get("observers", []): observer = Observer(config["name"], config["cmd"]) self.custom_observers[observer.name] = observer @@ -210,7 +210,7 @@ class CoreClient: """ response = self.client.create_session() logging.info("created session: %s", response) - location_config = self.app.config["location"] + location_config = self.app.guiconfig["location"] self.location = core_pb2.SessionLocation( x=location_config["x"], y=location_config["y"], diff --git a/coretk/coretk/dialogs/canvassizeandscale.py b/coretk/coretk/dialogs/canvassizeandscale.py index 0d88b11f..f25dfdd6 100644 --- a/coretk/coretk/dialogs/canvassizeandscale.py +++ b/coretk/coretk/dialogs/canvassizeandscale.py @@ -179,7 +179,7 @@ class SizeAndScaleDialog(Dialog): location.alt = self.alt.get() location.scale = self.scale.get() if self.save_default.get(): - location_config = self.app.config["location"] + location_config = self.app.guiconfig["location"] location_config["x"] = location.x location_config["y"] = location.y location_config["z"] = location.z diff --git a/coretk/coretk/dialogs/customnodes.py b/coretk/coretk/dialogs/customnodes.py index da7a1fb0..55199541 100644 --- a/coretk/coretk/dialogs/customnodes.py +++ b/coretk/coretk/dialogs/customnodes.py @@ -180,17 +180,17 @@ class CustomNodesDialog(Dialog): self.services.update(dialog.current_services) def click_save(self): - self.app.config["nodes"].clear() + self.app.guiconfig["nodes"].clear() for name in sorted(self.app.core.custom_nodes): node_draw = self.app.core.custom_nodes[name] - self.app.config["nodes"].append( + self.app.guiconfig["nodes"].append( { "name": name, "image": node_draw.image_file, "services": list(node_draw.services), } ) - logging.info("saving custom nodes: %s", self.app.config["nodes"]) + logging.info("saving custom nodes: %s", self.app.guiconfig["nodes"]) self.app.save_config() self.destroy() diff --git a/coretk/coretk/dialogs/observers.py b/coretk/coretk/dialogs/observers.py index 78c5811f..58499bd7 100644 --- a/coretk/coretk/dialogs/observers.py +++ b/coretk/coretk/dialogs/observers.py @@ -95,7 +95,7 @@ class ObserverDialog(Dialog): for name in sorted(self.app.core.custom_observers): observer = self.app.core.custom_observers[name] observers.append({"name": observer.name, "cmd": observer.cmd}) - self.app.config["observers"] = observers + self.app.guiconfig["observers"] = observers self.app.save_config() self.destroy() diff --git a/coretk/coretk/dialogs/preferences.py b/coretk/coretk/dialogs/preferences.py index 148cb6bb..7298f727 100644 --- a/coretk/coretk/dialogs/preferences.py +++ b/coretk/coretk/dialogs/preferences.py @@ -9,7 +9,7 @@ from coretk.dialogs.dialog import Dialog class PreferencesDialog(Dialog): def __init__(self, master, app): super().__init__(master, app, "Preferences", modal=True) - preferences = self.app.config["preferences"] + preferences = self.app.guiconfig["preferences"] self.editor = tk.StringVar(value=preferences["editor"]) self.theme = tk.StringVar(value=preferences["theme"]) self.terminal = tk.StringVar(value=preferences["terminal"]) @@ -76,7 +76,7 @@ class PreferencesDialog(Dialog): self.app.style.theme_use(theme) def click_save(self): - preferences = self.app.config["preferences"] + preferences = self.app.guiconfig["preferences"] preferences["terminal"] = self.terminal.get() preferences["editor"] = self.editor.get() preferences["gui3d"] = self.gui3d.get() diff --git a/coretk/coretk/dialogs/servers.py b/coretk/coretk/dialogs/servers.py index d3db22e7..10a6e79e 100644 --- a/coretk/coretk/dialogs/servers.py +++ b/coretk/coretk/dialogs/servers.py @@ -115,7 +115,7 @@ class ServersDialog(Dialog): servers.append( {"name": server.name, "address": server.address, "port": server.port} ) - self.app.config["servers"] = servers + self.app.guiconfig["servers"] = servers self.app.save_config() self.destroy() From 72e9ae75eb1f0a4579f6324bf72da5347faa2fb3 Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 22:39:39 -0800 Subject: [PATCH 6/7] fixed issue with changing themes and abg colors not being present --- coretk/coretk/menubar.py | 6 ++---- coretk/coretk/themes.py | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coretk/coretk/menubar.py b/coretk/coretk/menubar.py index 621510e2..d9508681 100644 --- a/coretk/coretk/menubar.py +++ b/coretk/coretk/menubar.py @@ -444,9 +444,7 @@ class Menubar(tk.Menu): menu.add_command(label="Comments...", state=tk.DISABLED) menu.add_command(label="Hooks...", command=self.menuaction.session_hooks) menu.add_command(label="Reset node positions", state=tk.DISABLED) - menu.add_command( - label="Emulation servers...", command=self.menuaction.session_servers - ) + menu.add_command(label="Servers...", command=self.menuaction.session_servers) menu.add_command(label="Options...", command=self.menuaction.session_options) self.add_cascade(label="Session", menu=menu) @@ -458,7 +456,7 @@ class Menubar(tk.Menu): """ menu = tk.Menu(self) menu.add_command( - label="Core Github (www)", command=self.menuaction.help_core_github + label="Core GitHub (www)", command=self.menuaction.help_core_github ) menu.add_command( label="Core Documentation (www)", diff --git a/coretk/coretk/themes.py b/coretk/coretk/themes.py index 267c3a3c..ad91276a 100644 --- a/coretk/coretk/themes.py +++ b/coretk/coretk/themes.py @@ -137,6 +137,8 @@ def update_menu(style, event): bg = style.lookup(".", "background") fg = style.lookup(".", "foreground") abg = style.lookup(".", "lightcolor") + if not abg: + abg = bg event.widget.config( background=bg, foreground=fg, activebackground=abg, activeforeground=fg ) From 8ff63219a3f5f13b93865b2180be93a7f7e074bc Mon Sep 17 00:00:00 2001 From: bharnden <32446120+bharnden@users.noreply.github.com> Date: Thu, 21 Nov 2019 22:55:37 -0800 Subject: [PATCH 7/7] increased node icon size, added improve way to offset text regardless of icon size --- coretk/coretk/graph.py | 6 +++++- coretk/coretk/images.py | 2 -- coretk/coretk/nodeutils.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/coretk/coretk/graph.py b/coretk/coretk/graph.py index 2c874f34..545cbb41 100644 --- a/coretk/coretk/graph.py +++ b/coretk/coretk/graph.py @@ -18,6 +18,8 @@ from coretk.nodedelete import CanvasComponentManagement from coretk.nodeutils import NodeUtils from coretk.wirelessconnection import WirelessConnection +NODE_TEXT_OFFSET = 5 + class GraphMode(enum.Enum): SELECT = 0 @@ -615,8 +617,10 @@ class CanvasNode: self.id = self.canvas.create_image( x, y, anchor=tk.CENTER, image=self.image, tags="node" ) + image_box = self.canvas.bbox(self.id) + y = image_box[3] + NODE_TEXT_OFFSET self.text_id = self.canvas.create_text( - x, y + 20, text=self.core_node.name, tags="nodename" + x, y, text=self.core_node.name, tags="nodename" ) self.antenna_draw = WlanAntennaManager(self.canvas, self.id) self.tooltip = CanvasTooltip(self.canvas) diff --git a/coretk/coretk/images.py b/coretk/coretk/images.py index 3763f2d2..287a7359 100644 --- a/coretk/coretk/images.py +++ b/coretk/coretk/images.py @@ -4,8 +4,6 @@ from PIL import Image, ImageTk from coretk.appconfig import LOCAL_ICONS_PATH -NODE_WIDTH = 32 - class Images: images = {} diff --git a/coretk/coretk/nodeutils.py b/coretk/coretk/nodeutils.py index fcc29953..18b468b5 100644 --- a/coretk/coretk/nodeutils.py +++ b/coretk/coretk/nodeutils.py @@ -1,7 +1,7 @@ from core.api.grpc.core_pb2 import NodeType from coretk.images import ImageEnum, Images -ICON_SIZE = 32 +ICON_SIZE = 48 class NodeDraw: