commit
daea312162
6 changed files with 63 additions and 26 deletions
|
@ -38,6 +38,17 @@ OBSERVERS = {
|
||||||
"IPSec policies": "setkey -DP",
|
"IPSec policies": "setkey -DP",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFAULT_TERMS = {
|
||||||
|
"xterm": "xterm -e",
|
||||||
|
"aterm": "aterm -e",
|
||||||
|
"eterm": "eterm -e",
|
||||||
|
"rxvt": "rxvt -e",
|
||||||
|
"konsole": "konsole -e",
|
||||||
|
"lxterminal": "lxterminal -e",
|
||||||
|
"xfce4-terminal": "xfce4-terminal -x",
|
||||||
|
"gnome-terminal": "gnome-terminal --window--",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CoreServer:
|
class CoreServer:
|
||||||
def __init__(self, name: str, address: str, port: int):
|
def __init__(self, name: str, address: str, port: int):
|
||||||
|
@ -110,6 +121,8 @@ class CoreClient:
|
||||||
self.emane_config = None
|
self.emane_config = None
|
||||||
self.service_configs.clear()
|
self.service_configs.clear()
|
||||||
self.file_configs.clear()
|
self.file_configs.clear()
|
||||||
|
for mobility_player in self.mobility_players.values():
|
||||||
|
mobility_player.handle_close()
|
||||||
self.mobility_players.clear()
|
self.mobility_players.clear()
|
||||||
# clear streams
|
# clear streams
|
||||||
if self.handling_throughputs:
|
if self.handling_throughputs:
|
||||||
|
@ -300,6 +313,7 @@ class CoreClient:
|
||||||
# get metadata
|
# get metadata
|
||||||
response = self.client.get_session_metadata(self.session_id)
|
response = self.client.get_session_metadata(self.session_id)
|
||||||
self.parse_metadata(response.config)
|
self.parse_metadata(response.config)
|
||||||
|
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.after(0, show_grpc_error, e)
|
self.app.after(0, show_grpc_error, e)
|
||||||
|
|
||||||
|
@ -396,7 +410,7 @@ class CoreClient:
|
||||||
session_id = self.session_id
|
session_id = self.session_id
|
||||||
try:
|
try:
|
||||||
response = self.client.delete_session(session_id)
|
response = self.client.delete_session(session_id)
|
||||||
logging.info("deleted session result: %s", response)
|
logging.info("deleted session(%s) result: %s", session_id, response)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.after(0, show_grpc_error, e)
|
self.app.after(0, show_grpc_error, e)
|
||||||
|
|
||||||
|
@ -527,6 +541,9 @@ class CoreClient:
|
||||||
try:
|
try:
|
||||||
terminal = self.app.guiconfig["preferences"]["terminal"]
|
terminal = self.app.guiconfig["preferences"]["terminal"]
|
||||||
response = self.client.get_node_terminal(self.session_id, node_id)
|
response = self.client.get_node_terminal(self.session_id, node_id)
|
||||||
|
output = os.popen(f"echo {terminal}").read()[:-1]
|
||||||
|
if output in DEFAULT_TERMS:
|
||||||
|
terminal = DEFAULT_TERMS[output]
|
||||||
cmd = f'{terminal} "{response.terminal}" &'
|
cmd = f'{terminal} "{response.terminal}" &'
|
||||||
logging.info("launching terminal %s", cmd)
|
logging.info("launching terminal %s", cmd)
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
@ -669,6 +686,8 @@ class CoreClient:
|
||||||
config = {x: self.emane_config[x].value for x in 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)
|
self.client.set_emane_config(self.session_id, config)
|
||||||
|
|
||||||
|
self.set_metadata()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
Clean ups when done using grpc
|
Clean ups when done using grpc
|
||||||
|
|
|
@ -215,3 +215,7 @@ class SessionsDialog(Dialog):
|
||||||
self.tree.delete(item[0])
|
self.tree.delete(item[0])
|
||||||
if sid == self.app.core.session_id:
|
if sid == self.app.core.session_id:
|
||||||
self.click_new()
|
self.click_new()
|
||||||
|
selections = self.tree.get_children()
|
||||||
|
if selections:
|
||||||
|
self.tree.focus(selections[0])
|
||||||
|
self.tree.selection_set(selections[0])
|
||||||
|
|
|
@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, Tuple
|
||||||
from core.gui import themes
|
from core.gui import themes
|
||||||
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
|
from core.gui.dialogs.linkconfig import LinkConfigurationDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import EdgeUtils, NodeUtils
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.graph.graph import CanvasGraph
|
from core.gui.graph.graph import CanvasGraph
|
||||||
|
@ -160,7 +160,7 @@ class CanvasEdge:
|
||||||
|
|
||||||
def complete(self, dst: int):
|
def complete(self, dst: int):
|
||||||
self.dst = dst
|
self.dst = dst
|
||||||
self.token = tuple(sorted((self.src, self.dst)))
|
self.token = EdgeUtils.get_token(self.src, self.dst)
|
||||||
x, y = self.canvas.coords(self.dst)
|
x, y = self.canvas.coords(self.dst)
|
||||||
x1, y1, _, _ = self.canvas.coords(self.id)
|
x1, y1, _, _ = self.canvas.coords(self.id)
|
||||||
self.canvas.coords(self.id, x1, y1, x, y)
|
self.canvas.coords(self.id, x1, y1, x, y)
|
||||||
|
|
|
@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, List, Tuple
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
from core.gui import nodeutils
|
|
||||||
from core.gui.dialogs.shapemod import ShapeDialog
|
from core.gui.dialogs.shapemod import ShapeDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||||
|
@ -13,8 +12,7 @@ from core.gui.graph.enums import GraphMode, ScaleOption
|
||||||
from core.gui.graph.node import CanvasNode
|
from core.gui.graph.node import CanvasNode
|
||||||
from core.gui.graph.shape import Shape
|
from core.gui.graph.shape import Shape
|
||||||
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
from core.gui.graph.shapeutils import ShapeType, is_draw_shape, is_marker
|
||||||
from core.gui.images import Images
|
from core.gui.nodeutils import EdgeUtils, NodeUtils
|
||||||
from core.gui.nodeutils import NodeUtils
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -192,7 +190,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
"""
|
"""
|
||||||
add a wireless edge between 2 canvas nodes
|
add a wireless edge between 2 canvas nodes
|
||||||
"""
|
"""
|
||||||
token = tuple(sorted((src.id, dst.id)))
|
token = EdgeUtils.get_token(src.id, dst.id)
|
||||||
x1, y1 = self.coords(src.id)
|
x1, y1 = self.coords(src.id)
|
||||||
x2, y2 = self.coords(dst.id)
|
x2, y2 = self.coords(dst.id)
|
||||||
position = (x1, y1, x2, y2)
|
position = (x1, y1, x2, y2)
|
||||||
|
@ -204,7 +202,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.tag_raise(dst.id)
|
self.tag_raise(dst.id)
|
||||||
|
|
||||||
def delete_wireless_edge(self, src: CanvasNode, dst: CanvasNode):
|
def delete_wireless_edge(self, src: CanvasNode, dst: CanvasNode):
|
||||||
token = tuple(sorted((src.id, dst.id)))
|
token = EdgeUtils.get_token(src.id, dst.id)
|
||||||
edge = self.wireless_edges.pop(token)
|
edge = self.wireless_edges.pop(token)
|
||||||
edge.delete()
|
edge.delete()
|
||||||
src.wireless_edges.remove(edge)
|
src.wireless_edges.remove(edge)
|
||||||
|
@ -219,16 +217,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
# peer to peer node is not drawn on the GUI
|
# peer to peer node is not drawn on the GUI
|
||||||
if NodeUtils.is_ignore_node(core_node.type):
|
if NodeUtils.is_ignore_node(core_node.type):
|
||||||
continue
|
continue
|
||||||
|
image = NodeUtils.node_image(core_node)
|
||||||
# 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:
|
|
||||||
image = Images.create(core_node.icon, nodeutils.ICON_SIZE)
|
|
||||||
except OSError:
|
|
||||||
logging.error("invalid icon: %s", core_node.icon)
|
|
||||||
|
|
||||||
x = core_node.position.x
|
x = core_node.position.x
|
||||||
y = core_node.position.y
|
y = core_node.position.y
|
||||||
node = CanvasNode(self.master, x, y, core_node, image)
|
node = CanvasNode(self.master, x, y, core_node, image)
|
||||||
|
@ -242,7 +231,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
node_one = canvas_node_one.core_node
|
node_one = canvas_node_one.core_node
|
||||||
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
|
canvas_node_two = self.core.canvas_nodes[link.node_two_id]
|
||||||
node_two = canvas_node_two.core_node
|
node_two = canvas_node_two.core_node
|
||||||
token = tuple(sorted((canvas_node_one.id, canvas_node_two.id)))
|
token = EdgeUtils.get_token(canvas_node_one.id, canvas_node_two.id)
|
||||||
|
|
||||||
if link.type == core_pb2.LinkType.WIRELESS:
|
if link.type == core_pb2.LinkType.WIRELESS:
|
||||||
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
self.add_wireless_edge(canvas_node_one, canvas_node_two)
|
||||||
|
@ -266,8 +255,10 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.core.links[edge.token] = edge
|
self.core.links[edge.token] = edge
|
||||||
if link.HasField("interface_one"):
|
if link.HasField("interface_one"):
|
||||||
canvas_node_one.interfaces.append(link.interface_one)
|
canvas_node_one.interfaces.append(link.interface_one)
|
||||||
|
edge.src_interface = link.interface_one
|
||||||
if link.HasField("interface_two"):
|
if link.HasField("interface_two"):
|
||||||
canvas_node_two.interfaces.append(link.interface_two)
|
canvas_node_two.interfaces.append(link.interface_two)
|
||||||
|
edge.dst_interface = link.interface_two
|
||||||
elif link.options.unidirectional:
|
elif link.options.unidirectional:
|
||||||
edge = self.edges[token]
|
edge = self.edges[token]
|
||||||
edge.asymmetric_link = link
|
edge.asymmetric_link = link
|
||||||
|
@ -385,7 +376,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
return
|
return
|
||||||
|
|
||||||
# ignore repeated edges
|
# ignore repeated edges
|
||||||
token = tuple(sorted((edge.src, self.selected)))
|
token = EdgeUtils.get_token(edge.src, self.selected)
|
||||||
if token in self.edges:
|
if token in self.edges:
|
||||||
edge.delete()
|
edge.delete()
|
||||||
return
|
return
|
||||||
|
@ -883,7 +874,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
dest_node_copy = self.nodes[copy_map[edge.token[1]]]
|
dest_node_copy = self.nodes[copy_map[edge.token[1]]]
|
||||||
self.create_edge(source_node_copy, dest_node_copy)
|
self.create_edge(source_node_copy, dest_node_copy)
|
||||||
copy_edge = self.edges[
|
copy_edge = self.edges[
|
||||||
tuple(sorted([source_node_copy.id, dest_node_copy.id]))
|
EdgeUtils.get_token(source_node_copy.id, dest_node_copy.id)
|
||||||
]
|
]
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
options = edge.link.options
|
options = edge.link.options
|
||||||
|
|
|
@ -32,10 +32,10 @@ class MenuAction:
|
||||||
self.app = app
|
self.app = app
|
||||||
self.canvas = app.canvas
|
self.canvas = app.canvas
|
||||||
|
|
||||||
def cleanup_old_session(self):
|
def cleanup_old_session(self, session_id):
|
||||||
logging.info("cleaning up old session")
|
logging.info("cleaning up old session")
|
||||||
self.app.core.stop_session()
|
self.app.core.stop_session()
|
||||||
self.app.core.delete_session()
|
self.app.core.delete_session(session_id)
|
||||||
|
|
||||||
def prompt_save_running_session(self, quitapp: bool = False):
|
def prompt_save_running_session(self, quitapp: bool = False):
|
||||||
"""
|
"""
|
||||||
|
@ -49,7 +49,12 @@ class MenuAction:
|
||||||
callback = None
|
callback = None
|
||||||
if quitapp:
|
if quitapp:
|
||||||
callback = self.app.quit
|
callback = self.app.quit
|
||||||
task = BackgroundTask(self.app, self.cleanup_old_session, callback)
|
task = BackgroundTask(
|
||||||
|
self.app,
|
||||||
|
self.cleanup_old_session,
|
||||||
|
callback,
|
||||||
|
(self.app.core.session_id,),
|
||||||
|
)
|
||||||
task.start()
|
task.start()
|
||||||
elif quitapp:
|
elif quitapp:
|
||||||
self.app.quit()
|
self.app.quit()
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
from typing import TYPE_CHECKING, Optional, Set
|
import logging
|
||||||
|
from typing import TYPE_CHECKING, Optional, Set, Tuple
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import NodeType
|
from core.api.grpc.core_pb2 import NodeType
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import core_pb2
|
||||||
|
from PIL import ImageTk
|
||||||
|
|
||||||
ICON_SIZE = 48
|
ICON_SIZE = 48
|
||||||
ANTENNA_SIZE = 32
|
ANTENNA_SIZE = 32
|
||||||
|
@ -89,11 +91,21 @@ class NodeUtils:
|
||||||
return node_type in cls.RJ45_NODES
|
return node_type in cls.RJ45_NODES
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def node_icon(cls, node_type: NodeType, model: str) -> bool:
|
def node_icon(cls, node_type: NodeType, model: str) -> "ImageTk.PhotoImage":
|
||||||
if model == "":
|
if model == "":
|
||||||
model = None
|
model = None
|
||||||
return cls.NODE_ICONS[(node_type, model)]
|
return cls.NODE_ICONS[(node_type, model)]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def node_image(cls, core_node: "core_pb2.Node") -> "ImageTk.PhotoImage":
|
||||||
|
image = cls.node_icon(core_node.type, core_node.model)
|
||||||
|
if core_node.icon:
|
||||||
|
try:
|
||||||
|
image = Images.create(core_node.icon, ICON_SIZE)
|
||||||
|
except OSError:
|
||||||
|
logging.error("invalid icon: %s", core_node.icon)
|
||||||
|
return image
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup(cls):
|
def setup(cls):
|
||||||
nodes = [
|
nodes = [
|
||||||
|
@ -123,3 +135,9 @@ class NodeUtils:
|
||||||
cls.NETWORK_NODES.append(node_draw)
|
cls.NETWORK_NODES.append(node_draw)
|
||||||
cls.NODE_ICONS[(node_type, None)] = node_draw.image
|
cls.NODE_ICONS[(node_type, None)] = node_draw.image
|
||||||
cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)
|
cls.ANTENNA_ICON = Images.get(ImageEnum.ANTENNA, ANTENNA_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
class EdgeUtils:
|
||||||
|
@classmethod
|
||||||
|
def get_token(cls, src: int, dst: int) -> Tuple[int, ...]:
|
||||||
|
return tuple(sorted([src, dst]))
|
||||||
|
|
Loading…
Reference in a new issue