pygui: added wrappers for most usages of protobufs within pygui
This commit is contained in:
parent
154fa8b77d
commit
77f6577bce
23 changed files with 475 additions and 173 deletions
|
@ -13,27 +13,8 @@ from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc import client
|
from core.api.grpc import client
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
|
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
|
||||||
from core.api.grpc.core_pb2 import (
|
from core.api.grpc.core_pb2 import CpuUsageEvent, Event, ThroughputsEvent
|
||||||
CpuUsageEvent,
|
|
||||||
Event,
|
|
||||||
ExceptionEvent,
|
|
||||||
Interface,
|
|
||||||
Link,
|
|
||||||
LinkEvent,
|
|
||||||
LinkType,
|
|
||||||
MessageType,
|
|
||||||
Node,
|
|
||||||
NodeEvent,
|
|
||||||
NodeType,
|
|
||||||
Position,
|
|
||||||
SessionLocation,
|
|
||||||
SessionState,
|
|
||||||
StartSessionResponse,
|
|
||||||
StopSessionResponse,
|
|
||||||
ThroughputsEvent,
|
|
||||||
)
|
|
||||||
from core.api.grpc.emane_pb2 import EmaneModelConfig
|
from core.api.grpc.emane_pb2 import EmaneModelConfig
|
||||||
from core.api.grpc.mobility_pb2 import MobilityConfig
|
from core.api.grpc.mobility_pb2 import MobilityConfig
|
||||||
from core.api.grpc.services_pb2 import NodeServiceData, ServiceConfig, ServiceFileConfig
|
from core.api.grpc.services_pb2 import NodeServiceData, ServiceConfig, ServiceFileConfig
|
||||||
|
@ -50,7 +31,22 @@ from core.gui.graph.shape import AnnotationData, Shape
|
||||||
from core.gui.graph.shapeutils import ShapeType
|
from core.gui.graph.shapeutils import ShapeType
|
||||||
from core.gui.interface import InterfaceManager
|
from core.gui.interface import InterfaceManager
|
||||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||||
from core.gui.wrappers import Hook
|
from core.gui.wrappers import (
|
||||||
|
ConfigOption,
|
||||||
|
ExceptionEvent,
|
||||||
|
Hook,
|
||||||
|
Interface,
|
||||||
|
Link,
|
||||||
|
LinkEvent,
|
||||||
|
LinkType,
|
||||||
|
MessageType,
|
||||||
|
Node,
|
||||||
|
NodeEvent,
|
||||||
|
NodeType,
|
||||||
|
Position,
|
||||||
|
SessionLocation,
|
||||||
|
SessionState,
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -165,12 +161,13 @@ class CoreClient:
|
||||||
return
|
return
|
||||||
|
|
||||||
if event.HasField("link_event"):
|
if event.HasField("link_event"):
|
||||||
self.app.after(0, self.handle_link_event, event.link_event)
|
link_event = LinkEvent.from_proto(event.link_event)
|
||||||
|
self.app.after(0, self.handle_link_event, link_event)
|
||||||
elif event.HasField("session_event"):
|
elif event.HasField("session_event"):
|
||||||
logging.info("session event: %s", event)
|
logging.info("session event: %s", event)
|
||||||
session_event = event.session_event
|
session_event = event.session_event
|
||||||
if session_event.event <= SessionState.SHUTDOWN:
|
if session_event.event <= SessionState.SHUTDOWN.value:
|
||||||
self.state = event.session_event.event
|
self.state = SessionState(session_event.event)
|
||||||
elif session_event.event in {7, 8, 9}:
|
elif session_event.event in {7, 8, 9}:
|
||||||
node_id = session_event.node_id
|
node_id = session_event.node_id
|
||||||
dialog = self.mobility_players.get(node_id)
|
dialog = self.mobility_players.get(node_id)
|
||||||
|
@ -184,10 +181,12 @@ class CoreClient:
|
||||||
else:
|
else:
|
||||||
logging.warning("unknown session event: %s", session_event)
|
logging.warning("unknown session event: %s", session_event)
|
||||||
elif event.HasField("node_event"):
|
elif event.HasField("node_event"):
|
||||||
self.app.after(0, self.handle_node_event, event.node_event)
|
node_event = NodeEvent.from_proto(event.node_event)
|
||||||
|
self.app.after(0, self.handle_node_event, node_event)
|
||||||
elif event.HasField("config_event"):
|
elif event.HasField("config_event"):
|
||||||
logging.info("config event: %s", event)
|
logging.info("config event: %s", event)
|
||||||
elif event.HasField("exception_event"):
|
elif event.HasField("exception_event"):
|
||||||
|
event = ExceptionEvent.from_proto(event.session_id, event.exception_event)
|
||||||
self.handle_exception_event(event)
|
self.handle_exception_event(event)
|
||||||
else:
|
else:
|
||||||
logging.info("unhandled event: %s", event)
|
logging.info("unhandled event: %s", event)
|
||||||
|
@ -307,7 +306,7 @@ class CoreClient:
|
||||||
try:
|
try:
|
||||||
response = self.client.get_session(self.session_id)
|
response = self.client.get_session(self.session_id)
|
||||||
session = response.session
|
session = response.session
|
||||||
self.state = session.state
|
self.state = SessionState(session.state)
|
||||||
self.handling_events = self.client.events(
|
self.handling_events = self.client.events(
|
||||||
self.session_id, self.handle_events
|
self.session_id, self.handle_events
|
||||||
)
|
)
|
||||||
|
@ -324,7 +323,7 @@ class CoreClient:
|
||||||
# get location
|
# get location
|
||||||
if query_location:
|
if query_location:
|
||||||
response = self.client.get_session_location(self.session_id)
|
response = self.client.get_session_location(self.session_id)
|
||||||
self.location = response.location
|
self.location = SessionLocation.from_proto(response.location)
|
||||||
|
|
||||||
# get emane models
|
# get emane models
|
||||||
response = self.client.get_emane_models(self.session_id)
|
response = self.client.get_emane_models(self.session_id)
|
||||||
|
@ -338,20 +337,22 @@ class CoreClient:
|
||||||
|
|
||||||
# get emane config
|
# get emane config
|
||||||
response = self.client.get_emane_config(self.session_id)
|
response = self.client.get_emane_config(self.session_id)
|
||||||
self.emane_config = response.config
|
self.emane_config = ConfigOption.from_dict(response.config)
|
||||||
|
|
||||||
# update interface manager
|
# update interface manager
|
||||||
self.ifaces_manager.joined(session.links)
|
self.ifaces_manager.joined(session.links)
|
||||||
|
|
||||||
# draw session
|
# draw session
|
||||||
self.app.canvas.reset_and_redraw(session)
|
nodes = [Node.from_proto(x) for x in session.nodes]
|
||||||
|
links = [Link.from_proto(x) for x in session.links]
|
||||||
|
self.app.canvas.reset_and_redraw(nodes, links)
|
||||||
|
|
||||||
# get mobility configs
|
# get mobility configs
|
||||||
response = self.client.get_mobility_configs(self.session_id)
|
response = self.client.get_mobility_configs(self.session_id)
|
||||||
for node_id in response.configs:
|
for node_id in response.configs:
|
||||||
config = response.configs[node_id].config
|
config = response.configs[node_id].config
|
||||||
canvas_node = self.canvas_nodes[node_id]
|
canvas_node = self.canvas_nodes[node_id]
|
||||||
canvas_node.mobility_config = dict(config)
|
canvas_node.mobility_config = ConfigOption.from_dict(config)
|
||||||
|
|
||||||
# get emane model config
|
# get emane model config
|
||||||
response = self.client.get_emane_model_configs(self.session_id)
|
response = self.client.get_emane_model_configs(self.session_id)
|
||||||
|
@ -360,16 +361,16 @@ class CoreClient:
|
||||||
if config.iface_id != -1:
|
if config.iface_id != -1:
|
||||||
iface_id = config.iface_id
|
iface_id = config.iface_id
|
||||||
canvas_node = self.canvas_nodes[config.node_id]
|
canvas_node = self.canvas_nodes[config.node_id]
|
||||||
canvas_node.emane_model_configs[(config.model, iface_id)] = dict(
|
canvas_node.emane_model_configs[
|
||||||
config.config
|
(config.model, iface_id)
|
||||||
)
|
] = ConfigOption.from_dict(config.config)
|
||||||
|
|
||||||
# get wlan configurations
|
# get wlan configurations
|
||||||
response = self.client.get_wlan_configs(self.session_id)
|
response = self.client.get_wlan_configs(self.session_id)
|
||||||
for _id in response.configs:
|
for _id in response.configs:
|
||||||
mapped_config = response.configs[_id]
|
mapped_config = response.configs[_id]
|
||||||
canvas_node = self.canvas_nodes[_id]
|
canvas_node = self.canvas_nodes[_id]
|
||||||
canvas_node.wlan_config = dict(mapped_config.config)
|
canvas_node.wlan_config = ConfigOption.from_dict(mapped_config.config)
|
||||||
|
|
||||||
# get service configurations
|
# get service configurations
|
||||||
response = self.client.get_node_service_configs(self.session_id)
|
response = self.client.get_node_service_configs(self.session_id)
|
||||||
|
@ -501,7 +502,6 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.client.connect()
|
self.client.connect()
|
||||||
self.setup_cpu_usage()
|
|
||||||
|
|
||||||
# get service information
|
# get service information
|
||||||
response = self.client.get_services()
|
response = self.client.get_services()
|
||||||
|
@ -546,8 +546,9 @@ class CoreClient:
|
||||||
|
|
||||||
def edit_node(self, core_node: Node) -> None:
|
def edit_node(self, core_node: Node) -> None:
|
||||||
try:
|
try:
|
||||||
|
position = core_node.position.to_proto()
|
||||||
self.client.edit_node(
|
self.client.edit_node(
|
||||||
self.session_id, core_node.id, core_node.position, source=GUI_SOURCE
|
self.session_id, core_node.id, position, source=GUI_SOURCE
|
||||||
)
|
)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Edit Node Error", e)
|
self.app.show_grpc_exception("Edit Node Error", e)
|
||||||
|
@ -556,18 +557,17 @@ class CoreClient:
|
||||||
for server in self.servers.values():
|
for server in self.servers.values():
|
||||||
self.client.add_session_server(self.session_id, server.name, server.address)
|
self.client.add_session_server(self.session_id, server.name, server.address)
|
||||||
|
|
||||||
def start_session(self) -> StartSessionResponse:
|
def start_session(self) -> Tuple[bool, List[str]]:
|
||||||
self.ifaces_manager.reset_mac()
|
self.ifaces_manager.reset_mac()
|
||||||
nodes = [x.core_node for x in self.canvas_nodes.values()]
|
nodes = [x.core_node.to_proto() for x in self.canvas_nodes.values()]
|
||||||
links = []
|
links = []
|
||||||
for edge in self.links.values():
|
for edge in self.links.values():
|
||||||
link = Link()
|
link = edge.link
|
||||||
link.CopyFrom(edge.link)
|
if link.iface1 and not link.iface1.mac:
|
||||||
if link.HasField("iface1") and not link.iface1.mac:
|
|
||||||
link.iface1.mac = self.ifaces_manager.next_mac()
|
link.iface1.mac = self.ifaces_manager.next_mac()
|
||||||
if link.HasField("iface2") and not link.iface2.mac:
|
if link.iface2 and not link.iface2.mac:
|
||||||
link.iface2.mac = self.ifaces_manager.next_mac()
|
link.iface2.mac = self.ifaces_manager.next_mac()
|
||||||
links.append(link)
|
links.append(link.to_proto())
|
||||||
wlan_configs = self.get_wlan_configs_proto()
|
wlan_configs = self.get_wlan_configs_proto()
|
||||||
mobility_configs = self.get_mobility_configs_proto()
|
mobility_configs = self.get_mobility_configs_proto()
|
||||||
emane_model_configs = self.get_emane_model_configs_proto()
|
emane_model_configs = self.get_emane_model_configs_proto()
|
||||||
|
@ -582,14 +582,15 @@ class CoreClient:
|
||||||
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
emane_config = {x: self.emane_config[x].value for x in self.emane_config}
|
||||||
else:
|
else:
|
||||||
emane_config = None
|
emane_config = None
|
||||||
response = StartSessionResponse(result=False)
|
result = False
|
||||||
|
exceptions = []
|
||||||
try:
|
try:
|
||||||
self.send_servers()
|
self.send_servers()
|
||||||
response = self.client.start_session(
|
response = self.client.start_session(
|
||||||
self.session_id,
|
self.session_id,
|
||||||
nodes,
|
nodes,
|
||||||
links,
|
links,
|
||||||
self.location,
|
self.location.to_proto(),
|
||||||
hooks,
|
hooks,
|
||||||
emane_config,
|
emane_config,
|
||||||
emane_model_configs,
|
emane_model_configs,
|
||||||
|
@ -605,20 +606,23 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
if response.result:
|
if response.result:
|
||||||
self.set_metadata()
|
self.set_metadata()
|
||||||
|
result = response.result
|
||||||
|
exceptions = response.exceptions
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Start Session Error", e)
|
self.app.show_grpc_exception("Start Session Error", e)
|
||||||
return response
|
return result, exceptions
|
||||||
|
|
||||||
def stop_session(self, session_id: int = None) -> StopSessionResponse:
|
def stop_session(self, session_id: int = None) -> bool:
|
||||||
if not session_id:
|
if not session_id:
|
||||||
session_id = self.session_id
|
session_id = self.session_id
|
||||||
response = StopSessionResponse(result=False)
|
result = False
|
||||||
try:
|
try:
|
||||||
response = self.client.stop_session(session_id)
|
response = self.client.stop_session(session_id)
|
||||||
logging.info("stopped session(%s), result: %s", session_id, response)
|
logging.info("stopped session(%s), result: %s", session_id, response)
|
||||||
|
result = response.result
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Stop Session Error", e)
|
self.app.show_grpc_exception("Stop Session Error", e)
|
||||||
return response
|
return result
|
||||||
|
|
||||||
def show_mobility_players(self) -> None:
|
def show_mobility_players(self) -> None:
|
||||||
for canvas_node in self.canvas_nodes.values():
|
for canvas_node in self.canvas_nodes.values():
|
||||||
|
@ -920,7 +924,7 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def deleted_graph_nodes(self, canvas_nodes: List[Node]) -> None:
|
def deleted_graph_nodes(self, canvas_nodes: List[CanvasNode]) -> None:
|
||||||
"""
|
"""
|
||||||
remove the nodes selected by the user and anything related to that node
|
remove the nodes selected by the user and anything related to that node
|
||||||
such as link, configurations, interfaces
|
such as link, configurations, interfaces
|
||||||
|
@ -951,13 +955,7 @@ class CoreClient:
|
||||||
ip6=ip6,
|
ip6=ip6,
|
||||||
ip6_mask=ip6_mask,
|
ip6_mask=ip6_mask,
|
||||||
)
|
)
|
||||||
logging.info(
|
logging.info("create node(%s) interface(%s)", node.name, iface)
|
||||||
"create node(%s) interface(%s) IPv4(%s) IPv6(%s)",
|
|
||||||
node.name,
|
|
||||||
iface.name,
|
|
||||||
iface.ip4,
|
|
||||||
iface.ip6,
|
|
||||||
)
|
|
||||||
return iface
|
return iface
|
||||||
|
|
||||||
def create_link(
|
def create_link(
|
||||||
|
@ -1010,8 +1008,7 @@ class CoreClient:
|
||||||
continue
|
continue
|
||||||
if not canvas_node.wlan_config:
|
if not canvas_node.wlan_config:
|
||||||
continue
|
continue
|
||||||
config = canvas_node.wlan_config
|
config = ConfigOption.to_dict(canvas_node.wlan_config)
|
||||||
config = {x: config[x].value for x in config}
|
|
||||||
node_id = canvas_node.core_node.id
|
node_id = canvas_node.core_node.id
|
||||||
wlan_config = WlanConfig(node_id=node_id, config=config)
|
wlan_config = WlanConfig(node_id=node_id, config=config)
|
||||||
configs.append(wlan_config)
|
configs.append(wlan_config)
|
||||||
|
@ -1024,8 +1021,7 @@ class CoreClient:
|
||||||
continue
|
continue
|
||||||
if not canvas_node.mobility_config:
|
if not canvas_node.mobility_config:
|
||||||
continue
|
continue
|
||||||
config = canvas_node.mobility_config
|
config = ConfigOption.to_dict(canvas_node.mobility_config)
|
||||||
config = {x: config[x].value for x in config}
|
|
||||||
node_id = canvas_node.core_node.id
|
node_id = canvas_node.core_node.id
|
||||||
mobility_config = MobilityConfig(node_id=node_id, config=config)
|
mobility_config = MobilityConfig(node_id=node_id, config=config)
|
||||||
configs.append(mobility_config)
|
configs.append(mobility_config)
|
||||||
|
@ -1039,7 +1035,7 @@ class CoreClient:
|
||||||
node_id = canvas_node.core_node.id
|
node_id = canvas_node.core_node.id
|
||||||
for key, config in canvas_node.emane_model_configs.items():
|
for key, config in canvas_node.emane_model_configs.items():
|
||||||
model, iface_id = key
|
model, iface_id = key
|
||||||
config = {x: config[x].value for x in config}
|
config = ConfigOption.to_dict(config)
|
||||||
if iface_id is None:
|
if iface_id is None:
|
||||||
iface_id = -1
|
iface_id = -1
|
||||||
config_proto = EmaneModelConfig(
|
config_proto = EmaneModelConfig(
|
||||||
|
@ -1116,7 +1112,7 @@ class CoreClient:
|
||||||
node_id,
|
node_id,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return dict(config)
|
return ConfigOption.from_dict(config)
|
||||||
|
|
||||||
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
def get_mobility_config(self, node_id: int) -> Dict[str, ConfigOption]:
|
||||||
response = self.client.get_mobility_config(self.session_id, node_id)
|
response = self.client.get_mobility_config(self.session_id, node_id)
|
||||||
|
@ -1126,7 +1122,7 @@ class CoreClient:
|
||||||
node_id,
|
node_id,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return dict(config)
|
return ConfigOption.from_dict(config)
|
||||||
|
|
||||||
def get_emane_model_config(
|
def get_emane_model_config(
|
||||||
self, node_id: int, model: str, iface_id: int = None
|
self, node_id: int, model: str, iface_id: int = None
|
||||||
|
@ -1145,7 +1141,7 @@ class CoreClient:
|
||||||
iface_id,
|
iface_id,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
return dict(config)
|
return ConfigOption.from_dict(config)
|
||||||
|
|
||||||
def execute_script(self, script) -> None:
|
def execute_script(self, script) -> None:
|
||||||
response = self.client.execute_script(script)
|
response = self.client.execute_script(script)
|
||||||
|
|
|
@ -5,10 +5,10 @@ import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, Dict, Optional
|
from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import ExceptionEvent, ExceptionLevel
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.widgets import CodeText
|
from core.gui.widgets import CodeText
|
||||||
|
from core.gui.wrappers import ExceptionEvent, ExceptionLevel
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -49,9 +49,8 @@ class AlertsDialog(Dialog):
|
||||||
self.tree.heading("source", text="Source")
|
self.tree.heading("source", text="Source")
|
||||||
self.tree.bind("<<TreeviewSelect>>", self.click_select)
|
self.tree.bind("<<TreeviewSelect>>", self.click_select)
|
||||||
|
|
||||||
for alarm in self.app.statusbar.core_alarms:
|
for exception in self.app.statusbar.core_alarms:
|
||||||
exception = alarm.exception_event
|
level_name = exception.level.name
|
||||||
level_name = ExceptionLevel.Enum.Name(exception.level)
|
|
||||||
node_id = exception.node_id if exception.node_id else ""
|
node_id = exception.node_id if exception.node_id else ""
|
||||||
insert_id = self.tree.insert(
|
insert_id = self.tree.insert(
|
||||||
"",
|
"",
|
||||||
|
@ -60,21 +59,21 @@ class AlertsDialog(Dialog):
|
||||||
values=(
|
values=(
|
||||||
exception.date,
|
exception.date,
|
||||||
level_name,
|
level_name,
|
||||||
alarm.session_id,
|
exception.session_id,
|
||||||
node_id,
|
node_id,
|
||||||
exception.source,
|
exception.source,
|
||||||
),
|
),
|
||||||
tags=(level_name,),
|
tags=(level_name,),
|
||||||
)
|
)
|
||||||
self.alarm_map[insert_id] = alarm
|
self.alarm_map[insert_id] = exception
|
||||||
|
|
||||||
error_name = ExceptionLevel.Enum.Name(ExceptionLevel.ERROR)
|
error_name = ExceptionLevel.ERROR.name
|
||||||
self.tree.tag_configure(error_name, background="#ff6666")
|
self.tree.tag_configure(error_name, background="#ff6666")
|
||||||
fatal_name = ExceptionLevel.Enum.Name(ExceptionLevel.FATAL)
|
fatal_name = ExceptionLevel.FATAL.name
|
||||||
self.tree.tag_configure(fatal_name, background="#d9d9d9")
|
self.tree.tag_configure(fatal_name, background="#d9d9d9")
|
||||||
warning_name = ExceptionLevel.Enum.Name(ExceptionLevel.WARNING)
|
warning_name = ExceptionLevel.WARNING.name
|
||||||
self.tree.tag_configure(warning_name, background="#ffff99")
|
self.tree.tag_configure(warning_name, background="#ffff99")
|
||||||
notice_name = ExceptionLevel.Enum.Name(ExceptionLevel.NOTICE)
|
notice_name = ExceptionLevel.NOTICE.name
|
||||||
self.tree.tag_configure(notice_name, background="#85e085")
|
self.tree.tag_configure(notice_name, background="#85e085")
|
||||||
|
|
||||||
yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
|
yscrollbar = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
|
||||||
|
@ -108,8 +107,8 @@ class AlertsDialog(Dialog):
|
||||||
|
|
||||||
def click_select(self, event: tk.Event) -> None:
|
def click_select(self, event: tk.Event) -> None:
|
||||||
current = self.tree.selection()[0]
|
current = self.tree.selection()[0]
|
||||||
alarm = self.alarm_map[current]
|
exception = self.alarm_map[current]
|
||||||
self.codetext.text.config(state=tk.NORMAL)
|
self.codetext.text.config(state=tk.NORMAL)
|
||||||
self.codetext.text.delete(1.0, tk.END)
|
self.codetext.text.delete(1.0, tk.END)
|
||||||
self.codetext.text.insert(1.0, alarm.exception_event.text)
|
self.codetext.text.insert(1.0, exception.text)
|
||||||
self.codetext.text.config(state=tk.DISABLED)
|
self.codetext.text.config(state=tk.DISABLED)
|
||||||
|
|
|
@ -8,11 +8,11 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.services_pb2 import ServiceValidationMode
|
from core.api.grpc.services_pb2 import ServiceValidationMode
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
|
from core.gui.widgets import CodeText, ConfigFrame, ListboxScroll
|
||||||
|
from core.gui.wrappers import ConfigOption
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -99,7 +99,7 @@ class ConfigServiceConfigDialog(Dialog):
|
||||||
service_config = self.canvas_node.config_service_configs.get(
|
service_config = self.canvas_node.config_service_configs.get(
|
||||||
self.service_name, {}
|
self.service_name, {}
|
||||||
)
|
)
|
||||||
self.config = response.config
|
self.config = ConfigOption.from_dict(response.config)
|
||||||
self.default_config = {x.name: x.value for x in self.config.values()}
|
self.default_config = {x.name: x.value for x in self.config.values()}
|
||||||
custom_config = service_config.get("config")
|
custom_config = service_config.get("config")
|
||||||
if custom_config:
|
if custom_config:
|
||||||
|
|
|
@ -8,12 +8,11 @@ from typing import TYPE_CHECKING, Dict, List, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import Node
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.widgets import ConfigFrame
|
from core.gui.widgets import ConfigFrame
|
||||||
|
from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -5,11 +5,11 @@ import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.gui import validation
|
from core.gui import validation
|
||||||
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
|
from core.gui.wrappers import Interface, Link, LinkOptions
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -21,7 +21,7 @@ def get_int(var: tk.StringVar) -> Optional[int]:
|
||||||
if value != "":
|
if value != "":
|
||||||
return int(value)
|
return int(value)
|
||||||
else:
|
else:
|
||||||
return None
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def get_float(var: tk.StringVar) -> Optional[float]:
|
def get_float(var: tk.StringVar) -> Optional[float]:
|
||||||
|
@ -29,14 +29,15 @@ def get_float(var: tk.StringVar) -> Optional[float]:
|
||||||
if value != "":
|
if value != "":
|
||||||
return float(value)
|
return float(value)
|
||||||
else:
|
else:
|
||||||
return None
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
class LinkConfigurationDialog(Dialog):
|
class LinkConfigurationDialog(Dialog):
|
||||||
def __init__(self, app: "Application", edge: "CanvasEdge") -> None:
|
def __init__(self, app: "Application", edge: "CanvasEdge") -> None:
|
||||||
super().__init__(app, "Link Configuration")
|
super().__init__(app, "Link Configuration")
|
||||||
self.edge: "CanvasEdge" = edge
|
self.edge: "CanvasEdge" = edge
|
||||||
self.is_symmetric: bool = edge.link.options.unidirectional is False
|
|
||||||
|
self.is_symmetric: bool = edge.link.is_symmetric()
|
||||||
if self.is_symmetric:
|
if self.is_symmetric:
|
||||||
symmetry_var = tk.StringVar(value=">>")
|
symmetry_var = tk.StringVar(value=">>")
|
||||||
else:
|
else:
|
||||||
|
@ -223,32 +224,32 @@ class LinkConfigurationDialog(Dialog):
|
||||||
delay = get_int(self.delay)
|
delay = get_int(self.delay)
|
||||||
duplicate = get_int(self.duplicate)
|
duplicate = get_int(self.duplicate)
|
||||||
loss = get_float(self.loss)
|
loss = get_float(self.loss)
|
||||||
options = core_pb2.LinkOptions(
|
options = LinkOptions(
|
||||||
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
bandwidth=bandwidth, jitter=jitter, delay=delay, dup=duplicate, loss=loss
|
||||||
)
|
)
|
||||||
link.options.CopyFrom(options)
|
link.options = options
|
||||||
|
|
||||||
iface1_id = None
|
iface1_id = None
|
||||||
if link.HasField("iface1"):
|
if link.iface1:
|
||||||
iface1_id = link.iface1.id
|
iface1_id = link.iface1.id
|
||||||
iface2_id = None
|
iface2_id = None
|
||||||
if link.HasField("iface2"):
|
if link.iface2:
|
||||||
iface2_id = link.iface2.id
|
iface2_id = link.iface2.id
|
||||||
|
|
||||||
if not self.is_symmetric:
|
if not self.is_symmetric:
|
||||||
link.options.unidirectional = True
|
link.options.unidirectional = True
|
||||||
asym_iface1 = None
|
asym_iface1 = None
|
||||||
if iface1_id:
|
if iface1_id:
|
||||||
asym_iface1 = core_pb2.Interface(id=iface1_id)
|
asym_iface1 = Interface(id=iface1_id)
|
||||||
asym_iface2 = None
|
asym_iface2 = None
|
||||||
if iface2_id:
|
if iface2_id:
|
||||||
asym_iface2 = core_pb2.Interface(id=iface2_id)
|
asym_iface2 = Interface(id=iface2_id)
|
||||||
down_bandwidth = get_int(self.down_bandwidth)
|
down_bandwidth = get_int(self.down_bandwidth)
|
||||||
down_jitter = get_int(self.down_jitter)
|
down_jitter = get_int(self.down_jitter)
|
||||||
down_delay = get_int(self.down_delay)
|
down_delay = get_int(self.down_delay)
|
||||||
down_duplicate = get_int(self.down_duplicate)
|
down_duplicate = get_int(self.down_duplicate)
|
||||||
down_loss = get_float(self.down_loss)
|
down_loss = get_float(self.down_loss)
|
||||||
options = core_pb2.LinkOptions(
|
options = LinkOptions(
|
||||||
bandwidth=down_bandwidth,
|
bandwidth=down_bandwidth,
|
||||||
jitter=down_jitter,
|
jitter=down_jitter,
|
||||||
delay=down_delay,
|
delay=down_delay,
|
||||||
|
@ -256,7 +257,7 @@ class LinkConfigurationDialog(Dialog):
|
||||||
loss=down_loss,
|
loss=down_loss,
|
||||||
unidirectional=True,
|
unidirectional=True,
|
||||||
)
|
)
|
||||||
self.edge.asymmetric_link = core_pb2.Link(
|
self.edge.asymmetric_link = Link(
|
||||||
node1_id=link.node2_id,
|
node1_id=link.node2_id,
|
||||||
node2_id=link.node1_id,
|
node2_id=link.node1_id,
|
||||||
iface1=asym_iface1,
|
iface1=asym_iface1,
|
||||||
|
@ -267,7 +268,7 @@ class LinkConfigurationDialog(Dialog):
|
||||||
link.options.unidirectional = False
|
link.options.unidirectional = False
|
||||||
self.edge.asymmetric_link = None
|
self.edge.asymmetric_link = None
|
||||||
|
|
||||||
if self.app.core.is_runtime() and link.HasField("options"):
|
if self.app.core.is_runtime() and link.options:
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session_id
|
||||||
self.app.core.client.edit_link(
|
self.app.core.client.edit_link(
|
||||||
session_id,
|
session_id,
|
||||||
|
@ -316,7 +317,7 @@ class LinkConfigurationDialog(Dialog):
|
||||||
color = self.app.canvas.itemcget(self.edge.id, "fill")
|
color = self.app.canvas.itemcget(self.edge.id, "fill")
|
||||||
self.color.set(color)
|
self.color.set(color)
|
||||||
link = self.edge.link
|
link = self.edge.link
|
||||||
if link.HasField("options"):
|
if link.options:
|
||||||
self.bandwidth.set(str(link.options.bandwidth))
|
self.bandwidth.set(str(link.options.bandwidth))
|
||||||
self.jitter.set(str(link.options.jitter))
|
self.jitter.set(str(link.options.jitter))
|
||||||
self.duplicate.set(str(link.options.dup))
|
self.duplicate.set(str(link.options.dup))
|
||||||
|
|
|
@ -6,11 +6,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import Node
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.widgets import ConfigFrame
|
from core.gui.widgets import ConfigFrame
|
||||||
|
from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -4,12 +4,11 @@ from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import Node
|
|
||||||
from core.api.grpc.mobility_pb2 import MobilityAction
|
from core.api.grpc.mobility_pb2 import MobilityAction
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.images import ImageEnum
|
from core.gui.images import ImageEnum
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
|
from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Dict, Optional
|
||||||
import netaddr
|
import netaddr
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import Node
|
|
||||||
from core.gui import nodeutils, validation
|
from core.gui import nodeutils, validation
|
||||||
from core.gui.appconfig import ICONS_PATH
|
from core.gui.appconfig import ICONS_PATH
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
|
@ -16,6 +15,7 @@ from core.gui.images import Images
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
from core.gui.widgets import ListboxScroll, image_chooser
|
from core.gui.widgets import ListboxScroll, image_chooser
|
||||||
|
from core.gui.wrappers import Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -5,10 +5,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.widgets import ConfigFrame
|
from core.gui.widgets import ConfigFrame
|
||||||
|
from core.gui.wrappers import ConfigOption
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -28,7 +28,7 @@ class SessionOptionsDialog(Dialog):
|
||||||
try:
|
try:
|
||||||
session_id = self.app.core.session_id
|
session_id = self.app.core.session_id
|
||||||
response = self.app.core.client.get_session_options(session_id)
|
response = self.app.core.client.get_session_options(session_id)
|
||||||
return response.config
|
return ConfigOption.from_dict(response.config)
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Get Session Options Error", e)
|
self.app.show_grpc_exception("Get Session Options Error", e)
|
||||||
self.has_error = True
|
self.has_error = True
|
||||||
|
|
|
@ -5,12 +5,11 @@ from typing import TYPE_CHECKING, List, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.api.grpc.core_pb2 import SessionSummary
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.images import ImageEnum, Images
|
from core.gui.images import ImageEnum, Images
|
||||||
from core.gui.task import ProgressTask
|
from core.gui.task import ProgressTask
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
|
from core.gui.wrappers import SessionState, SessionSummary
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -33,7 +32,7 @@ class SessionsDialog(Dialog):
|
||||||
try:
|
try:
|
||||||
response = self.app.core.client.get_sessions()
|
response = self.app.core.client.get_sessions()
|
||||||
logging.info("sessions: %s", response)
|
logging.info("sessions: %s", response)
|
||||||
return response.sessions
|
return [SessionSummary.from_proto(x) for x in response.sessions]
|
||||||
except grpc.RpcError as e:
|
except grpc.RpcError as e:
|
||||||
self.app.show_grpc_exception("Get Sessions Error", e)
|
self.app.show_grpc_exception("Get Sessions Error", e)
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
@ -82,7 +81,7 @@ class SessionsDialog(Dialog):
|
||||||
self.tree.heading("nodes", text="Node Count")
|
self.tree.heading("nodes", text="Node Count")
|
||||||
|
|
||||||
for index, session in enumerate(self.sessions):
|
for index, session in enumerate(self.sessions):
|
||||||
state_name = core_pb2.SessionState.Enum.Name(session.state)
|
state_name = SessionState(session.state).name
|
||||||
self.tree.insert(
|
self.tree.insert(
|
||||||
"",
|
"",
|
||||||
tk.END,
|
tk.END,
|
||||||
|
|
|
@ -3,11 +3,10 @@ from typing import TYPE_CHECKING, Dict, Optional
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import Node
|
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import PADX, PADY
|
from core.gui.themes import PADX, PADY
|
||||||
from core.gui.widgets import ConfigFrame
|
from core.gui.widgets import ConfigFrame
|
||||||
|
from core.gui.wrappers import ConfigOption, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import Interface
|
|
||||||
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
||||||
from core.gui.utils import bandwidth_text
|
from core.gui.utils import bandwidth_text
|
||||||
|
from core.gui.wrappers import Interface
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -62,7 +62,7 @@ class EdgeInfoFrame(InfoFrameBase):
|
||||||
ip6 = f"{iface2.ip6}/{iface2.ip6_mask}" if iface2.ip6 else ""
|
ip6 = f"{iface2.ip6}/{iface2.ip6_mask}" if iface2.ip6 else ""
|
||||||
frame.add_detail("IP6", ip6)
|
frame.add_detail("IP6", ip6)
|
||||||
|
|
||||||
if link.HasField("options"):
|
if link.options:
|
||||||
frame.add_separator()
|
frame.add_separator()
|
||||||
bandwidth = bandwidth_text(options.bandwidth)
|
bandwidth = bandwidth_text(options.bandwidth)
|
||||||
frame.add_detail("Bandwidth", bandwidth)
|
frame.add_detail("Bandwidth", bandwidth)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import NodeType
|
|
||||||
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
from core.gui.frames.base import DetailsFrame, InfoFrameBase
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
from core.gui.wrappers import NodeType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -3,14 +3,13 @@ import math
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from typing import TYPE_CHECKING, Optional, Tuple
|
from typing import TYPE_CHECKING, Optional, Tuple
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.api.grpc.core_pb2 import Interface, Link
|
|
||||||
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.frames.link import EdgeInfoFrame, WirelessEdgeInfoFrame
|
from core.gui.frames.link import EdgeInfoFrame, WirelessEdgeInfoFrame
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
from core.gui.utils import bandwidth_text
|
from core.gui.utils import bandwidth_text
|
||||||
|
from core.gui.wrappers import Interface, Link
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.graph.graph import CanvasGraph
|
from core.gui.graph.graph import CanvasGraph
|
||||||
|
@ -305,7 +304,7 @@ class CanvasEdge(Edge):
|
||||||
self.link = link
|
self.link = link
|
||||||
self.draw_labels()
|
self.draw_labels()
|
||||||
|
|
||||||
def iface_label(self, iface: core_pb2.Interface) -> str:
|
def iface_label(self, iface: Interface) -> str:
|
||||||
label = ""
|
label = ""
|
||||||
if iface.name and self.canvas.show_iface_names.get():
|
if iface.name and self.canvas.show_iface_names.get():
|
||||||
label = f"{iface.name}"
|
label = f"{iface.name}"
|
||||||
|
@ -319,10 +318,10 @@ class CanvasEdge(Edge):
|
||||||
|
|
||||||
def create_node_labels(self) -> Tuple[str, str]:
|
def create_node_labels(self) -> Tuple[str, str]:
|
||||||
label1 = None
|
label1 = None
|
||||||
if self.link.HasField("iface1"):
|
if self.link.iface1:
|
||||||
label1 = self.iface_label(self.link.iface1)
|
label1 = self.iface_label(self.link.iface1)
|
||||||
label2 = None
|
label2 = None
|
||||||
if self.link.HasField("iface2"):
|
if self.link.iface2:
|
||||||
label2 = self.iface_label(self.link.iface2)
|
label2 = self.iface_label(self.link.iface2)
|
||||||
return label1, label2
|
return label1, label2
|
||||||
|
|
||||||
|
@ -417,6 +416,8 @@ class CanvasEdge(Edge):
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
def draw_link_options(self):
|
def draw_link_options(self):
|
||||||
|
if not self.link.options:
|
||||||
|
return
|
||||||
options = self.link.options
|
options = self.link.options
|
||||||
lines = []
|
lines = []
|
||||||
bandwidth = options.bandwidth
|
bandwidth = options.bandwidth
|
||||||
|
|
|
@ -7,14 +7,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import (
|
from core.api.grpc.core_pb2 import ThroughputsEvent
|
||||||
Interface,
|
|
||||||
Link,
|
|
||||||
LinkType,
|
|
||||||
Node,
|
|
||||||
Session,
|
|
||||||
ThroughputsEvent,
|
|
||||||
)
|
|
||||||
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 (
|
from core.gui.graph.edges import (
|
||||||
|
@ -30,6 +23,7 @@ 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 ImageEnum, TypeToImage
|
from core.gui.images import ImageEnum, TypeToImage
|
||||||
from core.gui.nodeutils import NodeDraw, NodeUtils
|
from core.gui.nodeutils import NodeDraw, NodeUtils
|
||||||
|
from core.gui.wrappers import Interface, Link, LinkType, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -134,12 +128,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
)
|
)
|
||||||
self.configure(scrollregion=self.bbox(tk.ALL))
|
self.configure(scrollregion=self.bbox(tk.ALL))
|
||||||
|
|
||||||
def reset_and_redraw(self, session: Session) -> None:
|
def reset_and_redraw(self, nodes: List[Node], links: List[Link]) -> None:
|
||||||
"""
|
|
||||||
Reset the private variables CanvasGraph object, redraw nodes given the new grpc
|
|
||||||
client.
|
|
||||||
:param session: session to draw
|
|
||||||
"""
|
|
||||||
# reset view options to default state
|
# reset view options to default state
|
||||||
self.show_node_labels.set(True)
|
self.show_node_labels.set(True)
|
||||||
self.show_link_labels.set(True)
|
self.show_link_labels.set(True)
|
||||||
|
@ -164,7 +153,7 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.wireless_edges.clear()
|
self.wireless_edges.clear()
|
||||||
self.wireless_network.clear()
|
self.wireless_network.clear()
|
||||||
self.drawing_edge = None
|
self.drawing_edge = None
|
||||||
self.draw_session(session)
|
self.draw_session(nodes, links)
|
||||||
|
|
||||||
def setup_bindings(self) -> None:
|
def setup_bindings(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -251,12 +240,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
dst.edges.add(edge)
|
dst.edges.add(edge)
|
||||||
self.edges[edge.token] = edge
|
self.edges[edge.token] = edge
|
||||||
self.core.links[edge.token] = edge
|
self.core.links[edge.token] = edge
|
||||||
if link.HasField("iface1"):
|
if link.iface1:
|
||||||
iface1 = link.iface1
|
iface1 = link.iface1
|
||||||
self.core.iface_to_edge[(node1.id, iface1.id)] = token
|
self.core.iface_to_edge[(node1.id, iface1.id)] = token
|
||||||
src.ifaces[iface1.id] = iface1
|
src.ifaces[iface1.id] = iface1
|
||||||
edge.src_iface = iface1
|
edge.src_iface = iface1
|
||||||
if link.HasField("iface2"):
|
if link.iface2:
|
||||||
iface2 = link.iface2
|
iface2 = link.iface2
|
||||||
self.core.iface_to_edge[(node2.id, iface2.id)] = edge.token
|
self.core.iface_to_edge[(node2.id, iface2.id)] = edge.token
|
||||||
dst.ifaces[iface2.id] = iface2
|
dst.ifaces[iface2.id] = iface2
|
||||||
|
@ -337,19 +326,19 @@ class CanvasGraph(tk.Canvas):
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
self.core.canvas_nodes[core_node.id] = node
|
self.core.canvas_nodes[core_node.id] = node
|
||||||
|
|
||||||
def draw_session(self, session: Session) -> None:
|
def draw_session(self, nodes: List[Node], links: List[Link]) -> None:
|
||||||
"""
|
"""
|
||||||
Draw existing session.
|
Draw existing session.
|
||||||
"""
|
"""
|
||||||
# draw existing nodes
|
# draw existing nodes
|
||||||
for core_node in session.nodes:
|
for core_node in nodes:
|
||||||
# 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
|
||||||
self.add_core_node(core_node)
|
self.add_core_node(core_node)
|
||||||
|
|
||||||
# draw existing links
|
# draw existing links
|
||||||
for link in session.links:
|
for link in links:
|
||||||
logging.debug("drawing link: %s", link)
|
logging.debug("drawing link: %s", link)
|
||||||
canvas_node1 = self.core.canvas_nodes[link.node1_id]
|
canvas_node1 = self.core.canvas_nodes[link.node1_id]
|
||||||
canvas_node2 = self.core.canvas_nodes[link.node2_id]
|
canvas_node2 = self.core.canvas_nodes[link.node2_id]
|
||||||
|
@ -987,12 +976,12 @@ class CanvasGraph(tk.Canvas):
|
||||||
copy_edge = self.edges[token]
|
copy_edge = self.edges[token]
|
||||||
copy_link = copy_edge.link
|
copy_link = copy_edge.link
|
||||||
options = edge.link.options
|
options = edge.link.options
|
||||||
copy_link.options.CopyFrom(options)
|
copy_link.options = deepcopy(options)
|
||||||
iface1_id = None
|
iface1_id = None
|
||||||
if copy_link.HasField("iface1"):
|
if copy_link.iface1:
|
||||||
iface1_id = copy_link.iface1.id
|
iface1_id = copy_link.iface1.id
|
||||||
iface2_id = None
|
iface2_id = None
|
||||||
if copy_link.HasField("iface2"):
|
if copy_link.iface2:
|
||||||
iface2_id = copy_link.iface2.id
|
iface2_id = copy_link.iface2.id
|
||||||
if not options.unidirectional:
|
if not options.unidirectional:
|
||||||
copy_edge.asymmetric_link = None
|
copy_edge.asymmetric_link = None
|
||||||
|
|
|
@ -6,8 +6,6 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
||||||
import grpc
|
import grpc
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import Interface, Node, NodeType
|
|
||||||
from core.api.grpc.services_pb2 import NodeServiceData
|
from core.api.grpc.services_pb2 import NodeServiceData
|
||||||
from core.gui import themes
|
from core.gui import themes
|
||||||
from core.gui.dialogs.emaneconfig import EmaneConfigDialog
|
from core.gui.dialogs.emaneconfig import EmaneConfigDialog
|
||||||
|
@ -22,6 +20,7 @@ from core.gui.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||||
from core.gui.graph.tooltip import CanvasTooltip
|
from core.gui.graph.tooltip import CanvasTooltip
|
||||||
from core.gui.images import ImageEnum
|
from core.gui.images import ImageEnum
|
||||||
from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils
|
from core.gui.nodeutils import ANTENNA_SIZE, NodeUtils
|
||||||
|
from core.gui.wrappers import ConfigOption, Interface, Node, NodeType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
|
@ -5,8 +5,8 @@ from typing import Dict, Optional, Tuple
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import NodeType
|
|
||||||
from core.gui.appconfig import LOCAL_ICONS_PATH
|
from core.gui.appconfig import LOCAL_ICONS_PATH
|
||||||
|
from core.gui.wrappers import NodeType
|
||||||
|
|
||||||
|
|
||||||
class Images:
|
class Images:
|
||||||
|
|
|
@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
|
||||||
import netaddr
|
import netaddr
|
||||||
from netaddr import EUI, IPNetwork
|
from netaddr import EUI, IPNetwork
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import Interface, Link, Node
|
|
||||||
from core.gui.graph.node import CanvasNode
|
from core.gui.graph.node import CanvasNode
|
||||||
from core.gui.nodeutils import NodeUtils
|
from core.gui.nodeutils import NodeUtils
|
||||||
|
from core.gui.wrappers import Interface, Link, Node
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -89,10 +89,10 @@ class InterfaceManager:
|
||||||
remaining_subnets = set()
|
remaining_subnets = set()
|
||||||
for edge in self.app.core.links.values():
|
for edge in self.app.core.links.values():
|
||||||
link = edge.link
|
link = edge.link
|
||||||
if link.HasField("iface1"):
|
if link.iface1:
|
||||||
subnets = self.get_subnets(link.iface1)
|
subnets = self.get_subnets(link.iface1)
|
||||||
remaining_subnets.add(subnets)
|
remaining_subnets.add(subnets)
|
||||||
if link.HasField("iface2"):
|
if link.iface2:
|
||||||
subnets = self.get_subnets(link.iface2)
|
subnets = self.get_subnets(link.iface2)
|
||||||
remaining_subnets.add(subnets)
|
remaining_subnets.add(subnets)
|
||||||
|
|
||||||
|
@ -100,9 +100,9 @@ class InterfaceManager:
|
||||||
# or remove used indexes from subnet
|
# or remove used indexes from subnet
|
||||||
ifaces = []
|
ifaces = []
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.HasField("iface1"):
|
if link.iface1:
|
||||||
ifaces.append(link.iface1)
|
ifaces.append(link.iface1)
|
||||||
if link.HasField("iface2"):
|
if link.iface2:
|
||||||
ifaces.append(link.iface2)
|
ifaces.append(link.iface2)
|
||||||
for iface in ifaces:
|
for iface in ifaces:
|
||||||
subnets = self.get_subnets(iface)
|
subnets = self.get_subnets(iface)
|
||||||
|
@ -117,9 +117,9 @@ class InterfaceManager:
|
||||||
def joined(self, links: List[Link]) -> None:
|
def joined(self, links: List[Link]) -> None:
|
||||||
ifaces = []
|
ifaces = []
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.HasField("iface1"):
|
if link.iface1:
|
||||||
ifaces.append(link.iface1)
|
ifaces.append(link.iface1)
|
||||||
if link.HasField("iface2"):
|
if link.iface2:
|
||||||
ifaces.append(link.iface2)
|
ifaces.append(link.iface2)
|
||||||
|
|
||||||
# add to used subnets and mark used indexes
|
# add to used subnets and mark used indexes
|
||||||
|
|
|
@ -3,9 +3,9 @@ from typing import List, Optional, Set
|
||||||
|
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import Node, NodeType
|
|
||||||
from core.gui.appconfig import CustomNode, GuiConfig
|
from core.gui.appconfig import CustomNode, GuiConfig
|
||||||
from core.gui.images import ImageEnum, Images, TypeToImage
|
from core.gui.images import ImageEnum, Images, TypeToImage
|
||||||
|
from core.gui.wrappers import Node, NodeType
|
||||||
|
|
||||||
ICON_SIZE: int = 48
|
ICON_SIZE: int = 48
|
||||||
ANTENNA_SIZE: int = 32
|
ANTENNA_SIZE: int = 32
|
||||||
|
@ -17,7 +17,7 @@ class NodeDraw:
|
||||||
self.image: Optional[PhotoImage] = None
|
self.image: Optional[PhotoImage] = None
|
||||||
self.image_enum: Optional[ImageEnum] = None
|
self.image_enum: Optional[ImageEnum] = None
|
||||||
self.image_file: Optional[str] = None
|
self.image_file: Optional[str] = None
|
||||||
self.node_type: NodeType = None
|
self.node_type: Optional[NodeType] = None
|
||||||
self.model: Optional[str] = None
|
self.model: Optional[str] = None
|
||||||
self.services: Set[str] = set()
|
self.services: Set[str] = set()
|
||||||
self.label: Optional[str] = None
|
self.label: Optional[str] = None
|
||||||
|
|
|
@ -5,9 +5,9 @@ import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, List, Optional
|
from typing import TYPE_CHECKING, List, Optional
|
||||||
|
|
||||||
from core.api.grpc.core_pb2 import ExceptionEvent, ExceptionLevel
|
|
||||||
from core.gui.dialogs.alerts import AlertsDialog
|
from core.gui.dialogs.alerts import AlertsDialog
|
||||||
from core.gui.themes import Styles
|
from core.gui.themes import Styles
|
||||||
|
from core.gui.wrappers import ExceptionEvent, ExceptionLevel
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -69,7 +69,7 @@ class StatusBar(ttk.Frame):
|
||||||
|
|
||||||
def add_alert(self, event: ExceptionEvent) -> None:
|
def add_alert(self, event: ExceptionEvent) -> None:
|
||||||
self.core_alarms.append(event)
|
self.core_alarms.append(event)
|
||||||
level = event.exception_event.level
|
level = event.level
|
||||||
self._set_alert_style(level)
|
self._set_alert_style(level)
|
||||||
label = f"Alerts ({len(self.core_alarms)})"
|
label = f"Alerts ({len(self.core_alarms)})"
|
||||||
self.alerts_button.config(text=label, style=self.alert_style)
|
self.alerts_button.config(text=label, style=self.alert_style)
|
||||||
|
|
|
@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Callable, List, Optional
|
||||||
|
|
||||||
from PIL.ImageTk import PhotoImage
|
from PIL.ImageTk import PhotoImage
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
from core.gui.dialogs.colorpicker import ColorPickerDialog
|
||||||
from core.gui.dialogs.runtool import RunToolDialog
|
from core.gui.dialogs.runtool import RunToolDialog
|
||||||
from core.gui.graph import tags
|
from core.gui.graph import tags
|
||||||
|
@ -300,15 +299,15 @@ class Toolbar(ttk.Frame):
|
||||||
)
|
)
|
||||||
task.start()
|
task.start()
|
||||||
|
|
||||||
def start_callback(self, response: core_pb2.StartSessionResponse) -> None:
|
def start_callback(self, result: bool, exceptions: List[str]) -> None:
|
||||||
if response.result:
|
if result:
|
||||||
self.set_runtime()
|
self.set_runtime()
|
||||||
self.app.core.set_metadata()
|
self.app.core.set_metadata()
|
||||||
self.app.core.show_mobility_players()
|
self.app.core.show_mobility_players()
|
||||||
else:
|
else:
|
||||||
enable_buttons(self.design_frame, enabled=True)
|
enable_buttons(self.design_frame, enabled=True)
|
||||||
if response.exceptions:
|
if exceptions:
|
||||||
message = "\n".join(response.exceptions)
|
message = "\n".join(exceptions)
|
||||||
self.app.show_error("Start Session Error", message)
|
self.app.show_error("Start Session Error", message)
|
||||||
|
|
||||||
def set_runtime(self) -> None:
|
def set_runtime(self) -> None:
|
||||||
|
@ -405,7 +404,7 @@ class Toolbar(ttk.Frame):
|
||||||
)
|
)
|
||||||
task.start()
|
task.start()
|
||||||
|
|
||||||
def stop_callback(self, response: core_pb2.StopSessionResponse) -> None:
|
def stop_callback(self, result: bool) -> None:
|
||||||
self.set_design()
|
self.set_design()
|
||||||
self.app.canvas.stopped_session()
|
self.app.canvas.stopped_session()
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,10 @@ from pathlib import Path
|
||||||
from tkinter import filedialog, font, ttk
|
from tkinter import filedialog, font, ttk
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Set, Type
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Set, Type
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
|
||||||
from core.api.grpc.core_pb2 import ConfigOptionType
|
|
||||||
from core.gui import themes, validation
|
from core.gui import themes, validation
|
||||||
from core.gui.dialogs.dialog import Dialog
|
from core.gui.dialogs.dialog import Dialog
|
||||||
from core.gui.themes import FRAME_PAD, PADX, PADY
|
from core.gui.themes import FRAME_PAD, PADX, PADY
|
||||||
|
from core.gui.wrappers import ConfigOption, ConfigOptionType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
@ -110,7 +108,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
label = ttk.Label(tab.frame, text=option.label)
|
label = ttk.Label(tab.frame, text=option.label)
|
||||||
label.grid(row=index, pady=PADY, padx=PADX, sticky="w")
|
label.grid(row=index, pady=PADY, padx=PADX, sticky="w")
|
||||||
value = tk.StringVar()
|
value = tk.StringVar()
|
||||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
if option.type == ConfigOptionType.BOOL:
|
||||||
select = ("On", "Off")
|
select = ("On", "Off")
|
||||||
state = "readonly" if self.enabled else tk.DISABLED
|
state = "readonly" if self.enabled else tk.DISABLED
|
||||||
combobox = ttk.Combobox(
|
combobox = ttk.Combobox(
|
||||||
|
@ -129,7 +127,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
tab.frame, textvariable=value, values=select, state=state
|
tab.frame, textvariable=value, values=select, state=state
|
||||||
)
|
)
|
||||||
combobox.grid(row=index, column=1, sticky="ew")
|
combobox.grid(row=index, column=1, sticky="ew")
|
||||||
elif option.type == core_pb2.ConfigOptionType.STRING:
|
elif option.type == ConfigOptionType.STRING:
|
||||||
value.set(option.value)
|
value.set(option.value)
|
||||||
state = tk.NORMAL if self.enabled else tk.DISABLED
|
state = tk.NORMAL if self.enabled else tk.DISABLED
|
||||||
if "file" in option.label:
|
if "file" in option.label:
|
||||||
|
@ -153,7 +151,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
tab.frame, textvariable=value, state=state
|
tab.frame, textvariable=value, state=state
|
||||||
)
|
)
|
||||||
entry.grid(row=index, column=1, sticky="ew")
|
entry.grid(row=index, column=1, sticky="ew")
|
||||||
elif option.type == core_pb2.ConfigOptionType.FLOAT:
|
elif option.type == ConfigOptionType.FLOAT:
|
||||||
value.set(option.value)
|
value.set(option.value)
|
||||||
state = tk.NORMAL if self.enabled else tk.DISABLED
|
state = tk.NORMAL if self.enabled else tk.DISABLED
|
||||||
entry = validation.PositiveFloatEntry(
|
entry = validation.PositiveFloatEntry(
|
||||||
|
@ -169,7 +167,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
option = self.config[key]
|
option = self.config[key]
|
||||||
value = self.values[key]
|
value = self.values[key]
|
||||||
config_value = value.get()
|
config_value = value.get()
|
||||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
if option.type == ConfigOptionType.BOOL:
|
||||||
if config_value == "On":
|
if config_value == "On":
|
||||||
option.value = "1"
|
option.value = "1"
|
||||||
else:
|
else:
|
||||||
|
@ -182,7 +180,7 @@ class ConfigFrame(ttk.Notebook):
|
||||||
for name, data in config.items():
|
for name, data in config.items():
|
||||||
option = self.config[name]
|
option = self.config[name]
|
||||||
value = self.values[name]
|
value = self.values[name]
|
||||||
if option.type == core_pb2.ConfigOptionType.BOOL:
|
if option.type == ConfigOptionType.BOOL:
|
||||||
if data == "1":
|
if data == "1":
|
||||||
data = "On"
|
data = "On"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,8 +1,22 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import List
|
from typing import Dict, List
|
||||||
|
|
||||||
from core.api.grpc import core_pb2
|
from core.api.grpc import common_pb2, core_pb2
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigOptionType(Enum):
|
||||||
|
UINT8 = 1
|
||||||
|
UINT16 = 2
|
||||||
|
UINT32 = 3
|
||||||
|
UINT64 = 4
|
||||||
|
INT8 = 5
|
||||||
|
INT16 = 6
|
||||||
|
INT32 = 7
|
||||||
|
INT64 = 8
|
||||||
|
FLOAT = 9
|
||||||
|
STRING = 10
|
||||||
|
BOOL = 11
|
||||||
|
|
||||||
|
|
||||||
class SessionState(Enum):
|
class SessionState(Enum):
|
||||||
|
@ -30,6 +44,292 @@ class NodeType(Enum):
|
||||||
LXC = 16
|
LXC = 16
|
||||||
|
|
||||||
|
|
||||||
|
class LinkType(Enum):
|
||||||
|
WIRELESS = 0
|
||||||
|
WIRED = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionLevel(Enum):
|
||||||
|
DEFAULT = 0
|
||||||
|
FATAL = 1
|
||||||
|
ERROR = 2
|
||||||
|
WARNING = 3
|
||||||
|
NOTICE = 4
|
||||||
|
|
||||||
|
|
||||||
|
class MessageType(Enum):
|
||||||
|
NONE = 0
|
||||||
|
ADD = 1
|
||||||
|
DELETE = 2
|
||||||
|
CRI = 4
|
||||||
|
LOCAL = 8
|
||||||
|
STRING = 16
|
||||||
|
TEXT = 32
|
||||||
|
TTY = 64
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SessionLocation:
|
||||||
|
x: float
|
||||||
|
y: float
|
||||||
|
z: float
|
||||||
|
lat: float
|
||||||
|
lon: float
|
||||||
|
alt: float
|
||||||
|
scale: float
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, location: core_pb2.SessionLocation) -> "SessionLocation":
|
||||||
|
return SessionLocation(
|
||||||
|
x=location.x,
|
||||||
|
y=location.y,
|
||||||
|
z=location.z,
|
||||||
|
lat=location.lat,
|
||||||
|
lon=location.lon,
|
||||||
|
alt=location.alt,
|
||||||
|
scale=location.scale,
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_proto(self) -> core_pb2.SessionLocation:
|
||||||
|
return core_pb2.SessionLocation(
|
||||||
|
x=self.x,
|
||||||
|
y=self.y,
|
||||||
|
z=self.z,
|
||||||
|
lat=self.lat,
|
||||||
|
lon=self.lon,
|
||||||
|
alt=self.alt,
|
||||||
|
scale=self.scale,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExceptionEvent:
|
||||||
|
session_id: int
|
||||||
|
node_id: int
|
||||||
|
level: ExceptionLevel
|
||||||
|
source: str
|
||||||
|
date: str
|
||||||
|
text: str
|
||||||
|
opaque: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(
|
||||||
|
cls, session_id: int, event: core_pb2.ExceptionEvent
|
||||||
|
) -> "ExceptionEvent":
|
||||||
|
return ExceptionEvent(
|
||||||
|
session_id=session_id,
|
||||||
|
node_id=event.node_id,
|
||||||
|
level=ExceptionLevel(event.level),
|
||||||
|
source=event.source,
|
||||||
|
date=event.date,
|
||||||
|
text=event.text,
|
||||||
|
opaque=event.opaque,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ConfigOption:
|
||||||
|
label: str
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
type: ConfigOptionType
|
||||||
|
group: str
|
||||||
|
select: List[str] = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(
|
||||||
|
cls, config: Dict[str, common_pb2.ConfigOption]
|
||||||
|
) -> Dict[str, "ConfigOption"]:
|
||||||
|
d = {}
|
||||||
|
for key, value in config.items():
|
||||||
|
d[key] = ConfigOption.from_proto(value)
|
||||||
|
return d
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_dict(cls, config: Dict[str, "ConfigOption"]) -> Dict[str, str]:
|
||||||
|
return {k: v.value for k, v in config.items()}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, option: common_pb2.ConfigOption) -> "ConfigOption":
|
||||||
|
return ConfigOption(
|
||||||
|
label=option.label,
|
||||||
|
name=option.name,
|
||||||
|
value=option.value,
|
||||||
|
type=ConfigOptionType(option.type),
|
||||||
|
group=option.group,
|
||||||
|
select=option.select,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Interface:
|
||||||
|
id: int
|
||||||
|
name: str = None
|
||||||
|
mac: str = None
|
||||||
|
ip4: str = None
|
||||||
|
ip4_mask: int = None
|
||||||
|
ip6: str = None
|
||||||
|
ip6_mask: int = None
|
||||||
|
net_id: int = None
|
||||||
|
flow_id: int = None
|
||||||
|
mtu: int = None
|
||||||
|
node_id: int = None
|
||||||
|
net2_id: int = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, iface: core_pb2.Interface) -> "Interface":
|
||||||
|
return Interface(
|
||||||
|
id=iface.id,
|
||||||
|
name=iface.name,
|
||||||
|
mac=iface.mac,
|
||||||
|
ip4=iface.ip4,
|
||||||
|
ip4_mask=iface.ip4_mask,
|
||||||
|
ip6=iface.ip6,
|
||||||
|
ip6_mask=iface.ip6_mask,
|
||||||
|
net_id=iface.net_id,
|
||||||
|
flow_id=iface.flow_id,
|
||||||
|
mtu=iface.mtu,
|
||||||
|
node_id=iface.node_id,
|
||||||
|
net2_id=iface.net2_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_proto(self) -> core_pb2.Interface:
|
||||||
|
return core_pb2.Interface(
|
||||||
|
id=self.id,
|
||||||
|
name=self.name,
|
||||||
|
mac=self.mac,
|
||||||
|
ip4=self.ip4,
|
||||||
|
ip4_mask=self.ip4_mask,
|
||||||
|
ip6=self.ip6,
|
||||||
|
ip6_mask=self.ip6_mask,
|
||||||
|
net_id=self.net_id,
|
||||||
|
flow_id=self.flow_id,
|
||||||
|
mtu=self.mtu,
|
||||||
|
node_id=self.node_id,
|
||||||
|
net2_id=self.net2_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinkOptions:
|
||||||
|
jitter: int = 0
|
||||||
|
key: int = 0
|
||||||
|
mburst: int = 0
|
||||||
|
mer: int = 0
|
||||||
|
loss: float = 0.0
|
||||||
|
bandwidth: int = 0
|
||||||
|
burst: int = 0
|
||||||
|
delay: int = 0
|
||||||
|
dup: int = 0
|
||||||
|
unidirectional: bool = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, options: core_pb2.LinkOptions) -> "LinkOptions":
|
||||||
|
return LinkOptions(
|
||||||
|
jitter=options.jitter,
|
||||||
|
key=options.key,
|
||||||
|
mburst=options.mburst,
|
||||||
|
mer=options.mer,
|
||||||
|
loss=options.loss,
|
||||||
|
bandwidth=options.bandwidth,
|
||||||
|
burst=options.burst,
|
||||||
|
delay=options.delay,
|
||||||
|
dup=options.dup,
|
||||||
|
unidirectional=options.unidirectional,
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_proto(self) -> core_pb2.LinkOptions:
|
||||||
|
return core_pb2.LinkOptions(
|
||||||
|
jitter=self.jitter,
|
||||||
|
key=self.key,
|
||||||
|
mburst=self.mburst,
|
||||||
|
mer=self.mer,
|
||||||
|
loss=self.loss,
|
||||||
|
bandwidth=self.bandwidth,
|
||||||
|
burst=self.burst,
|
||||||
|
delay=self.delay,
|
||||||
|
dup=self.dup,
|
||||||
|
unidirectional=self.unidirectional,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Link:
|
||||||
|
node1_id: int
|
||||||
|
node2_id: int
|
||||||
|
type: LinkType = LinkType.WIRED
|
||||||
|
iface1: Interface = None
|
||||||
|
iface2: Interface = None
|
||||||
|
options: LinkOptions = None
|
||||||
|
network_id: int = None
|
||||||
|
label: str = None
|
||||||
|
color: str = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, link: core_pb2.Link) -> "Link":
|
||||||
|
iface1 = None
|
||||||
|
if link.HasField("iface1"):
|
||||||
|
iface1 = Interface.from_proto(link.iface1)
|
||||||
|
iface2 = None
|
||||||
|
if link.HasField("iface2"):
|
||||||
|
iface2 = Interface.from_proto(link.iface2)
|
||||||
|
options = None
|
||||||
|
if link.HasField("options"):
|
||||||
|
options = LinkOptions.from_proto(link.options)
|
||||||
|
return Link(
|
||||||
|
type=LinkType(link.type),
|
||||||
|
node1_id=link.node1_id,
|
||||||
|
node2_id=link.node2_id,
|
||||||
|
iface1=iface1,
|
||||||
|
iface2=iface2,
|
||||||
|
options=options,
|
||||||
|
network_id=link.network_id,
|
||||||
|
label=link.label,
|
||||||
|
color=link.color,
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_proto(self) -> core_pb2.Link:
|
||||||
|
iface1 = self.iface1.to_proto() if self.iface1 else None
|
||||||
|
iface2 = self.iface2.to_proto() if self.iface2 else None
|
||||||
|
options = self.options.to_proto() if self.options else None
|
||||||
|
return core_pb2.Link(
|
||||||
|
type=self.type.value,
|
||||||
|
node1_id=self.node1_id,
|
||||||
|
node2_id=self.node2_id,
|
||||||
|
iface1=iface1,
|
||||||
|
iface2=iface2,
|
||||||
|
options=options,
|
||||||
|
network_id=self.network_id,
|
||||||
|
label=self.label,
|
||||||
|
color=self.color,
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_symmetric(self) -> bool:
|
||||||
|
result = True
|
||||||
|
if self.options:
|
||||||
|
result = self.options.unidirectional is False
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SessionSummary:
|
||||||
|
id: int
|
||||||
|
state: SessionState
|
||||||
|
nodes: int
|
||||||
|
file: str
|
||||||
|
dir: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, summary: core_pb2.SessionSummary) -> "SessionSummary":
|
||||||
|
return SessionSummary(
|
||||||
|
id=summary.id,
|
||||||
|
state=SessionState(summary.state),
|
||||||
|
nodes=summary.nodes,
|
||||||
|
file=summary.file,
|
||||||
|
dir=summary.dir,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Hook:
|
class Hook:
|
||||||
state: SessionState
|
state: SessionState
|
||||||
|
@ -78,8 +378,8 @@ class Node:
|
||||||
type: NodeType
|
type: NodeType
|
||||||
model: str = None
|
model: str = None
|
||||||
position: Position = None
|
position: Position = None
|
||||||
services: List[str] = None
|
services: List[str] = field(default_factory=list)
|
||||||
config_services: List[str] = None
|
config_services: List[str] = field(default_factory=list)
|
||||||
emane: str = None
|
emane: str = None
|
||||||
icon: str = None
|
icon: str = None
|
||||||
image: str = None
|
image: str = None
|
||||||
|
@ -120,7 +420,32 @@ class Node:
|
||||||
icon=self.icon,
|
icon=self.icon,
|
||||||
image=self.image,
|
image=self.image,
|
||||||
server=self.server,
|
server=self.server,
|
||||||
geo=self.geo.to_proto(),
|
|
||||||
dir=self.dir,
|
dir=self.dir,
|
||||||
channel=self.channel,
|
channel=self.channel,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinkEvent:
|
||||||
|
message_type: MessageType
|
||||||
|
link: Link
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, event: core_pb2.LinkEvent) -> "LinkEvent":
|
||||||
|
return LinkEvent(
|
||||||
|
message_type=MessageType(event.message_type),
|
||||||
|
link=Link.from_proto(event.link),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NodeEvent:
|
||||||
|
message_type: MessageType
|
||||||
|
node: Node
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_proto(cls, event: core_pb2.NodeEvent) -> "NodeEvent":
|
||||||
|
return NodeEvent(
|
||||||
|
message_type=MessageType(event.message_type),
|
||||||
|
node=Node.from_proto(event.node),
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue