diff --git a/coretk/coretk/coreclient.py b/coretk/coretk/coreclient.py index 827f4d01..36562cce 100644 --- a/coretk/coretk/coreclient.py +++ b/coretk/coretk/coreclient.py @@ -421,8 +421,8 @@ class CoreClient: 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_config_proto() - file_configs = self.get_service_file_config_proto() + service_configs = self.get_service_configs_proto() + file_configs = self.get_service_file_configs_proto() if self.emane_config: emane_config = {x: self.emane_config[x].value for x in self.emane_config} else: @@ -512,6 +512,11 @@ class CoreClient: :return: nothing """ try: + if self.state != core_pb2.SessionState.RUNTIME: + logging.debug( + "session state not runtime, send session data to the daemon..." + ) + self.send_data() response = self.client.save_xml(self.session_id, file_path) logging.info("saved xml(%s): %s", file_path, response) except grpc.RpcError as e: @@ -586,6 +591,52 @@ class CoreClient: ) logging.debug("create link: %s", response) + def send_data(self): + """ + send to daemon all session info, but don't start the session + + :return: nothing + """ + self.create_nodes_and_links() + for config_proto in self.get_wlan_configs_proto(): + self.client.set_wlan_config( + self.session_id, config_proto.node_id, config_proto.config + ) + for config_proto in self.get_mobility_configs_proto(): + self.client.set_mobility_config( + self.session_id, config_proto.node_id, config_proto.config + ) + for config_proto in self.get_service_configs_proto(): + self.client.set_node_service( + self.session_id, + config_proto.node_id, + config_proto.service, + config_proto.startup, + config_proto.validate, + config_proto.shutdown, + ) + for config_proto in self.get_service_file_configs_proto(): + self.client.set_node_service_file( + self.session_id, + config_proto.node_id, + config_proto.service, + config_proto.file, + config_proto.data, + ) + for hook in self.hooks.values(): + self.client.add_hook(self.session_id, hook.state, hook.file, hook.data) + for config_proto in self.get_emane_model_configs_proto(): + self.client.set_emane_model_config( + self.session_id, + config_proto.node_id, + config_proto.model, + config_proto.config, + config_proto.interface_id, + ) + if self.emane_config: + config = {x: self.emane_config[x].value for x in self.emane_config} + self.client.set_emane_config(self.session_id, config) + def close(self): """ Clean ups when done using grpc @@ -762,7 +813,7 @@ class CoreClient: configs.append(config_proto) return configs - def get_service_config_proto(self): + def get_service_configs_proto(self): configs = [] for node_id, services in self.service_configs.items(): for name, config in services.items(): @@ -776,7 +827,7 @@ class CoreClient: configs.append(config_proto) return configs - def get_service_file_config_proto(self): + def get_service_file_configs_proto(self): configs = [] for (node_id, file_configs) in self.file_configs.items(): for service, file_config in file_configs.items(): diff --git a/coretk/coretk/graph/graph.py b/coretk/coretk/graph/graph.py index fc5d6fd7..e79e0f9c 100644 --- a/coretk/coretk/graph/graph.py +++ b/coretk/coretk/graph/graph.py @@ -11,7 +11,7 @@ from coretk.graph.enums import GraphMode, ScaleOption from coretk.graph.linkinfo import LinkInfo, Throughput from coretk.graph.node import CanvasNode from coretk.graph.shape import Shape -from coretk.graph.shapeutils import is_draw_shape +from coretk.graph.shapeutils import ShapeType, is_draw_shape from coretk.nodeutils import NodeUtils SCROLL_BUFFER = 25 @@ -32,6 +32,7 @@ class CanvasGraph(tk.Canvas): self.mode = GraphMode.SELECT self.annotation_type = None self.selection = {} + self.select_box = None self.selected = None self.node_draw = None self.context = None @@ -137,6 +138,13 @@ class CanvasGraph(tk.Canvas): self.tag_lower(self.grid) def add_wireless_edge(self, src, dst): + """ + add a wireless edge between 2 canvas nodes + + :param CanvasNode src: source node + :param CanvasNode dst: destination node + :return: nothing + """ token = tuple(sorted((src.id, dst.id))) x1, y1 = self.coords(src.id) x2, y2 = self.coords(dst.id) @@ -249,6 +257,7 @@ class CanvasGraph(tk.Canvas): :param event: mouse event :return: nothing """ + logging.debug("click release") if self.context: self.context.unpost() self.context = None @@ -260,6 +269,19 @@ class CanvasGraph(tk.Canvas): shape = self.shapes[self.selected] shape.shape_complete(x, y) self.shape_drawing = False + elif self.mode == GraphMode.SELECT: + self.focus_set() + if self.select_box: + x0, y0, x1, y1 = self.coords(self.select_box.id) + inside = [ + x + for x in self.find_enclosed(x0, y0, x1, y1) + if "node" in self.gettags(x) or "shape" in self.gettags(x) + ] + for i in inside: + self.select_object(i, True) + self.select_box.disappear() + self.select_box = None else: self.focus_set() self.selected = self.get_selected(event) @@ -445,6 +467,10 @@ class CanvasGraph(tk.Canvas): self.select_object(node.id) self.selected = selected else: + logging.debug("create selection box") + if self.mode == GraphMode.SELECT: + shape = Shape(self.app, self, ShapeType.RECTANGLE, x, y) + self.select_box = shape self.clear_selection() def ctrl_click(self, event): @@ -486,14 +512,18 @@ class CanvasGraph(tk.Canvas): return # move selected objects - for selected_id in self.selection: - if selected_id in self.shapes: - shape = self.shapes[selected_id] - shape.motion(x_offset, y_offset) + if len(self.selection) > 0: + for selected_id in self.selection: + if selected_id in self.shapes: + shape = self.shapes[selected_id] + shape.motion(x_offset, y_offset) - if selected_id in self.nodes: - node = self.nodes[selected_id] - node.motion(x_offset, y_offset, update=self.core.is_runtime()) + if selected_id in self.nodes: + node = self.nodes[selected_id] + node.motion(x_offset, y_offset, update=self.core.is_runtime()) + else: + if self.select_box and self.mode == GraphMode.SELECT: + self.select_box.shape_motion(x, y) def click_context(self, event): logging.info("context event: %s", self.context) @@ -681,3 +711,23 @@ class CanvasGraph(tk.Canvas): def is_selection_mode(self): return self.mode == GraphMode.SELECT + + def create_edge(self, source, dest): + """ + create an edge between source node and destination node + + :param CanvasNode source: source node + :param CanvasNode dest: destination node + :return: nothing + """ + if tuple([source.id, dest.id]) not in self.edges: + pos0 = source.core_node.position + x0 = pos0.x + y0 = pos0.y + edge = CanvasEdge(x0, y0, x0, y0, source.id, self) + edge.complete(dest.id) + 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) diff --git a/coretk/coretk/graph/node.py b/coretk/coretk/graph/node.py index 5757f081..bdad50e6 100644 --- a/coretk/coretk/graph/node.py +++ b/coretk/coretk/graph/node.py @@ -3,6 +3,7 @@ from tkinter import font import grpc +from core.api.grpc import core_pb2 from core.api.grpc.core_pb2 import NodeType from coretk.dialogs.emaneconfig import EmaneConfigDialog from coretk.dialogs.mobilityconfig import MobilityConfigDialog @@ -191,7 +192,9 @@ class CanvasNode: label="Mobility Config", command=self.show_mobility_config ) if NodeUtils.is_wireless_node(self.core_node.type): - context.add_command(label="Link To All MDRs", state=tk.DISABLED) + context.add_command( + label="Link To Selected", command=self.wireless_link_selected + ) context.add_command(label="Select Members", state=tk.DISABLED) context.add_command(label="Select Adjacent", state=tk.DISABLED) context.add_command(label="Create Link To", state=tk.DISABLED) @@ -233,3 +236,13 @@ class CanvasNode: self.canvas.context = None dialog = NodeService(self.app.master, self.app, self) dialog.show() + + def wireless_link_selected(self): + self.canvas.context = None + for canvas_nid in [ + x for x in self.canvas.selection if "node" in self.canvas.gettags(x) + ]: + core_node = self.canvas.nodes[canvas_nid].core_node + if core_node.type == core_pb2.NodeType.DEFAULT and core_node.model == "mdr": + self.canvas.create_edge(self, self.canvas.nodes[canvas_nid]) + self.canvas.clear_selection() diff --git a/coretk/coretk/graph/shape.py b/coretk/coretk/graph/shape.py index 56e679d2..80621107 100644 --- a/coretk/coretk/graph/shape.py +++ b/coretk/coretk/graph/shape.py @@ -132,6 +132,9 @@ class Shape: s = ShapeDialog(self.app, self.app, self) s.show() + def disappear(self): + self.canvas.delete(self.id) + def motion(self, x_offset, y_offset): self.canvas.move(self.id, x_offset, y_offset) self.canvas.move_selection(self.id, x_offset, y_offset)