Merge branch 'coretk-create-node' into coretk
|
@ -14,3 +14,4 @@ pre-commit = "*"
|
|||
|
||||
[packages]
|
||||
coretk = {editable = true,path = "."}
|
||||
core = {editable = true,path = "./../daemon"}
|
||||
|
|
111
coretk/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "52de2a0b7a80abe39564a3943879fe75305b0cb8243c0fce78ef43689bee77c0"
|
||||
"sha256": "2cb6b23bfb8b9bebb5ece1016de468cc57fe46cf4df08a61b1861aee4bed1028"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -14,10 +14,91 @@
|
|||
]
|
||||
},
|
||||
"default": {
|
||||
"configparser": {
|
||||
"hashes": [
|
||||
"sha256:254c1d9c79f60c45dfde850850883d5aaa7f19a23f13561243a050d5a7c3fe4c",
|
||||
"sha256:c7d282687a5308319bf3d2e7706e575c635b0a470342641c93bea0ea3b5331df"
|
||||
],
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"core": {
|
||||
"editable": true,
|
||||
"path": "./../daemon"
|
||||
},
|
||||
"coretk": {
|
||||
"editable": true,
|
||||
"path": "."
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"
|
||||
],
|
||||
"version": "==0.17.1"
|
||||
},
|
||||
"grpcio": {
|
||||
"hashes": [
|
||||
"sha256:0337debec20fe385bcd49048d6917270efbc17a5119857466559b4db91f8995b",
|
||||
"sha256:164f82a99e08797ea786283b66b45ebe76772d321577d1674ba6fe0200155892",
|
||||
"sha256:172dfba8d9621048c2cbc1d1cf7a02244e9a9a8cff5bb79bb30bcb0c13c7fd31",
|
||||
"sha256:18f4b536d8a9cfa15b3214e0bb628071def94160699e91798f0a954c3b2db88d",
|
||||
"sha256:2283b56bda49b068b0f08d006fffc7dd46eae72322f1a5dec87fc9c218f1dc2d",
|
||||
"sha256:26b33f488a955bf49262d2ce3423d3a8174108506d8f819e8150aca21bdd3b99",
|
||||
"sha256:31cc9b6f70bdd0d9ff53df2d563ea1fb278601d5c625932d8a82d03b08ff3de0",
|
||||
"sha256:37dd8684fbc2bc00766ae6784bcbd7f874bc96527636a341411db811d04ff650",
|
||||
"sha256:424c01189ef51a808669f020368b01204e0f1fa0bf2adab7c8d0d13166f92e9e",
|
||||
"sha256:431c099f20a1f1d97def98f87bb74fa752e8819c2bab23d79089353aed1acc9b",
|
||||
"sha256:4c2f1d0b27bcef301e5d5c1da05ffd7d174f807f61889c006b8e708b16bc978e",
|
||||
"sha256:59b8d738867b59c5daaff5df242b5f3f9c58b47862f603c6ee530964b897b69b",
|
||||
"sha256:8d4f1ee2a67cf8f792d4fc9b8c7bb2148174e838d935f175653aec234752828b",
|
||||
"sha256:97ab9e35b47bda0441332204960f95c1169c55ec8e989381bedd32bdb9f78b05",
|
||||
"sha256:9cf93e185507bfdaa7ed45a90049bd3f1ed3f6357ad3772b31e993ff723cf67d",
|
||||
"sha256:a5a81472c0ca6181492b9291c316ff60c6c94dd3f21c1e8c481f21923d899af0",
|
||||
"sha256:aaa1feb0fdd094af6db0a16cbd446ed94285a50e320aede5971152d9ea022df8",
|
||||
"sha256:b36bf4408f8400ee9ab13ff129e71f2e4c72ce2d8886b744aeab77ce50a55cf6",
|
||||
"sha256:bb345f7e98b38a2c1ef33ff1145687234f78dfeedf308b41b3e41f4b42eba099",
|
||||
"sha256:c13ae15695e0eb4ba2db920d6a197171d2398c675afa4f27460b6381d20a6884",
|
||||
"sha256:c4a233a00cc5b64543e97733902151bc6738396931b3c166aad03a3aaadbd479",
|
||||
"sha256:c521c5f8a95baabba69c68dd0f5e34f37c8adf1a9691f9884dba3eab4ebadc29",
|
||||
"sha256:c772edd094fe3e54d6d54fdecb90c51cb8d07b55e9c1cda2d33e9615e33d07e8",
|
||||
"sha256:cebfba6542855403b29e4bc95bbcd5ab444f21137b440f2fb7c7925ca0e55bfd",
|
||||
"sha256:d7490e013c4bad3e8db804fc6483b47125dc8df0ebcfd6e419bd25df35025301",
|
||||
"sha256:dfb6063619f297cbd22c67530d7465d98348b35d0424bbc1756b36c5ef9f99d4",
|
||||
"sha256:e80a15b48a66f35c7c33db2a7df4034a533b362269d0e60e0036e23f14bac7b5",
|
||||
"sha256:ea444fa1c1ec4f8d2ce965bb01e06148ef9ceb398fb2f627511d50f137eac35b",
|
||||
"sha256:ec986cbf8837a49f9612cc1cfc2a8ccb54875cfce5355a121279de35124ea1db",
|
||||
"sha256:fb641df6de8c4a55c784c24d334d53096954d9b30679d3ce5eb6a4d25c1020a3",
|
||||
"sha256:fb88bd791c8efbcb36de12f0aa519ceec0b7806d3decff16e412e097d4725d44",
|
||||
"sha256:ffa1be3d566a9cbd21a5f2d95fd9262ec6c337c499291bfeb51547b8de18942e"
|
||||
],
|
||||
"version": "==1.24.0"
|
||||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4",
|
||||
"sha256:096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc",
|
||||
"sha256:0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1",
|
||||
"sha256:17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046",
|
||||
"sha256:17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36",
|
||||
"sha256:1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5",
|
||||
"sha256:2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d",
|
||||
"sha256:4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916",
|
||||
"sha256:6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0",
|
||||
"sha256:726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27",
|
||||
"sha256:760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc",
|
||||
"sha256:796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7",
|
||||
"sha256:891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38",
|
||||
"sha256:a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5",
|
||||
"sha256:a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832",
|
||||
"sha256:b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a",
|
||||
"sha256:be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f",
|
||||
"sha256:c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9",
|
||||
"sha256:c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692",
|
||||
"sha256:d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84",
|
||||
"sha256:e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79",
|
||||
"sha256:fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"
|
||||
],
|
||||
"version": "==4.4.1"
|
||||
},
|
||||
"pillow": {
|
||||
"hashes": [
|
||||
"sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de",
|
||||
|
@ -48,6 +129,34 @@
|
|||
"sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b"
|
||||
],
|
||||
"version": "==6.1.0"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:26c0d756c7ad6823fccbc3b5f84c619b9cc7ac281496fe0a9d78e32023c45034",
|
||||
"sha256:3200046e4d4f6c42ed66257dbe15e2e5dc76072c280e9b3d69dc8f3a4fa3fbbc",
|
||||
"sha256:368f1bae6dd22d04fd2254d30cd301863408a96ff604422e3ddd8ab601f095a4",
|
||||
"sha256:3902fa1920b4ef9f710797496b309efc5ccd0faeba44dc82ed6a711a244764a0",
|
||||
"sha256:3a7a8925ba6481b9241cdb5d69cd0b0700f23efed6bb691dc9543faa4aa25d6f",
|
||||
"sha256:4bc33d49f43c6e9916fb56b7377cb4478cbf25824b4d2bedfb8a4e3df31c12ca",
|
||||
"sha256:568b434a36e31ed30d60d600b2227666ce150b8b5275948f50411481a4575d6d",
|
||||
"sha256:5c393cd665d03ce6b29561edd6b0cc4bcb3fb8e2a7843e8f223d693f07f61b40",
|
||||
"sha256:80072e9ba36c73cf89c01f669c7b123733fc2de1780b428082a850f53cc7865f",
|
||||
"sha256:843f498e98ad1469ad54ecb4a7ccf48605a1c5d2bd26ae799c7a2cddab4a37ec",
|
||||
"sha256:aa45443035651cbfae74c8deb53358ba660d8e7a5fbab3fc4beb33fb3e3ca4be",
|
||||
"sha256:aaab817d9d038dd5f56a6fb2b2e8ae68caf1fd28cc6a963c755fa73268495c13",
|
||||
"sha256:e6f68b9979dc8f75299293d682f67fecb72d78f98652da2eeb85c85edef1ca94",
|
||||
"sha256:e7366cabddff3441d583fdc0176ab42eba4ee7090ef857d50c4dd59ad124003a",
|
||||
"sha256:f0144ad97cd28bfdda0567b9278d25061ada5ad2b545b538cd3577697b32bda3",
|
||||
"sha256:f655338491481f482042f19016647e50365ab41b75b486e0df56e0dcc425abf4"
|
||||
],
|
||||
"version": "==3.9.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
|
|
|
@ -1,50 +1,72 @@
|
|||
import logging
|
||||
import tkinter as tk
|
||||
|
||||
import coretk.appcache as appcache
|
||||
import coretk.images as images
|
||||
from coretk.coregrpc import CoreGrpc
|
||||
from coretk.coremenubar import CoreMenubar
|
||||
from coretk.coretoolbar import CoreToolbar
|
||||
from coretk.graph import CanvasGraph
|
||||
from coretk.images import Images
|
||||
from coretk.images import ImageEnum, Images
|
||||
from coretk.menuaction import MenuAction
|
||||
|
||||
|
||||
class Application(tk.Frame):
|
||||
def __init__(self, master=None):
|
||||
super().__init__(master)
|
||||
appcache.cache_variable(self)
|
||||
print(self.is_open_xml)
|
||||
self.load_images()
|
||||
self.setup_app()
|
||||
self.menubar = None
|
||||
self.core_menu = None
|
||||
self.canvas = None
|
||||
self.core_editbar = None
|
||||
self.core_grpc = None
|
||||
|
||||
self.create_menu()
|
||||
self.create_widgets()
|
||||
self.draw_canvas()
|
||||
self.start_grpc()
|
||||
# self.try_make_table()
|
||||
|
||||
def load_images(self):
|
||||
Images.load("core", "core-icon.png")
|
||||
"""
|
||||
Load core images
|
||||
:return:
|
||||
"""
|
||||
images.load_core_images(Images)
|
||||
|
||||
def setup_app(self):
|
||||
self.master.title("CORE")
|
||||
self.master.geometry("800x600")
|
||||
image = Images.get("core")
|
||||
self.master.geometry("1000x800")
|
||||
image = Images.get(ImageEnum.CORE.value)
|
||||
self.master.tk.call("wm", "iconphoto", self.master._w, image)
|
||||
self.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
def create_menu(self):
|
||||
self.master.option_add("*tearOff", tk.FALSE)
|
||||
self.menubar = tk.Menu(self.master)
|
||||
core_menu = CoreMenubar(self, self.master, self.menubar)
|
||||
core_menu.create_core_menubar()
|
||||
self.core_menu = CoreMenubar(self, self.master, self.menubar)
|
||||
self.core_menu.create_core_menubar()
|
||||
self.master.config(menu=self.menubar)
|
||||
|
||||
def create_widgets(self):
|
||||
edit_frame = tk.Frame(self)
|
||||
edit_frame.pack(side=tk.LEFT, fill=tk.Y, ipadx=2, ipady=2)
|
||||
core_editbar = CoreToolbar(self.master, edit_frame, self.menubar)
|
||||
core_editbar.create_toolbar()
|
||||
self.core_editbar = CoreToolbar(self, edit_frame, self.menubar)
|
||||
self.core_editbar.create_toolbar()
|
||||
|
||||
def draw_canvas(self):
|
||||
self.canvas = CanvasGraph(
|
||||
self, background="#cccccc", scrollregion=(0, 0, 1000, 1000)
|
||||
master=self,
|
||||
grpc=self.core_grpc,
|
||||
background="#cccccc",
|
||||
scrollregion=(0, 0, 1200, 1000),
|
||||
)
|
||||
self.canvas.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# self.canvas.create_line(0, 0, 10, 10)
|
||||
self.core_editbar.canvas = self.canvas
|
||||
|
||||
scroll_x = tk.Scrollbar(
|
||||
self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview
|
||||
|
@ -64,8 +86,35 @@ class Application(tk.Frame):
|
|||
b = tk.Button(status_bar, text="Button 3")
|
||||
b.pack(side=tk.LEFT, padx=1)
|
||||
|
||||
def start_grpc(self):
|
||||
"""
|
||||
Conect client to grpc, query sessions and prompt use to choose an existing session if there exist any
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.master.update()
|
||||
self.core_grpc = CoreGrpc(self)
|
||||
self.core_grpc.set_up()
|
||||
self.canvas.core_grpc = self.core_grpc
|
||||
self.canvas.grpc_manager.core_grpc = self.core_grpc
|
||||
self.canvas.grpc_manager.update_preexisting_ids()
|
||||
self.canvas.draw_existing_component()
|
||||
|
||||
def on_closing(self):
|
||||
menu_action = MenuAction(self, self.master)
|
||||
menu_action.on_quit()
|
||||
# self.quit()
|
||||
|
||||
def try_make_table(self):
|
||||
f = tk.Frame(self.master)
|
||||
for i in range(3):
|
||||
e = tk.Entry(f)
|
||||
e.grid(row=0, column=1, stick="nsew")
|
||||
f.pack(side=tk.TOP)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
app = Application()
|
||||
app.master.protocol("WM_DELETE_WINDOW", app.on_closing)
|
||||
app.mainloop()
|
||||
|
|
33
coretk/coretk/appcache.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""
|
||||
stores some information helpful for setting starting values for some tables
|
||||
like size and scale, set wallpaper, etc
|
||||
"""
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
def cache_variable(application):
|
||||
# for menubar
|
||||
application.is_open_xml = False
|
||||
|
||||
application.size_and_scale = None
|
||||
application.set_wallpaper = None
|
||||
|
||||
# set wallpaper variables
|
||||
|
||||
# canvas id of the wallpaper
|
||||
application.wallpaper_id = None
|
||||
|
||||
# current image for wallpaper
|
||||
application.current_wallpaper = None
|
||||
|
||||
# wallpaper option
|
||||
application.radiovar = tk.IntVar()
|
||||
application.radiovar.set(1)
|
||||
|
||||
# show grid option
|
||||
application.show_grid_var = tk.IntVar()
|
||||
application.show_grid_var.set(1)
|
||||
|
||||
# adjust canvas to image dimension variable
|
||||
application.adjust_to_dim_var = tk.IntVar()
|
||||
application.adjust_to_dim_var.set(0)
|
291
coretk/coretk/coregrpc.py
Normal file
|
@ -0,0 +1,291 @@
|
|||
"""
|
||||
Incorporate grpc into python tkinter GUI
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
|
||||
from core.api.grpc import client, core_pb2
|
||||
from coretk.linkinfo import Throughput
|
||||
from coretk.querysessiondrawing import SessionTable
|
||||
from coretk.wirelessconnection import WirelessConnection
|
||||
|
||||
|
||||
class CoreGrpc:
|
||||
def __init__(self, app, sid=None):
|
||||
"""
|
||||
Create a CoreGrpc instance
|
||||
"""
|
||||
self.core = client.CoreGrpcClient()
|
||||
self.session_id = sid
|
||||
|
||||
self.node_ids = []
|
||||
|
||||
self.master = app.master
|
||||
|
||||
# self.set_up()
|
||||
self.interface_helper = None
|
||||
self.throughput_draw = Throughput(app.canvas, self)
|
||||
self.wireless_draw = WirelessConnection(app.canvas, self)
|
||||
|
||||
def log_event(self, event):
|
||||
logging.info("event: %s", event)
|
||||
if event.link_event is not None:
|
||||
self.wireless_draw.hangle_link_event(event.link_event)
|
||||
|
||||
def log_throughput(self, event):
|
||||
interface_throughputs = event.interface_throughputs
|
||||
for i in interface_throughputs:
|
||||
print("")
|
||||
return
|
||||
throughputs_belong_to_session = []
|
||||
for if_tp in interface_throughputs:
|
||||
if if_tp.node_id in self.node_ids:
|
||||
throughputs_belong_to_session.append(if_tp)
|
||||
# bridge_throughputs = event.bridge_throughputs
|
||||
self.throughput_draw.process_grpc_throughput_event(
|
||||
throughputs_belong_to_session
|
||||
)
|
||||
|
||||
def create_new_session(self):
|
||||
"""
|
||||
Create a new session
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
response = self.core.create_session()
|
||||
logging.info("created session: %s", response)
|
||||
|
||||
# handle events session may broadcast
|
||||
self.session_id = response.session_id
|
||||
self.master.title("CORE Session ID " + str(self.session_id))
|
||||
self.core.events(self.session_id, self.log_event)
|
||||
# self.core.throughputs(self.log_throughput)
|
||||
|
||||
def query_existing_sessions(self, sessions):
|
||||
"""
|
||||
Query for existing sessions and prompt to join one
|
||||
|
||||
:param repeated core_pb2.SessionSummary sessions: summaries of all the existing sessions
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
SessionTable(self, self.master)
|
||||
|
||||
def delete_session(self, custom_sid=None):
|
||||
if custom_sid is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = custom_sid
|
||||
response = self.core.delete_session(sid)
|
||||
logging.info("Deleted session result: %s", response)
|
||||
|
||||
def terminate_session(self, custom_sid=None):
|
||||
if custom_sid is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = custom_sid
|
||||
s = self.core.get_session(sid).session
|
||||
# delete links and nodes from running session
|
||||
if s.state == core_pb2.SessionState.RUNTIME:
|
||||
self.set_session_state("datacollect", sid)
|
||||
self.delete_links(sid)
|
||||
self.delete_nodes(sid)
|
||||
self.delete_session(sid)
|
||||
|
||||
def set_up(self):
|
||||
"""
|
||||
Query sessions, if there exist any, prompt whether to join one
|
||||
|
||||
:return: existing sessions
|
||||
"""
|
||||
self.core.connect()
|
||||
|
||||
response = self.core.get_sessions()
|
||||
# logging.info("coregrpc.py: all sessions: %s", response)
|
||||
|
||||
# if there are no sessions, create a new session, else join a session
|
||||
sessions = response.sessions
|
||||
|
||||
if len(sessions) == 0:
|
||||
self.create_new_session()
|
||||
else:
|
||||
|
||||
self.query_existing_sessions(sessions)
|
||||
|
||||
def get_session_state(self):
|
||||
response = self.core.get_session(self.session_id)
|
||||
# logging.info("get session: %s", response)
|
||||
return response.session.state
|
||||
|
||||
def set_session_state(self, state, custom_session_id=None):
|
||||
"""
|
||||
Set session state
|
||||
|
||||
:param str state: session state to set
|
||||
:return: nothing
|
||||
"""
|
||||
if custom_session_id is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = custom_session_id
|
||||
|
||||
if state == "configuration":
|
||||
response = self.core.set_session_state(
|
||||
sid, core_pb2.SessionState.CONFIGURATION
|
||||
)
|
||||
elif state == "instantiation":
|
||||
response = self.core.set_session_state(
|
||||
sid, core_pb2.SessionState.INSTANTIATION
|
||||
)
|
||||
elif state == "datacollect":
|
||||
response = self.core.set_session_state(
|
||||
sid, core_pb2.SessionState.DATACOLLECT
|
||||
)
|
||||
elif state == "shutdown":
|
||||
response = self.core.set_session_state(sid, core_pb2.SessionState.SHUTDOWN)
|
||||
elif state == "runtime":
|
||||
response = self.core.set_session_state(sid, core_pb2.SessionState.RUNTIME)
|
||||
elif state == "definition":
|
||||
response = self.core.set_session_state(
|
||||
sid, core_pb2.SessionState.DEFINITION
|
||||
)
|
||||
elif state == "none":
|
||||
response = self.core.set_session_state(sid, core_pb2.SessionState.NONE)
|
||||
else:
|
||||
logging.error("coregrpc.py: set_session_state: INVALID STATE")
|
||||
|
||||
logging.info("set session state: %s", response)
|
||||
|
||||
def add_node(self, node_type, model, x, y, name, node_id):
|
||||
position = core_pb2.Position(x=x, y=y)
|
||||
node = core_pb2.Node(id=node_id, type=node_type, position=position, model=model)
|
||||
self.node_ids.append(node_id)
|
||||
response = self.core.add_node(self.session_id, node)
|
||||
logging.info("created node: %s", response)
|
||||
if node_type == core_pb2.NodeType.WIRELESS_LAN:
|
||||
d = OrderedDict()
|
||||
d["basic_range"] = "275"
|
||||
d["bandwidth"] = "54000000"
|
||||
d["jitter"] = "0"
|
||||
d["delay"] = "20000"
|
||||
d["error"] = "0"
|
||||
r = self.core.set_wlan_config(self.session_id, node_id, d)
|
||||
logging.debug("set wlan config %s", r)
|
||||
return response.node_id
|
||||
|
||||
def edit_node(self, node_id, x, y):
|
||||
position = core_pb2.Position(x=x, y=y)
|
||||
response = self.core.edit_node(self.session_id, node_id, position)
|
||||
logging.info("updated node id %s: %s", node_id, response)
|
||||
# self.core.events(self.session_id, self.log_event)
|
||||
|
||||
def delete_nodes(self, delete_session=None):
|
||||
if delete_session is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = delete_session
|
||||
for node in self.core.get_session(sid).session.nodes:
|
||||
response = self.core.delete_node(self.session_id, node.id)
|
||||
logging.info("delete nodes %s", response)
|
||||
|
||||
def delete_links(self, delete_session=None):
|
||||
# sid = None
|
||||
if delete_session is None:
|
||||
sid = self.session_id
|
||||
else:
|
||||
sid = delete_session
|
||||
|
||||
for link in self.core.get_session(sid).session.links:
|
||||
response = self.core.delete_link(
|
||||
self.session_id,
|
||||
link.node_one_id,
|
||||
link.node_two_id,
|
||||
link.interface_one.id,
|
||||
link.interface_two.id,
|
||||
)
|
||||
logging.info("delete links %s", response)
|
||||
|
||||
def add_link(self, id1, id2, type1, type2, edge):
|
||||
"""
|
||||
Grpc client request add link
|
||||
|
||||
:param int session_id: session id
|
||||
:param int id1: node 1 core id
|
||||
:param core_pb2.NodeType type1: node 1 core node type
|
||||
:param int id2: node 2 core id
|
||||
:param core_pb2.NodeType type2: node 2 core node type
|
||||
:return: nothing
|
||||
"""
|
||||
if1 = None
|
||||
if2 = None
|
||||
if type1 == core_pb2.NodeType.DEFAULT:
|
||||
interface = edge.interface_1
|
||||
if1 = core_pb2.Interface(
|
||||
id=interface.id,
|
||||
name=interface.name,
|
||||
mac=interface.mac,
|
||||
ip4=interface.ipv4,
|
||||
ip4mask=interface.ip4prefix,
|
||||
)
|
||||
logging.debug("create interface 1 %s", if1)
|
||||
# interface1 = self.interface_helper.create_interface(id1, 0)
|
||||
|
||||
if type2 == core_pb2.NodeType.DEFAULT:
|
||||
interface = edge.interface_2
|
||||
if2 = core_pb2.Interface(
|
||||
id=interface.id,
|
||||
name=interface.name,
|
||||
mac=interface.mac,
|
||||
ip4=interface.ipv4,
|
||||
ip4mask=interface.ip4prefix,
|
||||
)
|
||||
logging.debug("create interface 2: %s", if2)
|
||||
|
||||
response = self.core.add_link(self.session_id, id1, id2, if1, if2)
|
||||
logging.info("created link: %s", response)
|
||||
|
||||
# self.core.get_node_links(self.session_id, id1)
|
||||
|
||||
# def get_session(self):
|
||||
# response = self.core.get_session(self.session_id)
|
||||
# nodes = response.session.nodes
|
||||
# for node in nodes:
|
||||
# r = self.core.get_node_links(self.session_id, node.id)
|
||||
# logging.info(r)
|
||||
|
||||
def launch_terminal(self, node_id):
|
||||
response = self.core.get_node_terminal(self.session_id, node_id)
|
||||
logging.info("get terminal %s", response.terminal)
|
||||
os.system("xterm -e %s &" % response.terminal)
|
||||
|
||||
def save_xml(self, file_path):
|
||||
"""
|
||||
Save core session as to an xml file
|
||||
|
||||
:param str file_path: file path that user pick
|
||||
:return: nothing
|
||||
"""
|
||||
response = self.core.save_xml(self.session_id, file_path)
|
||||
logging.info("coregrpc.py save xml %s", response)
|
||||
self.core.events(self.session_id, self.log_event)
|
||||
|
||||
def open_xml(self, file_path):
|
||||
"""
|
||||
Open core xml
|
||||
|
||||
:param str file_path: file to open
|
||||
:return: session id
|
||||
"""
|
||||
response = self.core.open_xml(file_path)
|
||||
self.session_id = response.session_id
|
||||
logging.debug("coreprgc.py open_xml(): %s", response.result)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Clean ups when done using grpc
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("Close grpc")
|
||||
self.core.close()
|
|
@ -1,6 +1,7 @@
|
|||
import tkinter as tk
|
||||
|
||||
import coretk.menuaction as action
|
||||
from coretk.menuaction import MenuAction
|
||||
|
||||
|
||||
class CoreMenubar(object):
|
||||
|
@ -19,6 +20,33 @@ class CoreMenubar(object):
|
|||
self.menubar = menubar
|
||||
self.master = master
|
||||
self.application = application
|
||||
self.menuaction = action.MenuAction(application, master)
|
||||
self.menu_action = MenuAction(self.application, self.master)
|
||||
|
||||
# def on_quit(self):
|
||||
# """
|
||||
# Prompt use to stop running session before application is closed
|
||||
#
|
||||
# :return: nothing
|
||||
# """
|
||||
# state = self.application.core_grpc.get_session_state()
|
||||
#
|
||||
# if state == core_pb2.SessionState.SHUTDOWN or state == core_pb2.SessionState.DEFINITION:
|
||||
# self.application.core_grpc.delete_session()
|
||||
# self.application.core_grpc.core.close()
|
||||
# # self.application.quit()
|
||||
# else:
|
||||
# msgbox = tk.messagebox.askyesnocancel("stop", "Stop the running session?")
|
||||
#
|
||||
# if msgbox or msgbox == False:
|
||||
# if msgbox:
|
||||
# self.application.core_grpc.set_session_state("datacollect")
|
||||
# self.application.core_grpc.delete_links()
|
||||
# self.application.core_grpc.delete_nodes()
|
||||
# self.application.core_grpc.delete_session()
|
||||
#
|
||||
# self.application.core_grpc.core.close()
|
||||
# # self.application.quit()
|
||||
|
||||
def create_file_menu(self):
|
||||
"""
|
||||
|
@ -27,18 +55,22 @@ class CoreMenubar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
file_menu = tk.Menu(self.menubar)
|
||||
# menu_action = MenuAction(self.application, self.master)
|
||||
file_menu.add_command(
|
||||
label="New", command=action.file_new, accelerator="Ctrl+N", underline=0
|
||||
)
|
||||
file_menu.add_command(
|
||||
label="Open...", command=action.file_open, accelerator="Ctrl+O", underline=0
|
||||
label="Open...",
|
||||
command=self.menu_action.file_open_xml,
|
||||
accelerator="Ctrl+O",
|
||||
underline=0,
|
||||
)
|
||||
file_menu.add_command(label="Reload", command=action.file_reload, underline=0)
|
||||
file_menu.add_command(
|
||||
label="Save", command=action.file_save, accelerator="Ctrl+S", underline=0
|
||||
)
|
||||
file_menu.add_command(label="Save As XML...", command=action.file_save_as_xml)
|
||||
file_menu.add_command(label="Save As imn...", command=action.file_save_as_imn)
|
||||
# file_menu.add_command(
|
||||
# label="Save", command=action.file_save, accelerator="Ctrl+S", underline=0
|
||||
# )
|
||||
# file_menu.add_command(label="Save As XML...", command=action.file_save_as_xml)
|
||||
file_menu.add_command(label="Save", command=self.menu_action.file_save_as_xml)
|
||||
|
||||
file_menu.add_separator()
|
||||
|
||||
|
@ -68,13 +100,8 @@ class CoreMenubar(object):
|
|||
file_menu.add_separator()
|
||||
|
||||
file_menu.add_command(
|
||||
label="/home/ncs/.core/configs/sample1.imn",
|
||||
command=action.file_example_link,
|
||||
label="Quit", command=self.menuaction.on_quit, underline=0
|
||||
)
|
||||
|
||||
file_menu.add_separator()
|
||||
|
||||
file_menu.add_command(label="Quit", command=self.master.quit, underline=0)
|
||||
self.menubar.add_cascade(label="File", menu=file_menu, underline=0)
|
||||
|
||||
def create_edit_menu(self):
|
||||
|
@ -137,8 +164,12 @@ class CoreMenubar(object):
|
|||
|
||||
canvas_menu.add_separator()
|
||||
|
||||
canvas_menu.add_command(label="Size/scale...", command=action.canvas_size_scale)
|
||||
canvas_menu.add_command(label="Wallpaper...", command=action.canvas_wallpaper)
|
||||
canvas_menu.add_command(
|
||||
label="Size/scale...", command=self.menu_action.canvas_size_and_scale
|
||||
)
|
||||
canvas_menu.add_command(
|
||||
label="Wallpaper...", command=self.menu_action.canvas_set_wallpaper
|
||||
)
|
||||
|
||||
canvas_menu.add_separator()
|
||||
|
||||
|
@ -560,9 +591,7 @@ class CoreMenubar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
session_menu = tk.Menu(self.menubar)
|
||||
session_menu.add_command(
|
||||
label="Start", command=action.session_start, underline=0
|
||||
)
|
||||
|
||||
session_menu.add_command(
|
||||
label="Change sessions...",
|
||||
command=action.session_change_sessions,
|
||||
|
@ -604,10 +633,11 @@ class CoreMenubar(object):
|
|||
"""
|
||||
help_menu = tk.Menu(self.menubar)
|
||||
help_menu.add_command(
|
||||
label="Core Github (www)", command=action.help_core_github
|
||||
label="Core Github (www)", command=self.menu_action.help_core_github
|
||||
)
|
||||
help_menu.add_command(
|
||||
label="Core Documentation (www)", command=action.help_core_documentation
|
||||
label="Core Documentation (www)",
|
||||
command=self.menu_action.help_core_documentation,
|
||||
)
|
||||
help_menu.add_command(label="About", command=action.help_about)
|
||||
|
||||
|
|
45
coretk/coretk/coretocanvas.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
"""
|
||||
provide mapping from core to canvas
|
||||
"""
|
||||
import logging
|
||||
|
||||
|
||||
class CoreToCanvasMapping:
|
||||
def __init__(self):
|
||||
self.core_id_to_canvas_id = {}
|
||||
self.core_node_and_interface_to_canvas_edge = {}
|
||||
# self.edge_id_to_canvas_token = {}
|
||||
|
||||
def map_node_and_interface_to_canvas_edge(self, nid, iid, edge_token):
|
||||
self.core_node_and_interface_to_canvas_edge[tuple([nid, iid])] = edge_token
|
||||
|
||||
def get_token_from_node_and_interface(self, nid, iid):
|
||||
key = tuple([nid, iid])
|
||||
if key in self.core_node_and_interface_to_canvas_edge:
|
||||
return self.core_node_and_interface_to_canvas_edge[key]
|
||||
else:
|
||||
logging.error("invalid key")
|
||||
return None
|
||||
|
||||
def map_core_id_to_canvas_id(self, core_nid, canvas_nid):
|
||||
if core_nid not in self.core_id_to_canvas_id:
|
||||
self.core_id_to_canvas_id[core_nid] = canvas_nid
|
||||
else:
|
||||
logging.debug("key already existed")
|
||||
|
||||
def get_canvas_id_from_core_id(self, core_id):
|
||||
if core_id in self.core_id_to_canvas_id:
|
||||
return self.core_id_to_canvas_id[core_id]
|
||||
else:
|
||||
logging.debug("invalid key")
|
||||
return None
|
||||
|
||||
# def add_mapping(self, core_id, canvas_id):
|
||||
# if core_id not in self.core_id_to_canvas_id:
|
||||
# self.core_id_to_canvas_id[core_id] = canvas_id
|
||||
# else:
|
||||
# logging.error("key already mapped")
|
||||
#
|
||||
# def delete_mapping(self, core_id):
|
||||
# result = self.core_id_to_canvas_id.pop(core_id, None)
|
||||
# return result
|
|
@ -1,22 +1,37 @@
|
|||
import logging
|
||||
import tkinter as tk
|
||||
from enum import Enum
|
||||
|
||||
from coretk.images import Images
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.coretoolbarhelp import CoreToolbarHelp
|
||||
from coretk.graph import GraphMode
|
||||
from coretk.images import ImageEnum, Images
|
||||
from coretk.tooltip import CreateToolTip
|
||||
|
||||
|
||||
class SessionStateEnum(Enum):
|
||||
NONE = "none"
|
||||
DEFINITION = "definition"
|
||||
CONFIGURATION = "configuration"
|
||||
RUNTIME = "runtime"
|
||||
DATACOLLECT = "datacollect"
|
||||
SHUTDOWN = "shutdown"
|
||||
INSTANTIATION = "instantiation"
|
||||
|
||||
|
||||
class CoreToolbar(object):
|
||||
"""
|
||||
Core toolbar class
|
||||
"""
|
||||
|
||||
def __init__(self, master, edit_frame, menubar):
|
||||
def __init__(self, application, edit_frame, menubar):
|
||||
"""
|
||||
Create a CoreToolbar instance
|
||||
|
||||
:param tkinter.Frame edit_frame: edit frame
|
||||
"""
|
||||
self.master = master
|
||||
self.application = application
|
||||
self.master = application.master
|
||||
self.edit_frame = edit_frame
|
||||
self.menubar = menubar
|
||||
self.radio_value = tk.IntVar()
|
||||
|
@ -26,7 +41,6 @@ class CoreToolbar(object):
|
|||
self.width = 32
|
||||
self.height = 32
|
||||
|
||||
# Used for drawing the horizontally displayed menu items for network-layer nodes and link-layer node
|
||||
self.selection_tool_button = None
|
||||
|
||||
# Reference to the option menus
|
||||
|
@ -34,37 +48,7 @@ class CoreToolbar(object):
|
|||
self.marker_option_menu = None
|
||||
self.network_layer_option_menu = None
|
||||
|
||||
def load_toolbar_images(self):
|
||||
"""
|
||||
Load the images that appear in core toolbar
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
Images.load("core", "core-icon.png")
|
||||
Images.load("start", "start.gif")
|
||||
Images.load("switch", "lanswitch.gif")
|
||||
Images.load("marker", "marker.gif")
|
||||
Images.load("router", "router.gif")
|
||||
Images.load("select", "select.gif")
|
||||
Images.load("link", "link.gif")
|
||||
Images.load("hub", "hub.gif")
|
||||
Images.load("wlan", "wlan.gif")
|
||||
Images.load("rj45", "rj45.gif")
|
||||
Images.load("tunnel", "tunnel.gif")
|
||||
Images.load("oval", "oval.gif")
|
||||
Images.load("rectangle", "rectangle.gif")
|
||||
Images.load("text", "text.gif")
|
||||
Images.load("host", "host.gif")
|
||||
Images.load("pc", "pc.gif")
|
||||
Images.load("mdr", "mdr.gif")
|
||||
Images.load("prouter", "router_green.gif")
|
||||
Images.load("ovs", "OVS.gif")
|
||||
Images.load("editnode", "document-properties.gif")
|
||||
Images.load("run", "run.gif")
|
||||
Images.load("plot", "plot.gif")
|
||||
Images.load("twonode", "twonode.gif")
|
||||
Images.load("stop", "stop.gif")
|
||||
Images.load("observe", "observe.gif")
|
||||
self.canvas = None
|
||||
|
||||
def destroy_previous_frame(self):
|
||||
"""
|
||||
|
@ -82,7 +66,7 @@ class CoreToolbar(object):
|
|||
if self.marker_option_menu and self.marker_option_menu.winfo_exists():
|
||||
self.marker_option_menu.destroy()
|
||||
|
||||
def destroy_children_widgets(self, parent):
|
||||
def destroy_children_widgets(self):
|
||||
"""
|
||||
Destroy all children of a parent widget
|
||||
|
||||
|
@ -90,11 +74,11 @@ class CoreToolbar(object):
|
|||
:return: nothing
|
||||
"""
|
||||
|
||||
for i in parent.winfo_children():
|
||||
for i in self.edit_frame.winfo_children():
|
||||
if i.winfo_name() != "!frame":
|
||||
i.destroy()
|
||||
|
||||
def create_button(self, img, func, frame, main_button):
|
||||
def create_button(self, img, func, frame, main_button, btt_message):
|
||||
"""
|
||||
Create button and put it on the frame
|
||||
|
||||
|
@ -106,6 +90,7 @@ class CoreToolbar(object):
|
|||
"""
|
||||
button = tk.Button(frame, width=self.width, height=self.height, image=img)
|
||||
button.pack(side=tk.LEFT, pady=1)
|
||||
CreateToolTip(button, btt_message)
|
||||
button.bind("<Button-1>", lambda mb: func(main_button))
|
||||
|
||||
def create_radio_button(self, frame, image, func, variable, value, tooltip_msg):
|
||||
|
@ -122,11 +107,12 @@ class CoreToolbar(object):
|
|||
button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(button, tooltip_msg)
|
||||
|
||||
def create_regular_button(self, frame, image, func):
|
||||
def create_regular_button(self, frame, image, func, btt_message):
|
||||
button = tk.Button(
|
||||
frame, width=self.width, height=self.height, image=image, command=func
|
||||
)
|
||||
button.pack(side=tk.TOP, pady=1)
|
||||
CreateToolTip(button, btt_message)
|
||||
|
||||
def draw_button_menu_frame(self, edit_frame, option_frame, main_button):
|
||||
"""
|
||||
|
@ -165,48 +151,104 @@ class CoreToolbar(object):
|
|||
|
||||
def click_selection_tool(self):
|
||||
logging.debug("Click SELECTION TOOL")
|
||||
self.canvas.mode = GraphMode.SELECT
|
||||
|
||||
def click_start_stop_session_tool(self):
|
||||
def click_start_session_tool(self):
|
||||
"""
|
||||
Start session handler: redraw buttons, send node and link messages to grpc server
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("Click START STOP SESSION button")
|
||||
self.destroy_children_widgets(self.edit_frame)
|
||||
helper = CoreToolbarHelp(self.application)
|
||||
# self.destroy_children_widgets(self.edit_frame)
|
||||
self.destroy_children_widgets()
|
||||
self.canvas.mode = GraphMode.SELECT
|
||||
|
||||
# set configuration state
|
||||
state = self.canvas.core_grpc.get_session_state()
|
||||
|
||||
if state == core_pb2.SessionState.SHUTDOWN or self.application.is_open_xml:
|
||||
self.canvas.core_grpc.set_session_state(SessionStateEnum.DEFINITION.value)
|
||||
self.application.is_open_xml = False
|
||||
|
||||
self.canvas.core_grpc.set_session_state(SessionStateEnum.CONFIGURATION.value)
|
||||
|
||||
helper.add_nodes()
|
||||
helper.add_edges()
|
||||
# for node in self.canvas.grpc_manager.nodes.values():
|
||||
# print(node.type, node.model, int(node.x), int(node.y), node.name, node.node_id)
|
||||
# self.canvas.core_grpc.add_node(
|
||||
# node.type, node.model, int(node.x), int(node.y), node.name, node.node_id
|
||||
# )
|
||||
|
||||
# print(len(self.canvas.grpc_manager.edges))
|
||||
# for edge in self.canvas.grpc_manager.edges.values():
|
||||
# print(edge.id1, edge.id2, edge.type1, edge.type2)
|
||||
# self.canvas.core_grpc.add_link(
|
||||
# edge.id1, edge.id2, edge.type1, edge.type2, edge
|
||||
# )
|
||||
self.canvas.core_grpc.set_session_state(SessionStateEnum.INSTANTIATION.value)
|
||||
# self.canvas.core_grpc.get_session()
|
||||
# self.application.is_open_xml = False
|
||||
self.create_runtime_toolbar()
|
||||
|
||||
def click_link_tool(self):
|
||||
logging.debug("Click LINK button")
|
||||
self.canvas.mode = GraphMode.EDGE
|
||||
|
||||
def pick_router(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("router"))
|
||||
logging.debug("Pick router option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.ROUTER.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.ROUTER.value)
|
||||
self.canvas.draw_node_name = "router"
|
||||
|
||||
def pick_host(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("host"))
|
||||
logging.debug("Pick host option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.HOST.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.HOST.value)
|
||||
self.canvas.draw_node_name = "host"
|
||||
|
||||
def pick_pc(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("pc"))
|
||||
logging.debug("Pick PC option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.PC.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.PC.value)
|
||||
self.canvas.draw_node_name = "PC"
|
||||
|
||||
def pick_mdr(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("mdr"))
|
||||
logging.debug("Pick MDR option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.MDR.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.MDR.value)
|
||||
self.canvas.draw_node_name = "mdr"
|
||||
|
||||
def pick_prouter(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("prouter"))
|
||||
logging.debug("Pick prouter option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.PROUTER.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.PROUTER.value)
|
||||
self.canvas.draw_node_name = "prouter"
|
||||
|
||||
def pick_ovs(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("ovs"))
|
||||
logging.debug("Pick OVS option")
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.OVS.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.OVS.value)
|
||||
self.canvas.draw_node_name = "OVS"
|
||||
|
||||
# TODO what graph node is this
|
||||
def pick_editnode(self, main_button):
|
||||
self.network_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("editnode"))
|
||||
main_button.configure(image=Images.get(ImageEnum.EDITNODE.value))
|
||||
logging.debug("Pick editnode option")
|
||||
|
||||
def draw_network_layer_options(self, network_layer_button):
|
||||
|
@ -220,13 +262,13 @@ class CoreToolbar(object):
|
|||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get("router"),
|
||||
Images.get("host"),
|
||||
Images.get("pc"),
|
||||
Images.get("mdr"),
|
||||
Images.get("prouter"),
|
||||
Images.get("ovs"),
|
||||
Images.get("editnode"),
|
||||
Images.get(ImageEnum.ROUTER.value),
|
||||
Images.get(ImageEnum.HOST.value),
|
||||
Images.get(ImageEnum.PC.value),
|
||||
Images.get(ImageEnum.MDR.value),
|
||||
Images.get(ImageEnum.PROUTER.value),
|
||||
Images.get(ImageEnum.OVS.value),
|
||||
Images.get(ImageEnum.EDITNODE.value),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_router,
|
||||
|
@ -237,9 +279,22 @@ class CoreToolbar(object):
|
|||
self.pick_ovs,
|
||||
self.pick_editnode,
|
||||
]
|
||||
tooltip_list = [
|
||||
"router",
|
||||
"host",
|
||||
"PC",
|
||||
"mdr",
|
||||
"prouter",
|
||||
"OVS",
|
||||
"edit node types",
|
||||
]
|
||||
for i in range(len(img_list)):
|
||||
self.create_button(
|
||||
img_list[i], func_list[i], option_frame, network_layer_button
|
||||
img_list[i],
|
||||
func_list[i],
|
||||
option_frame,
|
||||
network_layer_button,
|
||||
tooltip_list[i],
|
||||
)
|
||||
|
||||
# place frame at a calculated position as well as keep a reference of that frame
|
||||
|
@ -257,7 +312,7 @@ class CoreToolbar(object):
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
router_image = Images.get("router")
|
||||
router_image = Images.get(ImageEnum.ROUTER.value)
|
||||
network_layer_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
indicatoron=False,
|
||||
|
@ -272,29 +327,44 @@ class CoreToolbar(object):
|
|||
CreateToolTip(network_layer_button, "Network-layer virtual nodes")
|
||||
|
||||
def pick_hub(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("hub"))
|
||||
logging.debug("Pick link-layer node HUB")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.HUB.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.HUB.value)
|
||||
self.canvas.draw_node_name = "hub"
|
||||
|
||||
def pick_switch(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("switch"))
|
||||
logging.debug("Pick link-layer node SWITCH")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.SWITCH.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.SWITCH.value)
|
||||
self.canvas.draw_node_name = "switch"
|
||||
|
||||
def pick_wlan(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("wlan"))
|
||||
logging.debug("Pick link-layer node WLAN")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.WLAN.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.WLAN.value)
|
||||
self.canvas.draw_node_name = "wlan"
|
||||
|
||||
def pick_rj45(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("rj45"))
|
||||
logging.debug("Pick link-layer node RJ45")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.RJ45.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.RJ45.value)
|
||||
self.canvas.draw_node_name = "rj45"
|
||||
|
||||
def pick_tunnel(self, main_button):
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("tunnel"))
|
||||
logging.debug("Pick link-layer node TUNNEL")
|
||||
self.link_layer_option_menu.destroy()
|
||||
main_button.configure(image=Images.get(ImageEnum.TUNNEL.value))
|
||||
self.canvas.mode = GraphMode.PICKNODE
|
||||
self.canvas.draw_node_image = Images.get(ImageEnum.TUNNEL.value)
|
||||
self.canvas.draw_node_name = "tunnel"
|
||||
|
||||
def draw_link_layer_options(self, link_layer_button):
|
||||
"""
|
||||
|
@ -307,11 +377,11 @@ class CoreToolbar(object):
|
|||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get("hub"),
|
||||
Images.get("switch"),
|
||||
Images.get("wlan"),
|
||||
Images.get("rj45"),
|
||||
Images.get("tunnel"),
|
||||
Images.get(ImageEnum.HUB.value),
|
||||
Images.get(ImageEnum.SWITCH.value),
|
||||
Images.get(ImageEnum.WLAN.value),
|
||||
Images.get(ImageEnum.RJ45.value),
|
||||
Images.get(ImageEnum.TUNNEL.value),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_hub,
|
||||
|
@ -320,9 +390,20 @@ class CoreToolbar(object):
|
|||
self.pick_rj45,
|
||||
self.pick_tunnel,
|
||||
]
|
||||
tooltip_list = [
|
||||
"ethernet hub",
|
||||
"ethernet switch",
|
||||
"wireless LAN",
|
||||
"rj45 physical interface tool",
|
||||
"tunnel tool",
|
||||
]
|
||||
for i in range(len(img_list)):
|
||||
self.create_button(
|
||||
img_list[i], func_list[i], option_frame, link_layer_button
|
||||
img_list[i],
|
||||
func_list[i],
|
||||
option_frame,
|
||||
link_layer_button,
|
||||
tooltip_list[i],
|
||||
)
|
||||
|
||||
# place frame at a calculated position as well as keep a reference of the frame
|
||||
|
@ -340,7 +421,7 @@ class CoreToolbar(object):
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
hub_image = Images.get("hub")
|
||||
hub_image = Images.get(ImageEnum.HUB.value)
|
||||
link_layer_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
indicatoron=False,
|
||||
|
@ -356,23 +437,22 @@ class CoreToolbar(object):
|
|||
|
||||
def pick_marker(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("marker"))
|
||||
main_button.configure(image=Images.get(ImageEnum.MARKER.value))
|
||||
logging.debug("Pick MARKER")
|
||||
return "break"
|
||||
|
||||
def pick_oval(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("oval"))
|
||||
main_button.configure(image=Images.get(ImageEnum.OVAL.value))
|
||||
logging.debug("Pick OVAL")
|
||||
|
||||
def pick_rectangle(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("rectangle"))
|
||||
main_button.configure(image=Images.get(ImageEnum.RECTANGLE.value))
|
||||
logging.debug("Pick RECTANGLE")
|
||||
|
||||
def pick_text(self, main_button):
|
||||
self.marker_option_menu.destroy()
|
||||
main_button.configure(image=Images.get("text"))
|
||||
main_button.configure(image=Images.get(ImageEnum.TEXT.value))
|
||||
logging.debug("Pick TEXT")
|
||||
|
||||
def draw_marker_options(self, main_button):
|
||||
|
@ -386,10 +466,10 @@ class CoreToolbar(object):
|
|||
self.destroy_previous_frame()
|
||||
option_frame = tk.Frame(self.master, padx=1, pady=1)
|
||||
img_list = [
|
||||
Images.get("marker"),
|
||||
Images.get("oval"),
|
||||
Images.get("rectangle"),
|
||||
Images.get("text"),
|
||||
Images.get(ImageEnum.MARKER.value),
|
||||
Images.get(ImageEnum.OVAL.value),
|
||||
Images.get(ImageEnum.RECTANGLE.value),
|
||||
Images.get(ImageEnum.TEXT.value),
|
||||
]
|
||||
func_list = [
|
||||
self.pick_marker,
|
||||
|
@ -397,8 +477,11 @@ class CoreToolbar(object):
|
|||
self.pick_rectangle,
|
||||
self.pick_text,
|
||||
]
|
||||
tooltip_list = ["marker", "oval", "rectangle", "text"]
|
||||
for i in range(len(img_list)):
|
||||
self.create_button(img_list[i], func_list[i], option_frame, main_button)
|
||||
self.create_button(
|
||||
img_list[i], func_list[i], option_frame, main_button, tooltip_list[i]
|
||||
)
|
||||
|
||||
# place the frame at a calculated position as well as keep a reference of that frame
|
||||
self.draw_button_menu_frame(self.edit_frame, option_frame, main_button)
|
||||
|
@ -415,7 +498,7 @@ class CoreToolbar(object):
|
|||
|
||||
:return: nothing
|
||||
"""
|
||||
marker_image = Images.get("marker")
|
||||
marker_image = Images.get(ImageEnum.MARKER.value)
|
||||
marker_main_button = tk.Radiobutton(
|
||||
self.edit_frame,
|
||||
indicatoron=False,
|
||||
|
@ -430,13 +513,20 @@ class CoreToolbar(object):
|
|||
CreateToolTip(marker_main_button, "background annotation tools")
|
||||
|
||||
def create_toolbar(self):
|
||||
self.load_toolbar_images()
|
||||
"""
|
||||
Create buttons for toolbar in edit mode
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.create_regular_button(
|
||||
self.edit_frame, Images.get("start"), self.click_start_stop_session_tool
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.START.value),
|
||||
self.click_start_session_tool,
|
||||
"start the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("select"),
|
||||
Images.get(ImageEnum.SELECT.value),
|
||||
self.click_selection_tool,
|
||||
self.radio_value,
|
||||
1,
|
||||
|
@ -444,7 +534,7 @@ class CoreToolbar(object):
|
|||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("link"),
|
||||
Images.get(ImageEnum.LINK.value),
|
||||
self.click_link_tool,
|
||||
self.radio_value,
|
||||
2,
|
||||
|
@ -453,11 +543,12 @@ class CoreToolbar(object):
|
|||
self.create_network_layer_button()
|
||||
self.create_link_layer_button()
|
||||
self.create_marker_button()
|
||||
self.radio_value.set(1)
|
||||
|
||||
def create_observe_button(self):
|
||||
menu_button = tk.Menubutton(
|
||||
self.edit_frame,
|
||||
image=Images.get("observe"),
|
||||
image=Images.get(ImageEnum.OBSERVE.value),
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
direction=tk.RIGHT,
|
||||
|
@ -485,8 +576,18 @@ class CoreToolbar(object):
|
|||
menu_button.menu.add_command(label="Edit...")
|
||||
|
||||
def click_stop_button(self):
|
||||
"""
|
||||
redraw buttons on the toolbar, send node and link messages to grpc server
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("Click on STOP button ")
|
||||
self.destroy_children_widgets(self.edit_frame)
|
||||
# self.destroy_children_widgets(self.edit_frame)
|
||||
self.destroy_children_widgets()
|
||||
|
||||
self.canvas.core_grpc.set_session_state(SessionStateEnum.DATACOLLECT.value)
|
||||
self.canvas.core_grpc.delete_links()
|
||||
self.canvas.core_grpc.delete_nodes()
|
||||
self.create_toolbar()
|
||||
|
||||
def click_run_button(self):
|
||||
|
@ -503,11 +604,14 @@ class CoreToolbar(object):
|
|||
|
||||
def create_runtime_toolbar(self):
|
||||
self.create_regular_button(
|
||||
self.edit_frame, Images.get("stop"), self.click_stop_button
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.STOP.value),
|
||||
self.click_stop_button,
|
||||
"stop the session",
|
||||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("select"),
|
||||
Images.get(ImageEnum.SELECT.value),
|
||||
self.click_selection_tool,
|
||||
self.exec_radio_value,
|
||||
1,
|
||||
|
@ -516,7 +620,7 @@ class CoreToolbar(object):
|
|||
self.create_observe_button()
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("plot"),
|
||||
Images.get(ImageEnum.PLOT.value),
|
||||
self.click_plot_button,
|
||||
self.exec_radio_value,
|
||||
2,
|
||||
|
@ -524,7 +628,7 @@ class CoreToolbar(object):
|
|||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("marker"),
|
||||
Images.get(ImageEnum.MARKER.value),
|
||||
self.click_marker_button,
|
||||
self.exec_radio_value,
|
||||
3,
|
||||
|
@ -532,12 +636,16 @@ class CoreToolbar(object):
|
|||
)
|
||||
self.create_radio_button(
|
||||
self.edit_frame,
|
||||
Images.get("twonode"),
|
||||
Images.get(ImageEnum.TWONODE.value),
|
||||
self.click_two_node_button,
|
||||
self.exec_radio_value,
|
||||
4,
|
||||
"run command from one node to another",
|
||||
)
|
||||
self.create_regular_button(
|
||||
self.edit_frame, Images.get("run"), self.click_run_button
|
||||
self.edit_frame,
|
||||
Images.get(ImageEnum.RUN.value),
|
||||
self.click_run_button,
|
||||
"run",
|
||||
)
|
||||
self.exec_radio_value.set(1)
|
||||
|
|
31
coretk/coretk/coretoolbarhelp.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""
|
||||
CoreToolbar help to draw on canvas, and make grpc client call
|
||||
"""
|
||||
|
||||
|
||||
class CoreToolbarHelp:
|
||||
def __init__(self, application):
|
||||
self.application = application
|
||||
self.core_grpc = application.core_grpc
|
||||
|
||||
def add_nodes(self):
|
||||
"""
|
||||
add the nodes stored in grpc manager
|
||||
:return: nothing
|
||||
"""
|
||||
grpc_manager = self.application.canvas.grpc_manager
|
||||
for node in grpc_manager.nodes.values():
|
||||
self.application.core_grpc.add_node(
|
||||
node.type, node.model, int(node.x), int(node.y), node.name, node.node_id
|
||||
)
|
||||
|
||||
def add_edges(self):
|
||||
"""
|
||||
add the edges stored in grpc manager
|
||||
:return:
|
||||
"""
|
||||
grpc_manager = self.application.canvas.grpc_manager
|
||||
for edge in grpc_manager.edges.values():
|
||||
self.application.core_grpc.add_link(
|
||||
edge.id1, edge.id2, edge.type1, edge.type2, edge
|
||||
)
|
|
@ -2,29 +2,51 @@ import enum
|
|||
import logging
|
||||
import tkinter as tk
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.graph_helper import GraphHelper, WlanAntennaManager
|
||||
from coretk.grpcmanagement import GrpcManager
|
||||
from coretk.images import Images
|
||||
from coretk.interface import Interface
|
||||
from coretk.linkinfo import LinkInfo
|
||||
from coretk.nodeconfigtable import NodeConfig
|
||||
|
||||
|
||||
class GraphMode(enum.Enum):
|
||||
SELECT = 0
|
||||
EDGE = 1
|
||||
NODE = 2
|
||||
PICKNODE = 2
|
||||
NODE = 3
|
||||
OTHER = 4
|
||||
|
||||
|
||||
class CanvasGraph(tk.Canvas):
|
||||
def __init__(self, master=None, cnf=None, **kwargs):
|
||||
def __init__(self, master=None, grpc=None, cnf=None, **kwargs):
|
||||
if cnf is None:
|
||||
cnf = {}
|
||||
kwargs["highlightthickness"] = 0
|
||||
super().__init__(master, cnf, **kwargs)
|
||||
self.mode = GraphMode.SELECT
|
||||
self.draw_node_image = None
|
||||
self.draw_node_name = None
|
||||
self.selected = None
|
||||
self.node_context = None
|
||||
self.nodes = {}
|
||||
self.edges = {}
|
||||
self.drawing_edge = None
|
||||
|
||||
self.grid = None
|
||||
self.meters_per_pixel = 1.5
|
||||
self.setup_menus()
|
||||
self.setup_bindings()
|
||||
self.draw_grid()
|
||||
|
||||
self.core_grpc = grpc
|
||||
self.grpc_manager = GrpcManager(grpc)
|
||||
|
||||
self.helper = GraphHelper(self, grpc)
|
||||
# self.core_id_to_canvas_id = {}
|
||||
# self.core_map = CoreToCanvasMapping()
|
||||
# self.draw_existing_component()
|
||||
|
||||
def setup_menus(self):
|
||||
self.node_context = tk.Menu(self.master)
|
||||
|
@ -32,21 +54,201 @@ class CanvasGraph(tk.Canvas):
|
|||
self.node_context.add_command(label="Two")
|
||||
self.node_context.add_command(label="Three")
|
||||
|
||||
def canvas_reset_and_redraw(self, new_grpc):
|
||||
"""
|
||||
Reset the private variables CanvasGraph object, redraw nodes given the new grpc client
|
||||
:param new_grpc:
|
||||
:return:
|
||||
"""
|
||||
# delete any existing drawn items
|
||||
# self.delete_components()
|
||||
self.helper.delete_canvas_components()
|
||||
|
||||
# set the private variables to default value
|
||||
self.mode = GraphMode.SELECT
|
||||
self.draw_node_image = None
|
||||
self.draw_node_name = None
|
||||
self.selected = None
|
||||
self.node_context = None
|
||||
self.nodes = {}
|
||||
self.edges = {}
|
||||
self.drawing_edge = None
|
||||
|
||||
print("graph.py create a new grpc manager")
|
||||
self.grpc_manager = GrpcManager(new_grpc)
|
||||
|
||||
# new grpc
|
||||
self.core_grpc = new_grpc
|
||||
print("grpah.py draw existing component")
|
||||
self.draw_existing_component()
|
||||
print(self.grpc_manager.edges)
|
||||
|
||||
def setup_bindings(self):
|
||||
"""
|
||||
Bind any mouse events or hot keys to the matching action
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.bind("<ButtonPress-1>", self.click_press)
|
||||
self.bind("<ButtonRelease-1>", self.click_release)
|
||||
self.bind("<B1-Motion>", self.click_motion)
|
||||
self.bind("<Button-3>", self.context)
|
||||
self.bind("e", self.set_mode)
|
||||
self.bind("s", self.set_mode)
|
||||
self.bind("n", self.set_mode)
|
||||
|
||||
def draw_grid(self, width=1000, height=800):
|
||||
"""
|
||||
Create grid
|
||||
|
||||
:param int width: the width
|
||||
:param int height: the height
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.grid = self.create_rectangle(
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
outline="#000000",
|
||||
fill="#ffffff",
|
||||
width=1,
|
||||
tags="rectangle",
|
||||
)
|
||||
self.tag_lower(self.grid)
|
||||
for i in range(0, width, 27):
|
||||
self.create_line(i, 0, i, height, dash=(2, 4), tags="gridline")
|
||||
for i in range(0, height, 27):
|
||||
self.create_line(0, i, width, i, dash=(2, 4), tags="gridline")
|
||||
|
||||
def draw_existing_component(self):
|
||||
"""
|
||||
Draw existing node and update the information in grpc manager to match
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
core_id_to_canvas_id = {}
|
||||
|
||||
session_id = self.core_grpc.session_id
|
||||
session = self.core_grpc.core.get_session(session_id).session
|
||||
# redraw existing nodes
|
||||
for node in session.nodes:
|
||||
# peer to peer node is not drawn on the GUI
|
||||
if node.type != core_pb2.NodeType.PEER_TO_PEER:
|
||||
|
||||
# draw nodes on the canvas
|
||||
image, name = Images.convert_type_and_model_to_image(
|
||||
node.type, node.model
|
||||
)
|
||||
n = CanvasNode(
|
||||
node.position.x, node.position.y, image, name, self, node.id
|
||||
)
|
||||
self.nodes[n.id] = n
|
||||
core_id_to_canvas_id[node.id] = n.id
|
||||
|
||||
# store the node in grpc manager
|
||||
self.grpc_manager.add_preexisting_node(n, session_id, node, name)
|
||||
|
||||
self.grpc_manager.update_reusable_id()
|
||||
|
||||
# draw existing links
|
||||
for link in session.links:
|
||||
n1 = self.nodes[core_id_to_canvas_id[link.node_one_id]]
|
||||
n2 = self.nodes[core_id_to_canvas_id[link.node_two_id]]
|
||||
if link.type == core_pb2.LinkType.WIRED:
|
||||
e = CanvasEdge(
|
||||
n1.x_coord,
|
||||
n1.y_coord,
|
||||
n2.x_coord,
|
||||
n2.y_coord,
|
||||
n1.id,
|
||||
self,
|
||||
is_wired=True,
|
||||
)
|
||||
elif link.type == core_pb2.LinkType.WIRELESS:
|
||||
e = CanvasEdge(
|
||||
n1.x_coord,
|
||||
n1.y_coord,
|
||||
n2.x_coord,
|
||||
n2.y_coord,
|
||||
n1.id,
|
||||
self,
|
||||
is_wired=False,
|
||||
)
|
||||
edge_token = tuple(sorted((n1.id, n2.id)))
|
||||
e.token = edge_token
|
||||
e.dst = n2.id
|
||||
n1.edges.add(e)
|
||||
n2.edges.add(e)
|
||||
self.edges[e.token] = e
|
||||
self.grpc_manager.add_edge(session_id, e.token, n1.id, n2.id)
|
||||
|
||||
self.helper.redraw_antenna(link, n1, n2)
|
||||
|
||||
# TODO add back the link info to grpc manager also redraw
|
||||
grpc_if1 = link.interface_one
|
||||
grpc_if2 = link.interface_two
|
||||
ip4_src = None
|
||||
ip4_dst = None
|
||||
ip6_src = None
|
||||
ip6_dst = None
|
||||
if grpc_if1 is not None:
|
||||
ip4_src = grpc_if1.ip4
|
||||
ip6_src = grpc_if1.ip6
|
||||
if grpc_if2 is not None:
|
||||
ip4_dst = grpc_if2.ip4
|
||||
ip6_dst = grpc_if2.ip6
|
||||
e.link_info = LinkInfo(
|
||||
canvas=self,
|
||||
edge=e,
|
||||
ip4_src=ip4_src,
|
||||
ip6_src=ip6_src,
|
||||
ip4_dst=ip4_dst,
|
||||
ip6_dst=ip6_dst,
|
||||
)
|
||||
|
||||
# TODO will include throughput and ipv6 in the future
|
||||
if1 = Interface(grpc_if1.name, grpc_if1.ip4, ifid=grpc_if1.id)
|
||||
if2 = Interface(grpc_if2.name, grpc_if2.ip4, ifid=grpc_if2.id)
|
||||
self.grpc_manager.edges[e.token].interface_1 = if1
|
||||
self.grpc_manager.edges[e.token].interface_2 = if2
|
||||
self.grpc_manager.nodes[
|
||||
core_id_to_canvas_id[link.node_one_id]
|
||||
].interfaces.append(if1)
|
||||
self.grpc_manager.nodes[
|
||||
core_id_to_canvas_id[link.node_two_id]
|
||||
].interfaces.append(if2)
|
||||
|
||||
# lift the nodes so they on top of the links
|
||||
# for i in core_id_to_canvas_id.values():
|
||||
# self.lift(i)
|
||||
for i in self.find_withtag("node"):
|
||||
self.lift(i)
|
||||
|
||||
# def delete_components(self):
|
||||
# tags = ["node", "edge", "linkinfo", "nodename"]
|
||||
# for i in tags:
|
||||
# for id in self.find_withtag(i):
|
||||
# self.delete(id)
|
||||
|
||||
def canvas_xy(self, event):
|
||||
"""
|
||||
Convert window coordinate to canvas coordinate
|
||||
|
||||
:param event:
|
||||
:rtype: (int, int)
|
||||
:return: x, y canvas coordinate
|
||||
"""
|
||||
x = self.canvasx(event.x)
|
||||
y = self.canvasy(event.y)
|
||||
return x, y
|
||||
|
||||
def get_selected(self, event):
|
||||
"""
|
||||
Retrieve the item id that is on the mouse position
|
||||
|
||||
:param event: mouse event
|
||||
:rtype: int
|
||||
:return: the item that the mouse point to
|
||||
"""
|
||||
overlapping = self.find_overlapping(event.x, event.y, event.x, event.y)
|
||||
nodes = set(self.find_withtag("node"))
|
||||
selected = None
|
||||
|
@ -64,6 +266,12 @@ class CanvasGraph(tk.Canvas):
|
|||
return selected
|
||||
|
||||
def click_release(self, event):
|
||||
"""
|
||||
Draw a node or finish drawing an edge according to the current graph mode
|
||||
|
||||
:param event: mouse event
|
||||
:return: nothing
|
||||
"""
|
||||
self.focus_set()
|
||||
self.selected = self.get_selected(event)
|
||||
logging.debug(f"click release selected: {self.selected}")
|
||||
|
@ -71,7 +279,9 @@ class CanvasGraph(tk.Canvas):
|
|||
self.handle_edge_release(event)
|
||||
elif self.mode == GraphMode.NODE:
|
||||
x, y = self.canvas_xy(event)
|
||||
self.add_node(x, y, "switch")
|
||||
self.add_node(x, y, self.draw_node_image, self.draw_node_name)
|
||||
elif self.mode == GraphMode.PICKNODE:
|
||||
self.mode = GraphMode.NODE
|
||||
|
||||
def handle_edge_release(self, event):
|
||||
edge = self.drawing_edge
|
||||
|
@ -106,9 +316,37 @@ class CanvasGraph(tk.Canvas):
|
|||
node_dst = self.nodes[edge.dst]
|
||||
node_dst.edges.add(edge)
|
||||
|
||||
self.grpc_manager.add_edge(
|
||||
self.core_grpc.session_id, edge.token, node_src.id, node_dst.id
|
||||
)
|
||||
|
||||
# draw link info on the edge
|
||||
if1 = self.grpc_manager.edges[edge.token].interface_1
|
||||
if2 = self.grpc_manager.edges[edge.token].interface_2
|
||||
ip4_and_prefix_1 = None
|
||||
ip4_and_prefix_2 = None
|
||||
if if1 is not None:
|
||||
ip4_and_prefix_1 = if1.ip4_and_prefix
|
||||
if if2 is not None:
|
||||
ip4_and_prefix_2 = if2.ip4_and_prefix
|
||||
edge.link_info = LinkInfo(
|
||||
self,
|
||||
edge,
|
||||
ip4_src=ip4_and_prefix_1,
|
||||
ip6_src=None,
|
||||
ip4_dst=ip4_and_prefix_2,
|
||||
ip6_dst=None,
|
||||
)
|
||||
|
||||
logging.debug(f"edges: {self.find_withtag('edge')}")
|
||||
|
||||
def click_press(self, event):
|
||||
"""
|
||||
Start drawing an edge if mouse click is on a node
|
||||
|
||||
:param event: mouse event
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug(f"click press: {event}")
|
||||
selected = self.get_selected(event)
|
||||
is_node = selected in self.find_withtag("node")
|
||||
|
@ -117,6 +355,12 @@ class CanvasGraph(tk.Canvas):
|
|||
self.drawing_edge = CanvasEdge(x, y, x, y, selected, self)
|
||||
|
||||
def click_motion(self, event):
|
||||
"""
|
||||
Redraw drawing edge according to the current position of the mouse
|
||||
|
||||
:param event: mouse event
|
||||
:return: nothing
|
||||
"""
|
||||
if self.mode == GraphMode.EDGE and self.drawing_edge is not None:
|
||||
x2, y2 = self.canvas_xy(event)
|
||||
x1, y1, _, _ = self.coords(self.drawing_edge.id)
|
||||
|
@ -129,70 +373,136 @@ class CanvasGraph(tk.Canvas):
|
|||
logging.debug(f"node context: {selected}")
|
||||
self.node_context.post(event.x_root, event.y_root)
|
||||
|
||||
def set_mode(self, event):
|
||||
logging.debug(f"mode event: {event}")
|
||||
if event.char == "e":
|
||||
self.mode = GraphMode.EDGE
|
||||
elif event.char == "s":
|
||||
self.mode = GraphMode.SELECT
|
||||
elif event.char == "n":
|
||||
self.mode = GraphMode.NODE
|
||||
logging.debug(f"graph mode: {self.mode}")
|
||||
|
||||
def add_node(self, x, y, image_name):
|
||||
image = Images.get(image_name)
|
||||
node = CanvasNode(x, y, image, self)
|
||||
self.nodes[node.id] = node
|
||||
return node
|
||||
def add_node(self, x, y, image, node_name):
|
||||
plot_id = self.find_all()[0]
|
||||
if self.selected == plot_id:
|
||||
node = CanvasNode(
|
||||
x=x,
|
||||
y=y,
|
||||
image=image,
|
||||
node_type=node_name,
|
||||
canvas=self,
|
||||
core_id=self.grpc_manager.peek_id(),
|
||||
)
|
||||
self.nodes[node.id] = node
|
||||
self.grpc_manager.add_node(
|
||||
self.core_grpc.session_id, node.id, x, y, node_name
|
||||
)
|
||||
return node
|
||||
|
||||
|
||||
class CanvasEdge:
|
||||
width = 3
|
||||
"""
|
||||
Canvas edge class
|
||||
"""
|
||||
|
||||
def __init__(self, x1, y1, x2, y2, src, canvas):
|
||||
width = 1.4
|
||||
|
||||
def __init__(self, x1, y1, x2, y2, src, canvas, is_wired=None):
|
||||
"""
|
||||
Create an instance of canvas edge object
|
||||
:param int x1: source x-coord
|
||||
:param int y1: source y-coord
|
||||
:param int x2: destination x-coord
|
||||
:param int y2: destination y-coord
|
||||
:param int src: source id
|
||||
:param tkinter.Canvas canvas: canvas object
|
||||
"""
|
||||
self.src = src
|
||||
self.dst = None
|
||||
self.canvas = canvas
|
||||
self.id = self.canvas.create_line(x1, y1, x2, y2, tags="edge", width=self.width)
|
||||
|
||||
if is_wired is None or is_wired is True:
|
||||
self.id = self.canvas.create_line(
|
||||
x1, y1, x2, y2, tags="edge", width=self.width, fill="#ff0000"
|
||||
)
|
||||
else:
|
||||
self.id = self.canvas.create_line(
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
tags="edge",
|
||||
width=self.width,
|
||||
fill="#ff0000",
|
||||
state=tk.HIDDEN,
|
||||
)
|
||||
self.token = None
|
||||
self.canvas.tag_lower(self.id)
|
||||
|
||||
# link info object
|
||||
self.link_info = None
|
||||
self.throughput = None
|
||||
self.wired = is_wired
|
||||
# TODO resolve this
|
||||
# self.canvas.tag_lower(self.id)
|
||||
|
||||
def complete(self, dst, x, y):
|
||||
self.dst = dst
|
||||
self.token = tuple(sorted((self.src, self.dst)))
|
||||
x1, y1, _, _ = self.canvas.coords(self.id)
|
||||
self.canvas.coords(self.id, x1, y1, x, y)
|
||||
self.canvas.helper.draw_wireless_case(self.src, self.dst, self)
|
||||
self.canvas.lift(self.src)
|
||||
self.canvas.lift(self.dst)
|
||||
|
||||
def delete(self):
|
||||
self.canvas.delete(self.id)
|
||||
|
||||
|
||||
class CanvasNode:
|
||||
def __init__(self, x, y, image, canvas):
|
||||
def __init__(self, x, y, image, node_type, canvas, core_id):
|
||||
self.image = image
|
||||
self.node_type = node_type
|
||||
self.canvas = canvas
|
||||
self.id = self.canvas.create_image(
|
||||
x, y, anchor=tk.CENTER, image=self.image, tags="node"
|
||||
)
|
||||
self.name = f"Node {self.id}"
|
||||
self.text_id = self.canvas.create_text(x, y + 20, text=self.name)
|
||||
self.core_id = core_id
|
||||
self.x_coord = x
|
||||
self.y_coord = y
|
||||
self.name = f"N{self.core_id}"
|
||||
self.text_id = self.canvas.create_text(
|
||||
x, y + 20, text=self.name, tags="nodename"
|
||||
)
|
||||
self.antenna_draw = WlanAntennaManager(self.canvas, self.id)
|
||||
|
||||
self.canvas.tag_bind(self.id, "<ButtonPress-1>", self.click_press)
|
||||
self.canvas.tag_bind(self.id, "<ButtonRelease-1>", self.click_release)
|
||||
self.canvas.tag_bind(self.id, "<B1-Motion>", self.motion)
|
||||
self.canvas.tag_bind(self.id, "<Button-3>", self.context)
|
||||
self.canvas.tag_bind(self.id, "<Double-Button-1>", self.double_click)
|
||||
|
||||
self.edges = set()
|
||||
self.wlans = []
|
||||
self.moving = None
|
||||
|
||||
def double_click(self, event):
|
||||
node_id = self.canvas.grpc_manager.nodes[self.id].node_id
|
||||
state = self.canvas.core_grpc.get_session_state()
|
||||
if state == core_pb2.SessionState.RUNTIME:
|
||||
self.canvas.core_grpc.launch_terminal(node_id)
|
||||
else:
|
||||
print("config table show up")
|
||||
NodeConfig(self, self.image, self.node_type, self.name)
|
||||
|
||||
def update_coords(self):
|
||||
self.x_coord, self.y_coord = self.canvas.coords(self.id)
|
||||
|
||||
def click_press(self, event):
|
||||
logging.debug(f"click press {self.name}: {event}")
|
||||
self.moving = self.canvas.canvas_xy(event)
|
||||
# return "break"
|
||||
|
||||
def click_release(self, event):
|
||||
logging.debug(f"click release {self.name}: {event}")
|
||||
self.update_coords()
|
||||
self.canvas.grpc_manager.update_node_location(
|
||||
self.id, self.x_coord, self.y_coord
|
||||
)
|
||||
self.moving = None
|
||||
|
||||
def motion(self, event):
|
||||
if self.canvas.mode == GraphMode.EDGE:
|
||||
if self.canvas.mode == GraphMode.EDGE or self.canvas.mode == GraphMode.NODE:
|
||||
return
|
||||
x, y = self.canvas.canvas_xy(event)
|
||||
moving_x, moving_y = self.moving
|
||||
|
@ -202,13 +512,25 @@ class CanvasNode:
|
|||
old_x, old_y = self.canvas.coords(self.id)
|
||||
self.canvas.move(self.id, offset_x, offset_y)
|
||||
self.canvas.move(self.text_id, offset_x, offset_y)
|
||||
self.antenna_draw.update_antennas_position(offset_x, offset_y)
|
||||
|
||||
new_x, new_y = self.canvas.coords(self.id)
|
||||
|
||||
if self.canvas.core_grpc.get_session_state() == core_pb2.SessionState.RUNTIME:
|
||||
self.canvas.core_grpc.edit_node(self.core_id, int(new_x), int(new_y))
|
||||
|
||||
for edge in self.edges:
|
||||
x1, y1, x2, y2 = self.canvas.coords(edge.id)
|
||||
if x1 == old_x and y1 == old_y:
|
||||
self.canvas.coords(edge.id, new_x, new_y, x2, y2)
|
||||
else:
|
||||
self.canvas.coords(edge.id, x1, y1, new_x, new_y)
|
||||
edge.link_info.recalculate_info()
|
||||
# self.canvas.core_grpc.throughput_draw.update_throughtput_location(edge)
|
||||
|
||||
self.canvas.helper.update_wlan_connection(
|
||||
old_x, old_y, new_x, new_y, self.wlans
|
||||
)
|
||||
|
||||
def context(self, event):
|
||||
logging.debug(f"context click {self.name}: {event}")
|
||||
|
|
211
coretk/coretk/graph_helper.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
"""
|
||||
Some graph helper functions
|
||||
"""
|
||||
import logging
|
||||
import tkinter as tk
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.images import ImageEnum, Images
|
||||
|
||||
CANVAS_COMPONENT_TAGS = ["edge", "node", "nodename", "wallpaper", "linkinfo"]
|
||||
|
||||
|
||||
class GraphHelper:
|
||||
def __init__(self, canvas, grpc):
|
||||
"""
|
||||
create an instance of GraphHelper object
|
||||
"""
|
||||
self.canvas = canvas
|
||||
self.core_grpc = grpc
|
||||
|
||||
def delete_canvas_components(self):
|
||||
"""
|
||||
delete the components of the graph leaving only the blank canvas
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
for tag in CANVAS_COMPONENT_TAGS:
|
||||
for i in self.canvas.find_withtag(tag):
|
||||
self.canvas.delete(i)
|
||||
|
||||
def draw_wireless_case(self, src_id, dst_id, edge):
|
||||
src_node_name = self.canvas.nodes[src_id].node_type
|
||||
dst_node_name = self.canvas.nodes[dst_id].node_type
|
||||
|
||||
if src_node_name == "wlan" or dst_node_name == "wlan":
|
||||
self.canvas.itemconfig(edge.id, state=tk.HIDDEN)
|
||||
edge.wired = False
|
||||
if edge.token not in self.canvas.edges:
|
||||
if src_node_name == "wlan" and dst_node_name == "wlan":
|
||||
self.canvas.nodes[src_id].antenna_draw.add_antenna()
|
||||
elif src_node_name == "wlan":
|
||||
self.canvas.nodes[dst_id].antenna_draw.add_antenna()
|
||||
else:
|
||||
self.canvas.nodes[src_id].antenna_draw.add_antenna()
|
||||
|
||||
edge.wired = True
|
||||
|
||||
def redraw_antenna(self, link, node_one, node_two):
|
||||
if link.type == core_pb2.LinkType.WIRELESS:
|
||||
if node_one.node_type == "wlan" and node_two.node_type == "wlan":
|
||||
node_one.antenna_draw.add_antenna()
|
||||
elif node_one.node_type == "wlan" and node_two.node_type != "wlan":
|
||||
node_two.antenna_draw.add_antenna()
|
||||
elif node_one.node_type != "wlan" and node_two.node_type == "wlan":
|
||||
node_one.antenna_draw.add_antenna()
|
||||
else:
|
||||
logging.error(
|
||||
"graph_helper.py WIRELESS link but both nodes are non-wireless node"
|
||||
)
|
||||
|
||||
def update_wlan_connection(self, old_x, old_y, new_x, new_y, edge_ids):
|
||||
for eid in edge_ids:
|
||||
x1, y1, x2, y2 = self.canvas.coords(eid)
|
||||
if x1 == old_x and y1 == old_y:
|
||||
self.canvas.coords(eid, new_x, new_y, x2, y2)
|
||||
else:
|
||||
self.canvas.coords(eid, x1, y1, new_x, new_y)
|
||||
|
||||
|
||||
class WlanAntennaManager:
|
||||
def __init__(self, canvas, node_id):
|
||||
"""
|
||||
crate an instance for AntennaManager
|
||||
"""
|
||||
self.canvas = canvas
|
||||
self.node_id = node_id
|
||||
self.quantity = 0
|
||||
self._max = 5
|
||||
self.antennas = []
|
||||
|
||||
# distance between each antenna
|
||||
self.offset = 0
|
||||
|
||||
def add_antenna(self):
|
||||
"""
|
||||
add an antenna to a node
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if self.quantity < 5:
|
||||
x, y = self.canvas.coords(self.node_id)
|
||||
self.antennas.append(
|
||||
self.canvas.create_image(
|
||||
x - 16 + self.offset,
|
||||
y - 16,
|
||||
anchor=tk.CENTER,
|
||||
image=Images.get(ImageEnum.ANTENNA.value),
|
||||
tags="antenna",
|
||||
)
|
||||
)
|
||||
self.quantity = self.quantity + 1
|
||||
self.offset = self.offset + 8
|
||||
|
||||
def update_antennas_position(self, offset_x, offset_y):
|
||||
"""
|
||||
redraw antennas of a node according to the new node position
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
for i in self.antennas:
|
||||
self.canvas.move(i, offset_x, offset_y)
|
||||
|
||||
def delete_antenna(self, canvas_id):
|
||||
return
|
||||
|
||||
def delete_antennas(self):
|
||||
"""
|
||||
Delete all the antennas of a node
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
for i in self.antennas:
|
||||
self.canvas.delete(i)
|
||||
|
||||
|
||||
# class WlanConnection:
|
||||
# def __init__(self, canvas, grpc):
|
||||
# """
|
||||
# create in
|
||||
# :param canvas:
|
||||
# """
|
||||
# self.canvas = canvas
|
||||
# self.core_grpc = grpc
|
||||
# self.throughput_on = False
|
||||
# self.map_node_link = {}
|
||||
# self.links = []
|
||||
#
|
||||
# def wireless_nodes(self):
|
||||
# """
|
||||
# retrieve all the wireless clouds in the canvas
|
||||
#
|
||||
# :return: list(coretk.graph.CanvasNode)
|
||||
# """
|
||||
# wireless_nodes = []
|
||||
# for n in self.canvas.nodes.values():
|
||||
# if n.node_type == "wlan":
|
||||
# wireless_nodes.append(n)
|
||||
# return wireless_nodes
|
||||
#
|
||||
# def draw_wireless_link(self, src, dst):
|
||||
# """
|
||||
# draw a line between 2 nodes that are connected to the same wireless cloud
|
||||
#
|
||||
# :param coretk.graph.CanvasNode src: source node
|
||||
# :param coretk.graph.CanvasNode dst: destination node
|
||||
# :return: nothing
|
||||
# """
|
||||
# cid = self.canvas.create_line(src.x_coord, src.y_coord, dst.x_coord, dst.y_coord, tags="wlanconnection")
|
||||
# if src.id not in self.map_node_link:
|
||||
# self.map_node_link[src.id] = []
|
||||
# if dst.id not in self.map_node_link:
|
||||
# self.map_node_link[dst.id] = []
|
||||
# self.map_node_link[src.id].append(cid)
|
||||
# self.map_node_link[dst.id].append(cid)
|
||||
# self.links.append(cid)
|
||||
#
|
||||
# def subnet_wireless_connection(self, wlan_node):
|
||||
# """
|
||||
# retrieve all the non-wireless nodes connected to wireless_node and create line (represent wireless connection) between each pair of nodes
|
||||
# :param coretk.grpah.CanvasNode wlan_node: wireless node
|
||||
#
|
||||
# :return: nothing
|
||||
# """
|
||||
# non_wlan_nodes = []
|
||||
# for e in wlan_node.edges:
|
||||
# src = self.canvas.nodes[e.src]
|
||||
# dst = self.canvas.nodes[e.dst]
|
||||
# if src.node_type == "wlan" and dst.node_type != "wlan":
|
||||
# non_wlan_nodes.append(dst)
|
||||
# elif src.node_type != "wlan" and dst.node_type == "wlan":
|
||||
# non_wlan_nodes.append(src)
|
||||
#
|
||||
# size = len(non_wlan_nodes)
|
||||
# for i in range(size):
|
||||
# for j in range(i+1, size):
|
||||
# self.draw_wireless_link(non_wlan_nodes[i], non_wlan_nodes[j])
|
||||
#
|
||||
# def session_wireless_connection(self):
|
||||
# """
|
||||
# draw all the wireless connection in the canvas
|
||||
#
|
||||
# :return: nothing
|
||||
# """
|
||||
# wlan_nodes = self.wireless_nodes()
|
||||
# for n in wlan_nodes:
|
||||
# self.subnet_wireless_connection(n)
|
||||
#
|
||||
# def show_links(self):
|
||||
# """
|
||||
# show all the links
|
||||
# """
|
||||
# for l in self.links:
|
||||
# self.canvas.itemconfig(l, state=tk.NORMAL)
|
||||
#
|
||||
# def hide_links(self):
|
||||
# """
|
||||
# hide all the links
|
||||
# :return:
|
||||
# """
|
||||
# for l in self.links:
|
||||
# self.canvas.itemconfig(l, state=tk.HIDDEN)
|
326
coretk/coretk/grpcmanagement.py
Normal file
|
@ -0,0 +1,326 @@
|
|||
"""
|
||||
Manage useful informations about the nodes, edges and configuration
|
||||
that can be useful for grpc, acts like a session class
|
||||
"""
|
||||
import logging
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.coretocanvas import CoreToCanvasMapping
|
||||
from coretk.interface import Interface, InterfaceManager
|
||||
|
||||
link_layer_nodes = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
||||
network_layer_nodes = ["router", "host", "PC", "mdr", "prouter", "OVS"]
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, session_id, node_id, node_type, model, x, y, name):
|
||||
"""
|
||||
Create an instance of a node
|
||||
|
||||
:param int session_id: session id
|
||||
:param int node_id: node id
|
||||
:param core_pb2.NodeType node_type: node type
|
||||
:param int x: x coordinate
|
||||
:param int y: coordinate
|
||||
:param str name: node name
|
||||
"""
|
||||
self.session_id = session_id
|
||||
self.node_id = node_id
|
||||
self.type = node_type
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.model = model
|
||||
self.name = name
|
||||
self.interfaces = []
|
||||
|
||||
|
||||
class Edge:
|
||||
def __init__(self, session_id, node_id_1, node_type_1, node_id_2, node_type_2):
|
||||
"""
|
||||
Create an instance of an edge
|
||||
:param int session_id: session id
|
||||
:param int node_id_1: node 1 id
|
||||
:param int node_type_1: node 1 type
|
||||
:param core_pb2.NodeType node_id_2: node 2 id
|
||||
:param core_pb2.NodeType node_type_2: node 2 type
|
||||
"""
|
||||
self.session_id = session_id
|
||||
self.id1 = node_id_1
|
||||
self.id2 = node_id_2
|
||||
self.type1 = node_type_1
|
||||
self.type2 = node_type_2
|
||||
self.interface_1 = None
|
||||
self.interface_2 = None
|
||||
|
||||
|
||||
class GrpcManager:
|
||||
def __init__(self, grpc):
|
||||
self.nodes = {}
|
||||
self.edges = {}
|
||||
self.id = None
|
||||
# A list of id for re-use, keep in increasing order
|
||||
self.reusable = []
|
||||
|
||||
self.preexisting = []
|
||||
self.core_grpc = None
|
||||
|
||||
# self.update_preexisting_ids()
|
||||
# self.core_id_to_canvas_id = {}
|
||||
self.interfaces_manager = InterfaceManager()
|
||||
|
||||
# map tuple(core_node_id, interface_id) to and edge
|
||||
# self.node_id_and_interface_to_edge_token = {}
|
||||
self.core_mapping = CoreToCanvasMapping()
|
||||
|
||||
def update_preexisting_ids(self):
|
||||
"""
|
||||
get preexisting node ids
|
||||
:return:
|
||||
"""
|
||||
max_id = 0
|
||||
client = self.core_grpc.core
|
||||
sessions = client.get_sessions().sessions
|
||||
for session_summary in sessions:
|
||||
session = client.get_session(session_summary.id).session
|
||||
for node in session.nodes:
|
||||
if node.id > max_id:
|
||||
max_id = node.id
|
||||
self.preexisting.append(node.id)
|
||||
self.id = max_id + 1
|
||||
self.update_reusable_id()
|
||||
|
||||
def peek_id(self):
|
||||
"""
|
||||
Peek the next id to be used
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if len(self.reusable) == 0:
|
||||
return self.id
|
||||
else:
|
||||
return self.reusable[0]
|
||||
|
||||
def get_id(self):
|
||||
"""
|
||||
Get the next node id as well as update id status and reusable ids
|
||||
|
||||
:rtype: int
|
||||
:return: the next id to be used
|
||||
"""
|
||||
if len(self.reusable) == 0:
|
||||
new_id = self.id
|
||||
self.id = self.id + 1
|
||||
return new_id
|
||||
else:
|
||||
return self.reusable.pop(0)
|
||||
|
||||
def add_node(self, session_id, canvas_id, x, y, name):
|
||||
"""
|
||||
Add node, with information filled in, to grpc manager
|
||||
|
||||
:param int session_id: session id
|
||||
:param int canvas_id: node's canvas id
|
||||
:param int x: x coord
|
||||
:param int y: y coord
|
||||
:param str name: node type
|
||||
:return: nothing
|
||||
"""
|
||||
node_type = None
|
||||
node_model = None
|
||||
if name in link_layer_nodes:
|
||||
if name == "switch":
|
||||
node_type = core_pb2.NodeType.SWITCH
|
||||
elif name == "hub":
|
||||
node_type = core_pb2.NodeType.HUB
|
||||
elif name == "wlan":
|
||||
node_type = core_pb2.NodeType.WIRELESS_LAN
|
||||
elif name == "rj45":
|
||||
node_type = core_pb2.NodeType.RJ45
|
||||
elif name == "tunnel":
|
||||
node_type = core_pb2.NodeType.TUNNEL
|
||||
elif name in network_layer_nodes:
|
||||
node_type = core_pb2.NodeType.DEFAULT
|
||||
node_model = name
|
||||
else:
|
||||
logging.error("grpcmanagemeny.py INVALID node name")
|
||||
nid = self.get_id()
|
||||
create_node = Node(session_id, nid, node_type, node_model, x, y, name)
|
||||
self.nodes[canvas_id] = create_node
|
||||
self.core_mapping.map_core_id_to_canvas_id(nid, canvas_id)
|
||||
# self.core_id_to_canvas_id[nid] = canvas_id
|
||||
logging.debug(
|
||||
"Adding node to GrpcManager.. Session id: %s, Coords: (%s, %s), Name: %s",
|
||||
session_id,
|
||||
x,
|
||||
y,
|
||||
name,
|
||||
)
|
||||
|
||||
def add_preexisting_node(self, canvas_node, session_id, core_node, name):
|
||||
"""
|
||||
Add preexisting nodes to grpc manager
|
||||
|
||||
:param str name: node_type
|
||||
:param core_pb2.Node core_node: core node grpc message
|
||||
:param coretk.graph.CanvasNode canvas_node: canvas node
|
||||
:param int session_id: session id
|
||||
:return: nothing
|
||||
"""
|
||||
|
||||
# update the next available id
|
||||
core_id = core_node.id
|
||||
print(core_id)
|
||||
if self.id is None or core_id >= self.id:
|
||||
self.id = core_id + 1
|
||||
self.preexisting.append(core_id)
|
||||
n = Node(
|
||||
session_id,
|
||||
core_id,
|
||||
core_node.type,
|
||||
core_node.model,
|
||||
canvas_node.x_coord,
|
||||
canvas_node.y_coord,
|
||||
name,
|
||||
)
|
||||
self.nodes[canvas_node.id] = n
|
||||
|
||||
def update_node_location(self, canvas_id, new_x, new_y):
|
||||
"""
|
||||
update node
|
||||
|
||||
:param int canvas_id: canvas id of that node
|
||||
:param int new_x: new x coord
|
||||
:param int new_y: new y coord
|
||||
:return: nothing
|
||||
"""
|
||||
self.nodes[canvas_id].x = new_x
|
||||
self.nodes[canvas_id].y = new_y
|
||||
|
||||
def update_reusable_id(self):
|
||||
"""
|
||||
Update available id for reuse
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if len(self.preexisting) > 0:
|
||||
for i in range(1, self.id):
|
||||
if i not in self.preexisting:
|
||||
self.reusable.append(i)
|
||||
|
||||
self.preexisting.clear()
|
||||
logging.debug("Next id: %s, Reusable: %s", self.id, self.reusable)
|
||||
|
||||
def delete_node(self, canvas_id):
|
||||
"""
|
||||
Delete a node from the session
|
||||
|
||||
:param int canvas_id: node's id in the canvas
|
||||
:return: thing
|
||||
"""
|
||||
try:
|
||||
self.nodes.pop(canvas_id)
|
||||
self.reuseable.append(canvas_id)
|
||||
self.reuseable.sort()
|
||||
except KeyError:
|
||||
logging.error("grpcmanagement.py INVALID NODE CANVAS ID")
|
||||
|
||||
def create_interface(self, edge, src_canvas_id, dst_canvas_id):
|
||||
"""
|
||||
Create the interface for the two end of an edge, add a copy to node's interfaces
|
||||
|
||||
:param coretk.grpcmanagement.Edge edge: edge to add interfaces to
|
||||
:param int src_canvas_id: canvas id for the source node
|
||||
:param int dst_canvas_id: canvas id for the destination node
|
||||
:return: nothing
|
||||
"""
|
||||
src_interface = None
|
||||
dst_interface = None
|
||||
print("create interface")
|
||||
self.interfaces_manager.new_subnet()
|
||||
|
||||
src_node = self.nodes[src_canvas_id]
|
||||
if src_node.model in network_layer_nodes:
|
||||
ifid = len(src_node.interfaces)
|
||||
name = "eth" + str(ifid)
|
||||
src_interface = Interface(
|
||||
name=name, ifid=ifid, ipv4=str(self.interfaces_manager.get_address())
|
||||
)
|
||||
self.nodes[src_canvas_id].interfaces.append(src_interface)
|
||||
logging.debug(
|
||||
"Create source interface 1... IP: %s, name: %s",
|
||||
src_interface.ipv4,
|
||||
src_interface.name,
|
||||
)
|
||||
|
||||
dst_node = self.nodes[dst_canvas_id]
|
||||
if dst_node.model in network_layer_nodes:
|
||||
ifid = len(dst_node.interfaces)
|
||||
name = "eth" + str(ifid)
|
||||
dst_interface = Interface(
|
||||
name=name, ifid=ifid, ipv4=str(self.interfaces_manager.get_address())
|
||||
)
|
||||
self.nodes[dst_canvas_id].interfaces.append(dst_interface)
|
||||
logging.debug(
|
||||
"Create destination interface... IP: %s, name: %s",
|
||||
dst_interface.ipv4,
|
||||
dst_interface.name,
|
||||
)
|
||||
|
||||
edge.interface_1 = src_interface
|
||||
edge.interface_2 = dst_interface
|
||||
return src_interface, dst_interface
|
||||
|
||||
def add_edge(self, session_id, token, canvas_id_1, canvas_id_2):
|
||||
"""
|
||||
Add an edge to grpc manager
|
||||
|
||||
:param int session_id: core session id
|
||||
:param tuple(int, int) token: edge's identification in the canvas
|
||||
:param int canvas_id_1: canvas id of source node
|
||||
:param int canvas_id_2: canvas_id of destination node
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if canvas_id_1 in self.nodes and canvas_id_2 in self.nodes:
|
||||
edge = Edge(
|
||||
session_id,
|
||||
self.nodes[canvas_id_1].node_id,
|
||||
self.nodes[canvas_id_1].type,
|
||||
self.nodes[canvas_id_2].node_id,
|
||||
self.nodes[canvas_id_2].type,
|
||||
)
|
||||
self.edges[token] = edge
|
||||
src_interface, dst_interface = self.create_interface(
|
||||
edge, canvas_id_1, canvas_id_2
|
||||
)
|
||||
node_one_id = self.nodes[canvas_id_1].node_id
|
||||
node_two_id = self.nodes[canvas_id_2].node_id
|
||||
|
||||
# provide a way to get an edge from a core node and an interface id
|
||||
if src_interface is not None:
|
||||
# self.node_id_and_interface_to_edge_token[tuple([node_one_id, src_interface.id])] = token
|
||||
self.core_mapping.map_node_and_interface_to_canvas_edge(
|
||||
node_one_id, src_interface.id, token
|
||||
)
|
||||
logging.debug(
|
||||
"map node id %s, interface_id %s to edge token %s",
|
||||
node_one_id,
|
||||
src_interface.id,
|
||||
token,
|
||||
)
|
||||
|
||||
if dst_interface is not None:
|
||||
# self.node_id_and_interface_to_edge_token[tuple([node_two_id, dst_interface.id])] = token
|
||||
self.core_mapping.map_node_and_interface_to_canvas_edge(
|
||||
node_two_id, dst_interface.id, token
|
||||
)
|
||||
logging.debug(
|
||||
"map node id %s, interface_id %s to edge token %s",
|
||||
node_two_id,
|
||||
dst_interface.id,
|
||||
token,
|
||||
)
|
||||
|
||||
logging.debug("Adding edge to grpc manager...")
|
||||
else:
|
||||
logging.error("grpcmanagement.py INVALID CANVAS NODE ID")
|
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
BIN
coretk/coretk/icons/antenna.gif
Normal file
After Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
coretk/coretk/icons/document-new.gif
Normal file
After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 635 B After Width: | Height: | Size: 635 B |
BIN
coretk/coretk/icons/edit-delete.gif
Normal file
After Width: | Height: | Size: 1,006 B |
BIN
coretk/coretk/icons/fileopen.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 719 B After Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 86 B |
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 174 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 160 B After Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 755 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 753 B After Width: | Height: | Size: 753 B |
Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B |
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 127 B After Width: | Height: | Size: 127 B |
Before Width: | Height: | Size: 799 B After Width: | Height: | Size: 799 B |
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
@ -1,8 +1,13 @@
|
|||
import logging
|
||||
import os
|
||||
from enum import Enum
|
||||
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
|
||||
PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
ICONS_DIR = os.path.join(PATH, "icons")
|
||||
|
||||
|
||||
class Images:
|
||||
|
@ -10,7 +15,7 @@ class Images:
|
|||
|
||||
@classmethod
|
||||
def load(cls, name, file_path):
|
||||
file_path = os.path.join(PATH, file_path)
|
||||
# file_path = os.path.join(PATH, file_path)
|
||||
image = Image.open(file_path)
|
||||
tk_image = ImageTk.PhotoImage(image)
|
||||
cls.images[name] = tk_image
|
||||
|
@ -18,3 +23,78 @@ class Images:
|
|||
@classmethod
|
||||
def get(cls, name):
|
||||
return cls.images[name]
|
||||
|
||||
@classmethod
|
||||
def convert_type_and_model_to_image(cls, node_type, node_model):
|
||||
"""
|
||||
Retrieve image based on type and model
|
||||
:param core_pb2.NodeType node_type: core node type
|
||||
:param string node_model: the node model
|
||||
|
||||
:rtype: tuple(PhotoImage, str)
|
||||
:return: the matching image and its name
|
||||
"""
|
||||
if node_type == core_pb2.NodeType.SWITCH:
|
||||
return Images.get(ImageEnum.SWITCH.value), "switch"
|
||||
if node_type == core_pb2.NodeType.HUB:
|
||||
return Images.get(ImageEnum.HUB.value), "hub"
|
||||
if node_type == core_pb2.NodeType.WIRELESS_LAN:
|
||||
return Images.get(ImageEnum.WLAN.value), "wlan"
|
||||
if node_type == core_pb2.NodeType.RJ45:
|
||||
return Images.get(ImageEnum.RJ45.value), "rj45"
|
||||
if node_type == core_pb2.NodeType.TUNNEL:
|
||||
return Images.get(ImageEnum.TUNNEL.value), "tunnel"
|
||||
if node_type == core_pb2.NodeType.DEFAULT:
|
||||
if node_model == "router":
|
||||
return Images.get(ImageEnum.ROUTER.value), "router"
|
||||
if node_model == "host":
|
||||
return Images.get((ImageEnum.HOST.value)), "host"
|
||||
if node_model == "PC":
|
||||
return Images.get(ImageEnum.PC.value), "PC"
|
||||
if node_model == "mdr":
|
||||
return Images.get(ImageEnum.MDR.value), "mdr"
|
||||
if node_model == "prouter":
|
||||
return Images.get(ImageEnum.PROUTER.value), "prouter"
|
||||
if node_model == "OVS":
|
||||
return Images.get(ImageEnum.OVS.value), "ovs"
|
||||
else:
|
||||
logging.debug("INVALID INPUT OR NOT CONSIDERED YET")
|
||||
|
||||
|
||||
class ImageEnum(Enum):
|
||||
SWITCH = "lanswitch"
|
||||
CORE = "core-icon"
|
||||
START = "start"
|
||||
MARKER = "marker"
|
||||
ROUTER = "router"
|
||||
SELECT = "select"
|
||||
LINK = "link"
|
||||
HUB = "hub"
|
||||
WLAN = "wlan"
|
||||
RJ45 = "rj45"
|
||||
TUNNEL = "tunnel"
|
||||
OVAL = "oval"
|
||||
RECTANGLE = "rectangle"
|
||||
TEXT = "text"
|
||||
HOST = "host"
|
||||
PC = "pc"
|
||||
MDR = "mdr"
|
||||
PROUTER = "router_green"
|
||||
OVS = "OVS"
|
||||
EDITNODE = "document-properties"
|
||||
PLOT = "plot"
|
||||
TWONODE = "twonode"
|
||||
STOP = "stop"
|
||||
OBSERVE = "observe"
|
||||
RUN = "run"
|
||||
DOCUMENTNEW = "document-new"
|
||||
FILEOPEN = "fileopen"
|
||||
EDITDELETE = "edit-delete"
|
||||
ANTENNA = "antenna"
|
||||
|
||||
|
||||
def load_core_images(images):
|
||||
for file_name in os.listdir(ICONS_DIR):
|
||||
file_path = os.path.join(ICONS_DIR, file_name)
|
||||
name = file_name.split(".")[0]
|
||||
images.load(name, file_path)
|
||||
|
|
95
coretk/coretk/interface.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
import ipaddress
|
||||
import random
|
||||
|
||||
|
||||
class Interface:
|
||||
def __init__(self, name, ipv4, ifid=None):
|
||||
"""
|
||||
Create an interface instance
|
||||
|
||||
:param str name: interface name
|
||||
:param str ip4: IPv4
|
||||
:param str mac: MAC address
|
||||
:param int ifid: interface id
|
||||
"""
|
||||
self.name = name
|
||||
self.ipv4 = ipv4
|
||||
self.ip4prefix = 24
|
||||
self.ip4_and_prefix = ipv4 + "/" + str(self.ip4prefix)
|
||||
self.mac = self.random_mac_address()
|
||||
self.id = ifid
|
||||
|
||||
def random_mac_address(self):
|
||||
"""
|
||||
create a random MAC address for an interface
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
return "02:00:00:%02x:%02x:%02x" % (
|
||||
random.randint(0, 255),
|
||||
random.randint(0, 255),
|
||||
random.randint(0, 225),
|
||||
)
|
||||
|
||||
|
||||
class SubnetAddresses:
|
||||
def __init__(self, network, addresses):
|
||||
self.network = network
|
||||
self.address = addresses
|
||||
self.address_index = 0
|
||||
|
||||
def get_new_ip_address(self):
|
||||
ipaddr = self.address[self.address_index]
|
||||
self.address_index = self.address_index + 1
|
||||
return ipaddr
|
||||
|
||||
|
||||
class InterfaceManager:
|
||||
def __init__(self):
|
||||
# self.prefix = None
|
||||
self.core_subnets = list(
|
||||
ipaddress.ip_network("10.0.0.0/12").subnets(prefixlen_diff=12)
|
||||
)
|
||||
self.subnet_index = 0
|
||||
self.address_index = None
|
||||
|
||||
# self.network = ipaddress.ip_network("10.0.0.0/24")
|
||||
# self.addresses = list(self.network.hosts())
|
||||
self.network = None
|
||||
self.addresses = None
|
||||
# self.start_interface_manager()
|
||||
|
||||
def start_interface_manager(self):
|
||||
self.subnet_index = 0
|
||||
self.network = self.core_subnets[self.subnet_index]
|
||||
self.subnet_index = self.subnet_index + 1
|
||||
self.addresses = list(self.network.hosts())
|
||||
self.address_index = 0
|
||||
|
||||
def get_address(self):
|
||||
"""
|
||||
Retrieve a new ipv4 address
|
||||
|
||||
:return:
|
||||
"""
|
||||
# i = self.index
|
||||
# self.address_index = self.index + 1
|
||||
# return self.addresses[i]
|
||||
ipaddr = self.addresses[self.address_index]
|
||||
self.address_index = self.address_index + 1
|
||||
return ipaddr
|
||||
|
||||
def new_subnet(self):
|
||||
self.network = self.core_subnets[self.subnet_index]
|
||||
self.subnet_index = self.subnet_index + 1
|
||||
self.addresses = list(self.network.hosts())
|
||||
self.address_index = 0
|
||||
|
||||
# def new_subnet(self):
|
||||
# """
|
||||
# retrieve a new subnet
|
||||
# :return:
|
||||
# """
|
||||
# if self.prefix is None:
|
||||
# self.prefix =
|
||||
# self.addresses = list(ipaddress.ip_network("10.0.0.0/24").hosts())
|
272
coretk/coretk/linkinfo.py
Normal file
|
@ -0,0 +1,272 @@
|
|||
"""
|
||||
Link information, such as IPv4, IPv6 and throughput drawn in the canvas
|
||||
"""
|
||||
import logging
|
||||
import math
|
||||
|
||||
WIRELESS_DEF = ["mdr", "wlan"]
|
||||
|
||||
|
||||
class LinkInfo:
|
||||
def __init__(self, canvas, edge, ip4_src, ip6_src, ip4_dst, ip6_dst):
|
||||
"""
|
||||
create an instance of LinkInfo object
|
||||
:param tkinter.Canvas canvas: canvas object
|
||||
:param coretk.graph.CanvasEdge edge: canvas edge onject
|
||||
:param ip4_src:
|
||||
:param ip6_src:
|
||||
:param ip4_dst:
|
||||
:param ip6_dst:
|
||||
"""
|
||||
self.canvas = canvas
|
||||
self.edge = edge
|
||||
# self.edge_id = edge.id
|
||||
self.radius = 37
|
||||
self.core_grpc = self.canvas.core_grpc
|
||||
|
||||
self.ip4_address_1 = ip4_src
|
||||
self.ip6_address_1 = ip6_src
|
||||
self.ip4_address_2 = ip4_dst
|
||||
self.ip6_address_2 = ip6_dst
|
||||
self.id1 = self.create_edge_src_info()
|
||||
self.id2 = self.create_edge_dst_info()
|
||||
|
||||
def slope_src_dst(self):
|
||||
"""
|
||||
calculate slope of the line connecting source node to destination node
|
||||
:rtype: float
|
||||
:return: slope of line
|
||||
"""
|
||||
x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
|
||||
if x2 - x1 == 0:
|
||||
return 9999.0
|
||||
else:
|
||||
return (y2 - y1) / (x2 - x1)
|
||||
|
||||
def create_edge_src_info(self):
|
||||
"""
|
||||
draw the ip address for source node
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
x1, y1, x2, _ = self.canvas.coords(self.edge.id)
|
||||
m = self.slope_src_dst()
|
||||
distance = math.cos(math.atan(m)) * self.radius
|
||||
if x1 > x2:
|
||||
distance = -distance
|
||||
# id1 = self.canvas.create_text(x1, y1, text=self.ip4_address_1)
|
||||
id1 = self.canvas.create_text(
|
||||
x1 + distance, y1 + distance * m, text=self.ip4_address_1, tags="linkinfo"
|
||||
)
|
||||
return id1
|
||||
|
||||
def create_edge_dst_info(self):
|
||||
"""
|
||||
draw the ip address for destination node
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
x1, _, x2, y2 = self.canvas.coords(self.edge.id)
|
||||
m = self.slope_src_dst()
|
||||
distance = math.cos(math.atan(m)) * self.radius
|
||||
if x1 > x2:
|
||||
distance = -distance
|
||||
# id2 = self.canvas.create_text(x2, y2, text=self.ip4_address_2)
|
||||
id2 = self.canvas.create_text(
|
||||
x2 - distance, y2 - distance * m, text=self.ip4_address_2, tags="linkinfo"
|
||||
)
|
||||
return id2
|
||||
|
||||
def recalculate_info(self):
|
||||
"""
|
||||
move the node info when the canvas node move
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
|
||||
m = self.slope_src_dst()
|
||||
distance = math.cos(math.atan(m)) * self.radius
|
||||
if x1 > x2:
|
||||
distance = -distance
|
||||
new_x1 = x1 + distance
|
||||
new_y1 = y1 + distance * m
|
||||
new_x2 = x2 - distance
|
||||
new_y2 = y2 - distance * m
|
||||
self.canvas.coords(self.id1, new_x1, new_y1)
|
||||
self.canvas.coords(self.id2, new_x2, new_y2)
|
||||
|
||||
# def link_througput(self):
|
||||
# x1, y1, x2, y2 = self.canvas.coords(self.edge.id)
|
||||
# x = (x1 + x2) / 2
|
||||
# y = (y1 + y2) / 2
|
||||
# tid = self.canvas.create_text(x, y, text="place text here")
|
||||
# return tid
|
||||
|
||||
|
||||
class Throughput:
|
||||
def __init__(self, canvas, grpc):
|
||||
"""
|
||||
create an instance of Throughput object
|
||||
:param tkinter.Canvas canvas: canvas object
|
||||
:param coretk.coregrpc,CoreGrpc grpc: grpc object
|
||||
"""
|
||||
self.canvas = canvas
|
||||
self.core_grpc = grpc
|
||||
self.grpc_manager = canvas.grpc_manager
|
||||
|
||||
# edge canvas id mapped to throughput value
|
||||
self.tracker = {}
|
||||
|
||||
# map an edge canvas id to a throughput canvas id
|
||||
self.map = {}
|
||||
|
||||
self.edge_id_to_token = {}
|
||||
|
||||
def load_throughput_info(self, interface_throughputs):
|
||||
"""
|
||||
load all interface throughouts from an event
|
||||
|
||||
:param repeated core_bp2.InterfaceThroughputinterface_throughputs: interface throughputs
|
||||
:return: nothing
|
||||
"""
|
||||
for t in interface_throughputs:
|
||||
nid = t.node_id
|
||||
iid = t.interface_id
|
||||
tp = t.throughput
|
||||
# token = self.grpc_manager.node_id_and_interface_to_edge_token[nid, iid]
|
||||
token = self.grpc_manager.core_mapping.get_token_from_node_and_interface(
|
||||
nid, iid
|
||||
)
|
||||
print(token)
|
||||
edge_id = self.canvas.edges[token].id
|
||||
|
||||
self.edge_id_to_token[edge_id] = token
|
||||
|
||||
if edge_id not in self.tracker:
|
||||
self.tracker[edge_id] = tp
|
||||
else:
|
||||
temp = self.tracker[edge_id]
|
||||
self.tracker[edge_id] = (temp + tp) / 2
|
||||
|
||||
def edge_is_wired(self, token):
|
||||
"""
|
||||
determine whether link is a WIRED link
|
||||
|
||||
:param token:
|
||||
:return:
|
||||
"""
|
||||
canvas_edge = self.canvas.edges[token]
|
||||
canvas_src_id = canvas_edge.src
|
||||
canvas_dst_id = canvas_edge.dst
|
||||
src_node = self.canvas.nodes[canvas_src_id]
|
||||
dst_node = self.canvas.nodes[canvas_dst_id]
|
||||
|
||||
if src_node.node_type == "wlan":
|
||||
if dst_node.node_type == "mdr":
|
||||
return False
|
||||
else:
|
||||
logging.debug("linkinfo.py is_wired WARNING wlan only connected to mdr")
|
||||
return True
|
||||
if dst_node.node_type == "wlan":
|
||||
if src_node.node_type == "mdr":
|
||||
return False
|
||||
else:
|
||||
logging.debug("linkinfo.py is_wired WARNING wlan only connected to mdr")
|
||||
return True
|
||||
return True
|
||||
|
||||
def draw_wired_throughput(self, edge_id):
|
||||
|
||||
x1, y1, x2, y2 = self.canvas.coords(edge_id)
|
||||
x = (x1 + x2) / 2
|
||||
y = (y1 + y2) / 2
|
||||
|
||||
if edge_id not in self.map:
|
||||
tp_id = self.canvas.create_text(
|
||||
x, y, text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id])
|
||||
)
|
||||
self.map[edge_id] = tp_id
|
||||
|
||||
# redraw throughput
|
||||
else:
|
||||
self.canvas.itemconfig(
|
||||
self.map[edge_id],
|
||||
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
|
||||
)
|
||||
|
||||
def draw_wireless_throughput(self, edge_id):
|
||||
token = self.edge_id_to_token[edge_id]
|
||||
canvas_edge = self.canvas.edges[token]
|
||||
canvas_src_id = canvas_edge.src
|
||||
canvas_dst_id = canvas_edge.dst
|
||||
src_node = self.canvas.nodes[canvas_src_id]
|
||||
dst_node = self.canvas.nodes[canvas_dst_id]
|
||||
|
||||
# non_wlan_node = None
|
||||
if src_node.node_type == "wlan":
|
||||
non_wlan_node = dst_node
|
||||
else:
|
||||
non_wlan_node = src_node
|
||||
|
||||
x, y = self.canvas.coords(non_wlan_node.id)
|
||||
if edge_id not in self.map:
|
||||
tp_id = self.canvas.create_text(
|
||||
x + 50,
|
||||
y + 25,
|
||||
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
|
||||
)
|
||||
self.map[edge_id] = tp_id
|
||||
|
||||
# redraw throughput
|
||||
else:
|
||||
self.canvas.itemconfig(
|
||||
self.map[edge_id],
|
||||
text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
|
||||
)
|
||||
|
||||
def draw_throughputs(self):
|
||||
for edge_id in self.tracker:
|
||||
if self.edge_is_wired(self.edge_id_to_token[edge_id]):
|
||||
self.draw_wired_throughput(edge_id)
|
||||
else:
|
||||
self.draw_wireless_throughput(edge_id)
|
||||
# draw wireless throughput
|
||||
|
||||
# x1, y1, x2, y2 = self.canvas.coords(edge_id)
|
||||
# x = (x1 + x2) / 2
|
||||
# y = (y1 + y2) / 2
|
||||
#
|
||||
# print(self.is_wired(self.edge_id_to_token[edge_id]))
|
||||
# # new throughput
|
||||
# if edge_id not in self.map:
|
||||
# tp_id = self.canvas.create_text(
|
||||
# x, y, text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id])
|
||||
# )
|
||||
# self.map[edge_id] = tp_id
|
||||
#
|
||||
# # redraw throughput
|
||||
# else:
|
||||
# self.canvas.itemconfig(
|
||||
# self.map[edge_id],
|
||||
# text="{0:.3f} kbps".format(0.001 * self.tracker[edge_id]),
|
||||
# )
|
||||
|
||||
def process_grpc_throughput_event(self, interface_throughputs):
|
||||
self.load_throughput_info(interface_throughputs)
|
||||
self.draw_throughputs()
|
||||
|
||||
def update_throughtput_location(self, edge):
|
||||
tp_id = self.map[edge.id]
|
||||
if self.edge_is_wired(self.edge_id_to_token[edge.id]):
|
||||
x1, y1 = self.canvas.coords(edge.src)
|
||||
x2, y2 = self.canvas.coords(edge.dst)
|
||||
x = (x1 + x2) / 2
|
||||
y = (y1 + y2) / 2
|
||||
self.canvas.coords(tp_id, x, y)
|
||||
else:
|
||||
if self.canvas.nodes[edge.src].node_type == "wlan":
|
||||
x, y = self.canvas.coords(edge.dst)
|
||||
self.canvas.coords(tp_id, x + 50, y + 20)
|
||||
else:
|
||||
x, y = self.canvas.coords(edge.src)
|
||||
self.canvas.coords(tp_id, x + 50, y + 25)
|
|
@ -4,6 +4,13 @@ The actions taken when each menubar option is clicked
|
|||
|
||||
import logging
|
||||
import webbrowser
|
||||
from tkinter import filedialog, messagebox
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk.setwallpaper import CanvasWallpaper
|
||||
from coretk.sizeandscale import SizeAndScale
|
||||
|
||||
SAVEDIR = "/home/ncs/Desktop/"
|
||||
|
||||
|
||||
def sub_menu_items():
|
||||
|
@ -30,22 +37,14 @@ def file_reload():
|
|||
logging.debug("Click file Reload")
|
||||
|
||||
|
||||
def file_save():
|
||||
logging.debug("Click file save")
|
||||
# def file_save():
|
||||
# logging.debug("Click file save")
|
||||
|
||||
|
||||
def file_save_shortcut(event):
|
||||
logging.debug("Shortcut for file save")
|
||||
|
||||
|
||||
def file_save_as_xml():
|
||||
logging.debug("Click save as XML")
|
||||
|
||||
|
||||
def file_save_as_imn():
|
||||
logging.debug("Click save as imn")
|
||||
|
||||
|
||||
def file_export_python_script():
|
||||
logging.debug("Click file export python script")
|
||||
|
||||
|
@ -70,10 +69,6 @@ def file_save_screenshot():
|
|||
logging.debug("Click file save screenshot")
|
||||
|
||||
|
||||
def file_example_link():
|
||||
logging.debug("Click file example link")
|
||||
|
||||
|
||||
def edit_undo():
|
||||
logging.debug("Click edit undo")
|
||||
|
||||
|
@ -160,6 +155,7 @@ def canvas_delete():
|
|||
|
||||
def canvas_size_scale():
|
||||
logging.debug("Click canvas size/scale")
|
||||
SizeAndScale()
|
||||
|
||||
|
||||
def canvas_wallpaper():
|
||||
|
@ -294,10 +290,6 @@ def widgets_configure_throughput():
|
|||
logging.debug("Click widgets configure throughput")
|
||||
|
||||
|
||||
def session_start():
|
||||
logging.debug("Click session start")
|
||||
|
||||
|
||||
def session_change_sessions():
|
||||
logging.debug("Click session change sessions")
|
||||
|
||||
|
@ -326,13 +318,118 @@ def session_options():
|
|||
logging.debug("Click session options")
|
||||
|
||||
|
||||
def help_core_github():
|
||||
webbrowser.open_new("https://github.com/coreemu/core")
|
||||
|
||||
|
||||
def help_core_documentation():
|
||||
webbrowser.open_new("http://coreemu.github.io/core/")
|
||||
|
||||
|
||||
def help_about():
|
||||
logging.debug("Click help About")
|
||||
|
||||
|
||||
class MenuAction:
|
||||
"""
|
||||
Actions performed when choosing menu items
|
||||
"""
|
||||
|
||||
def __init__(self, application, master):
|
||||
self.master = master
|
||||
self.application = application
|
||||
self.core_grpc = application.core_grpc
|
||||
|
||||
def clean_nodes_links_and_set_configuarations(self):
|
||||
"""
|
||||
Prompt use to stop running session before application is closed
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info(
|
||||
"menuaction.py: clean_nodes_links_and_set_configuration() Exiting the program"
|
||||
)
|
||||
grpc = self.application.core_grpc
|
||||
state = grpc.get_session_state()
|
||||
|
||||
if (
|
||||
state == core_pb2.SessionState.SHUTDOWN
|
||||
or state == core_pb2.SessionState.DEFINITION
|
||||
):
|
||||
grpc.delete_session()
|
||||
grpc.core.close()
|
||||
# self.application.quit()
|
||||
else:
|
||||
msgbox = messagebox.askyesnocancel("stop", "Stop the running session?")
|
||||
|
||||
if msgbox or msgbox is False:
|
||||
if msgbox:
|
||||
grpc.set_session_state("datacollect")
|
||||
grpc.delete_links()
|
||||
grpc.delete_nodes()
|
||||
grpc.delete_session()
|
||||
# else:
|
||||
# grpc.set_session_state("definition")
|
||||
grpc.core.close()
|
||||
# self.application.quit()
|
||||
|
||||
def on_quit(self):
|
||||
"""
|
||||
Prompt user whether so save running session, and then close the application
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.clean_nodes_links_and_set_configuarations()
|
||||
# self.application.core_grpc.close()
|
||||
self.application.quit()
|
||||
|
||||
def file_save_as_xml(self):
|
||||
logging.info("menuaction.py file_save_as_xml()")
|
||||
grpc = self.application.core_grpc
|
||||
file_path = filedialog.asksaveasfilename(
|
||||
initialdir=SAVEDIR,
|
||||
title="Save As",
|
||||
filetypes=(("EmulationScript XML files", "*.xml"), ("All files", "*")),
|
||||
defaultextension=".xml",
|
||||
)
|
||||
# with open("prev_saved_xml.txt", "a") as file:
|
||||
# file.write(file_path + "\n")
|
||||
grpc.save_xml(file_path)
|
||||
|
||||
def file_open_xml(self):
|
||||
logging.info("menuaction.py file_open_xml()")
|
||||
self.application.is_open_xml = True
|
||||
file_path = filedialog.askopenfilename(
|
||||
initialdir=SAVEDIR,
|
||||
title="Open",
|
||||
filetypes=(("EmulationScript XML File", "*.xml"), ("All Files", "*")),
|
||||
)
|
||||
# clean up before opening a new session
|
||||
self.clean_nodes_links_and_set_configuarations()
|
||||
# grpc = CoreGrpc(self.application.master)
|
||||
# grpc.core.connect()
|
||||
core_grpc = self.application.core_grpc
|
||||
core_grpc.core.connect()
|
||||
# session_id = core_grpc.open_xml(file_path)
|
||||
# core_grpc.session_id = session_id
|
||||
|
||||
core_grpc.open_xml(file_path)
|
||||
# print("Print session state")
|
||||
# print(grpc.get_session_state())
|
||||
self.application.canvas.canvas_reset_and_redraw(core_grpc)
|
||||
|
||||
# Todo might not need
|
||||
self.application.core_grpc = core_grpc
|
||||
|
||||
self.application.core_editbar.destroy_children_widgets()
|
||||
self.application.core_editbar.create_toolbar()
|
||||
# self.application.is_open_xml = False
|
||||
|
||||
# self.application.core_editbar.create_runtime_toolbar()
|
||||
# self.application.canvas.draw_existing_component()
|
||||
# t1 = time.clock()
|
||||
# print(t1 - t0)
|
||||
|
||||
def canvas_size_and_scale(self):
|
||||
self.application.size_and_scale = SizeAndScale(self.application)
|
||||
|
||||
def canvas_set_wallpaper(self):
|
||||
self.application.set_wallpaper = CanvasWallpaper(self.application)
|
||||
|
||||
def help_core_github(self):
|
||||
webbrowser.open_new("https://github.com/coreemu/core")
|
||||
|
||||
def help_core_documentation(self):
|
||||
webbrowser.open_new("http://coreemu.github.io/core/")
|
||||
|
|
150
coretk/coretk/nodeconfigtable.py
Normal file
|
@ -0,0 +1,150 @@
|
|||
"""
|
||||
Create toplevel for node configuration
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
ICONS_DIR = os.path.join(PATH, "icons")
|
||||
|
||||
NETWORKNODETYPES = ["switch", "hub", "wlan", "rj45", "tunnel"]
|
||||
DEFAULTNODES = ["router", "host", "PC"]
|
||||
|
||||
|
||||
class NodeConfig:
|
||||
def __init__(self, canvas_node, image, node_type, name):
|
||||
self.image = image
|
||||
self.node_type = node_type
|
||||
self.name = name
|
||||
self.canvas_node = canvas_node
|
||||
|
||||
self.top = tk.Toplevel()
|
||||
self.top.title(node_type + " configuration")
|
||||
self.namevar = tk.StringVar(self.top, value="default name")
|
||||
self.name_and_image_definition()
|
||||
self.type_and_service_definition()
|
||||
self.select_definition()
|
||||
|
||||
def open_icon_dir(self, toplevel, entry_text):
|
||||
filename = filedialog.askopenfilename(
|
||||
initialdir=ICONS_DIR,
|
||||
title="Open",
|
||||
filetypes=(
|
||||
("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."),
|
||||
("All Files", "*"),
|
||||
),
|
||||
)
|
||||
if len(filename) > 0:
|
||||
img = Image.open(filename)
|
||||
tk_img = ImageTk.PhotoImage(img)
|
||||
lb = toplevel.grid_slaves(1, 0)[0]
|
||||
lb.configure(image=tk_img)
|
||||
lb.image = tk_img
|
||||
entry_text.set(filename)
|
||||
|
||||
def click_apply(self, toplevel, entry_text):
|
||||
imgfile = entry_text.get()
|
||||
if imgfile:
|
||||
img = Image.open(imgfile)
|
||||
tk_img = ImageTk.PhotoImage(img)
|
||||
lb = self.top.grid_slaves(row=0, column=3)[0]
|
||||
lb.configure(image=tk_img)
|
||||
lb.image = tk_img
|
||||
self.image = tk_img
|
||||
toplevel.destroy()
|
||||
|
||||
def img_modification(self):
|
||||
print("image modification")
|
||||
t = tk.Toplevel()
|
||||
t.title(self.name + " image")
|
||||
|
||||
f = tk.Frame(t)
|
||||
entry_text = tk.StringVar()
|
||||
image_file_label = tk.Label(f, text="Image file: ")
|
||||
image_file_label.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
image_file_entry = tk.Entry(f, textvariable=entry_text, width=60)
|
||||
image_file_entry.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
image_file_button = tk.Button(
|
||||
f, text="...", command=lambda: self.open_icon_dir(t, entry_text)
|
||||
)
|
||||
image_file_button.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
f.grid(sticky=tk.W + tk.E)
|
||||
|
||||
img = tk.Label(t, image=self.image)
|
||||
img.grid(sticky=tk.W + tk.E)
|
||||
|
||||
f = tk.Frame(t)
|
||||
apply_button = tk.Button(
|
||||
f, text="Apply", command=lambda: self.click_apply(t, entry_text)
|
||||
)
|
||||
apply_button.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
apply_to_multiple_button = tk.Button(f, text="Apply to multiple...")
|
||||
apply_to_multiple_button.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
cancel_button = tk.Button(f, text="Cancel", command=t.destroy)
|
||||
cancel_button.pack(side=tk.LEFT, padx=2, pady=2)
|
||||
f.grid(sticky=tk.E + tk.W)
|
||||
|
||||
def name_and_image_definition(self):
|
||||
name_label = tk.Label(self.top, text="Node name: ")
|
||||
name_label.grid()
|
||||
name_entry = tk.Entry(self.top, textvariable=self.namevar)
|
||||
name_entry.grid(row=0, column=1)
|
||||
|
||||
core_button = tk.Button(self.top, text="None")
|
||||
core_button.grid(row=0, column=2)
|
||||
img_button = tk.Button(
|
||||
self.top,
|
||||
image=self.image,
|
||||
width=40,
|
||||
height=40,
|
||||
command=self.img_modification,
|
||||
)
|
||||
img_button.grid(row=0, column=3)
|
||||
|
||||
def type_and_service_definition(self):
|
||||
f = tk.Frame(self.top)
|
||||
type_label = tk.Label(f, text="Type: ")
|
||||
type_label.pack(side=tk.LEFT)
|
||||
|
||||
type_button = tk.Button(f, text="None")
|
||||
type_button.pack(side=tk.LEFT)
|
||||
|
||||
service_button = tk.Button(f, text="Services...")
|
||||
service_button.pack(side=tk.LEFT)
|
||||
|
||||
f.grid(row=1, column=1, columnspan=2, sticky=tk.W)
|
||||
|
||||
def config_apply(self):
|
||||
"""
|
||||
modify image of the canvas node
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("nodeconfigtable.py configuration apply")
|
||||
self.canvas_node.image = self.image
|
||||
self.canvas_node.canvas.itemconfig(self.canvas_node.id, image=self.image)
|
||||
self.top.destroy()
|
||||
|
||||
def config_cancel(self):
|
||||
"""
|
||||
save chosen image but not modify canvas node
|
||||
:return: nothing
|
||||
"""
|
||||
logging.debug("nodeconfigtable.py configuration cancel")
|
||||
self.canvas_node.image = self.image
|
||||
self.top.destroy()
|
||||
|
||||
def select_definition(self):
|
||||
f = tk.Frame(self.top)
|
||||
apply_button = tk.Button(f, text="Apply", command=self.config_apply)
|
||||
apply_button.pack(side=tk.LEFT)
|
||||
cancel_button = tk.Button(f, text="Cancel", command=self.config_cancel)
|
||||
cancel_button.pack(side=tk.LEFT)
|
||||
f.grid(row=3, column=1, sticky=tk.W)
|
||||
|
||||
def network_node_config(self):
|
||||
self.name_and_image_definition()
|
||||
self.select_definition()
|
BIN
coretk/coretk/oldimage/OVS.gif
Executable file
After Width: | Height: | Size: 744 B |
BIN
coretk/coretk/oldimage/core-icon.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
coretk/coretk/oldimage/document-properties.gif
Normal file
After Width: | Height: | Size: 635 B |
BIN
coretk/coretk/oldimage/host.gif
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
coretk/coretk/oldimage/hub.gif
Normal file
After Width: | Height: | Size: 719 B |
BIN
coretk/coretk/oldimage/lanswitch.gif
Normal file
After Width: | Height: | Size: 744 B |
BIN
coretk/coretk/oldimage/link.gif
Normal file
After Width: | Height: | Size: 86 B |
BIN
coretk/coretk/oldimage/marker.gif
Normal file
After Width: | Height: | Size: 375 B |
BIN
coretk/coretk/oldimage/mdr.gif
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
coretk/coretk/oldimage/observe.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
coretk/coretk/oldimage/oval.gif
Normal file
After Width: | Height: | Size: 174 B |
BIN
coretk/coretk/oldimage/pc.gif
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
coretk/coretk/oldimage/plot.gif
Normal file
After Width: | Height: | Size: 265 B |
BIN
coretk/coretk/oldimage/rectangle.gif
Normal file
After Width: | Height: | Size: 160 B |
BIN
coretk/coretk/oldimage/rj45.gif
Normal file
After Width: | Height: | Size: 755 B |
BIN
coretk/coretk/oldimage/router.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
coretk/coretk/oldimage/router_green.gif
Normal file
After Width: | Height: | Size: 753 B |
BIN
coretk/coretk/oldimage/run.gif
Normal file
After Width: | Height: | Size: 324 B |
BIN
coretk/coretk/oldimage/select.gif
Normal file
After Width: | Height: | Size: 925 B |
BIN
coretk/coretk/oldimage/start.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
coretk/coretk/oldimage/stop.gif
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
coretk/coretk/oldimage/text.gif
Normal file
After Width: | Height: | Size: 127 B |
BIN
coretk/coretk/oldimage/tunnel.gif
Normal file
After Width: | Height: | Size: 799 B |
BIN
coretk/coretk/oldimage/twonode.gif
Normal file
After Width: | Height: | Size: 220 B |
BIN
coretk/coretk/oldimage/wlan.gif
Normal file
After Width: | Height: | Size: 146 B |
183
coretk/coretk/querysessiondrawing.py
Normal file
|
@ -0,0 +1,183 @@
|
|||
import logging
|
||||
import tkinter as tk
|
||||
from tkinter.ttk import Scrollbar, Treeview
|
||||
|
||||
from coretk.images import ImageEnum, Images
|
||||
|
||||
|
||||
class SessionTable:
|
||||
def __init__(self, grpc, master):
|
||||
"""
|
||||
create session table instance
|
||||
:param coretk.coregrpc.CoreGrpc grpc: coregrpc
|
||||
:param root.master master:
|
||||
"""
|
||||
self.grpc = grpc
|
||||
self.selected = False
|
||||
self.selected_sid = None
|
||||
self.master = master
|
||||
self.top = tk.Toplevel(self.master)
|
||||
self.description_definition()
|
||||
self.top.title("CORE sessions")
|
||||
|
||||
self.tree = Treeview(self.top)
|
||||
# self.tree.pack(side=tk.TOP)
|
||||
self.tree.grid(row=1, column=0, columnspan=2)
|
||||
self.draw_scrollbar()
|
||||
self.draw()
|
||||
|
||||
def description_definition(self):
|
||||
"""
|
||||
write a short description
|
||||
:return: nothing
|
||||
"""
|
||||
lable = tk.Label(
|
||||
self.top,
|
||||
text="Below is a list of active CORE sessions. Double-click to "
|
||||
"\nconnect to an existing session. Usually, only sessions in "
|
||||
"\nthe RUNTIME state persist in the daemon, except for the "
|
||||
"\none you might be concurrently editting.",
|
||||
)
|
||||
lable.grid(sticky=tk.W)
|
||||
|
||||
def column_definition(self):
|
||||
# self.tree["columns"] = ("name", "nodecount", "filename", "date")
|
||||
self.tree["columns"] = "nodecount"
|
||||
self.tree.column("#0", width=300, minwidth=30)
|
||||
# self.tree.column("name", width=72, miwidth=30)
|
||||
self.tree.column("nodecount", width=300, minwidth=30)
|
||||
# self.tree.column("filename", width=92, minwidth=30)
|
||||
# self.tree.column("date", width=170, minwidth=30)
|
||||
|
||||
def draw_scrollbar(self):
|
||||
yscrollbar = Scrollbar(self.top, orient="vertical", command=self.tree.yview)
|
||||
yscrollbar.grid(row=1, column=3, sticky=tk.N + tk.S + tk.W)
|
||||
self.tree.configure(yscrollcommand=yscrollbar.set)
|
||||
|
||||
xscrollbar = Scrollbar(self.top, orient="horizontal", command=self.tree.xview)
|
||||
xscrollbar.grid(row=2, columnspan=2, sticky=tk.E + tk.W + tk.S)
|
||||
self.tree.configure(xscrollcommand=xscrollbar.set)
|
||||
|
||||
def heading_definition(self):
|
||||
self.tree.heading("#0", text="ID", anchor=tk.W)
|
||||
# self.tree.heading("name", text="Name", anchor=tk.CENTER)
|
||||
self.tree.heading("nodecount", text="Node Count", anchor=tk.W)
|
||||
# self.tree.heading("filename", text="Filename", anchor=tk.CENTER)
|
||||
# self.tree.heading("date", text="Date", anchor=tk.CENTER)
|
||||
|
||||
def enter_session(self, sid):
|
||||
self.top.destroy()
|
||||
response = self.grpc.core.get_session(sid)
|
||||
self.grpc.session_id = sid
|
||||
self.grpc.core.events(sid, self.grpc.log_event)
|
||||
logging.info("Entering session_id %s.... Result: %s", sid, response)
|
||||
|
||||
def new_session(self):
|
||||
self.top.destroy()
|
||||
self.grpc.create_new_session()
|
||||
|
||||
def on_selected(self, event):
|
||||
item = self.tree.selection()
|
||||
sid = int(self.tree.item(item, "text"))
|
||||
self.enter_session(sid)
|
||||
|
||||
def click_select(self, event):
|
||||
# logging.debug("Click on %s ", event)
|
||||
item = self.tree.selection()
|
||||
sid = int(self.tree.item(item, "text"))
|
||||
self.selected = True
|
||||
self.selected_sid = sid
|
||||
|
||||
def session_definition(self):
|
||||
response = self.grpc.core.get_sessions()
|
||||
# logging.info("querysessiondrawing.py Get all sessions %s", response)
|
||||
index = 1
|
||||
for session in response.sessions:
|
||||
self.tree.insert(
|
||||
"", index, None, text=str(session.id), values=(str(session.nodes))
|
||||
)
|
||||
index = index + 1
|
||||
self.tree.bind("<Double-1>", self.on_selected)
|
||||
self.tree.bind("<<TreeviewSelect>>", self.click_select)
|
||||
|
||||
def click_connect(self):
|
||||
"""
|
||||
if no session is selected yet, create a new one else join that session
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if self.selected and self.selected_sid is not None:
|
||||
self.enter_session(self.selected_sid)
|
||||
elif not self.selected and self.selected_sid is None:
|
||||
self.new_session()
|
||||
else:
|
||||
logging.error("querysessiondrawing.py invalid state")
|
||||
|
||||
def shutdown_session(self, sid):
|
||||
self.grpc.terminate_session(sid)
|
||||
self.new_session()
|
||||
self.top.destroy()
|
||||
|
||||
def click_shutdown(self):
|
||||
"""
|
||||
if no session is currently selected create a new session else shut the selected session down
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
if self.selected and self.selected_sid is not None:
|
||||
self.shutdown_session(self.selected_sid)
|
||||
elif not self.selected and self.selected_sid is None:
|
||||
self.new_session()
|
||||
else:
|
||||
logging.error("querysessiondrawing.py invalid state")
|
||||
# if self.selected and self.selected_sid is not None:
|
||||
|
||||
def draw_buttons(self):
|
||||
f = tk.Frame(self.top)
|
||||
f.grid(row=3, sticky=tk.W)
|
||||
|
||||
b = tk.Button(
|
||||
f,
|
||||
image=Images.get(ImageEnum.DOCUMENTNEW.value),
|
||||
text="New",
|
||||
compound=tk.LEFT,
|
||||
command=self.new_session,
|
||||
)
|
||||
b.pack(side=tk.LEFT, padx=3, pady=4)
|
||||
b = tk.Button(
|
||||
f,
|
||||
image=Images.get(ImageEnum.FILEOPEN.value),
|
||||
text="Connect",
|
||||
compound=tk.LEFT,
|
||||
command=self.click_connect,
|
||||
)
|
||||
b.pack(side=tk.LEFT, padx=3, pady=4)
|
||||
b = tk.Button(
|
||||
f,
|
||||
image=Images.get(ImageEnum.EDITDELETE.value),
|
||||
text="Shutdown",
|
||||
compound=tk.LEFT,
|
||||
command=self.click_shutdown,
|
||||
)
|
||||
b.pack(side=tk.LEFT, padx=3, pady=4)
|
||||
b = tk.Button(f, text="Cancel", command=self.new_session)
|
||||
b.pack(side=tk.LEFT, padx=3, pady=4)
|
||||
|
||||
def center(self):
|
||||
window_width = self.master.winfo_width()
|
||||
window_height = self.master.winfo_height()
|
||||
self.top.update()
|
||||
top_level_width = self.top.winfo_width()
|
||||
top_level_height = self.top.winfo_height()
|
||||
x = window_width / 2 - top_level_width / 2
|
||||
y = window_height / 2 - top_level_height / 2
|
||||
|
||||
self.top.geometry("+%d+%d" % (x, y))
|
||||
|
||||
def draw(self):
|
||||
self.column_definition()
|
||||
self.heading_definition()
|
||||
self.session_definition()
|
||||
self.draw_buttons()
|
||||
self.center()
|
||||
self.top.wait_window()
|
377
coretk/coretk/setwallpaper.py
Normal file
|
@ -0,0 +1,377 @@
|
|||
"""
|
||||
set wallpaper
|
||||
"""
|
||||
import enum
|
||||
import logging
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog
|
||||
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
WALLPAPER_DIR = os.path.join(PATH, "wallpaper")
|
||||
|
||||
|
||||
class ScaleOption(enum.Enum):
|
||||
NONE = 0
|
||||
UPPER_LEFT = 1
|
||||
CENTERED = 2
|
||||
SCALED = 3
|
||||
TILED = 4
|
||||
|
||||
|
||||
class CanvasWallpaper:
|
||||
def __init__(self, application):
|
||||
"""
|
||||
create an instance of CanvasWallpaper object
|
||||
|
||||
:param coretk.app.Application application: root application
|
||||
"""
|
||||
self.application = application
|
||||
self.canvas = self.application.canvas
|
||||
|
||||
self.top = tk.Toplevel()
|
||||
self.top.title("Set Canvas Wallpaper")
|
||||
self.radiovar = tk.IntVar()
|
||||
print(self.application.radiovar.get())
|
||||
self.radiovar.set(self.application.radiovar.get())
|
||||
self.show_grid_var = tk.IntVar()
|
||||
self.show_grid_var.set(self.application.show_grid_var.get())
|
||||
self.adjust_to_dim_var = tk.IntVar()
|
||||
self.adjust_to_dim_var.set(self.application.adjust_to_dim_var.get())
|
||||
|
||||
self.create_image_label()
|
||||
self.create_text_label()
|
||||
self.open_image()
|
||||
self.display_options()
|
||||
self.additional_options()
|
||||
self.apply_cancel()
|
||||
|
||||
def create_image_label(self):
|
||||
image_label = tk.Label(
|
||||
self.top, text="(image preview)", height=8, width=32, bg="white"
|
||||
)
|
||||
image_label.grid(pady=5)
|
||||
|
||||
def create_text_label(self):
|
||||
text_label = tk.Label(self.top, text="Image filename: ")
|
||||
text_label.grid()
|
||||
|
||||
def open_image_link(self):
|
||||
filename = filedialog.askopenfilename(
|
||||
initialdir=WALLPAPER_DIR,
|
||||
title="Open",
|
||||
filetypes=(
|
||||
("images", "*.gif *.jpg *.png *.bmp *pcx *.tga ..."),
|
||||
("All Files", "*"),
|
||||
),
|
||||
)
|
||||
|
||||
# fill the file name into the file name entry
|
||||
img_open_frame = self.top.grid_slaves(2, 0)[0]
|
||||
filename_entry = img_open_frame.grid_slaves(0, 0)[0]
|
||||
filename_entry.delete(0, tk.END)
|
||||
filename_entry.insert(tk.END, filename)
|
||||
|
||||
# display that onto the label
|
||||
img_label = self.top.grid_slaves(0, 0)[0]
|
||||
if filename:
|
||||
img = Image.open(filename)
|
||||
img = img.resize((250, 135), Image.ANTIALIAS)
|
||||
tk_img = ImageTk.PhotoImage(img)
|
||||
img_label.config(image=tk_img, width=250, height=135)
|
||||
img_label.image = tk_img
|
||||
|
||||
def clear_link(self):
|
||||
"""
|
||||
delete like shown in image link entry if there is any
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
# delete entry
|
||||
img_open_frame = self.top.grid_slaves(2, 0)[0]
|
||||
filename_entry = img_open_frame.grid_slaves(0, 0)[0]
|
||||
filename_entry.delete(0, tk.END)
|
||||
|
||||
# delete display image
|
||||
img_label = self.top.grid_slaves(0, 0)[0]
|
||||
img_label.config(image="", width=32, height=8)
|
||||
|
||||
def open_image(self):
|
||||
f = tk.Frame(self.top)
|
||||
|
||||
var = tk.StringVar(f, value="")
|
||||
e = tk.Entry(f, textvariable=var)
|
||||
e.focus()
|
||||
e.grid()
|
||||
|
||||
b = tk.Button(f, text="...", command=self.open_image_link)
|
||||
b.grid(row=0, column=1)
|
||||
|
||||
b = tk.Button(f, text="Clear", command=self.clear_link)
|
||||
b.grid(row=0, column=2)
|
||||
|
||||
f.grid()
|
||||
|
||||
def display_options(self):
|
||||
f = tk.Frame(self.top)
|
||||
|
||||
b1 = tk.Radiobutton(f, text="upper-left", value=1, variable=self.radiovar)
|
||||
b1.grid(row=0, column=0)
|
||||
|
||||
b2 = tk.Radiobutton(f, text="centered", value=2, variable=self.radiovar)
|
||||
b2.grid(row=0, column=1)
|
||||
|
||||
b3 = tk.Radiobutton(f, text="scaled", value=3, variable=self.radiovar)
|
||||
b3.grid(row=0, column=2)
|
||||
|
||||
b4 = tk.Radiobutton(f, text="titled", value=4, variable=self.radiovar)
|
||||
b4.grid(row=0, column=3)
|
||||
|
||||
# self.radiovar.set(1)
|
||||
|
||||
f.grid()
|
||||
|
||||
def adjust_canvas_size(self):
|
||||
|
||||
# deselect all radio buttons and grey them out
|
||||
if self.adjust_to_dim_var.get() == 1:
|
||||
self.radiovar.set(0)
|
||||
option_frame = self.top.grid_slaves(3, 0)[0]
|
||||
for i in option_frame.grid_slaves():
|
||||
i.config(state=tk.DISABLED)
|
||||
|
||||
# turn back the radio button to active state so that user can choose again
|
||||
elif self.adjust_to_dim_var.get() == 0:
|
||||
option_frame = self.top.grid_slaves(3, 0)[0]
|
||||
for i in option_frame.grid_slaves():
|
||||
i.config(state=tk.NORMAL)
|
||||
self.radiovar.set(1)
|
||||
else:
|
||||
logging.error("setwallpaper.py adjust_canvas_size invalid value")
|
||||
|
||||
def additional_options(self):
|
||||
b = tk.Checkbutton(self.top, text="Show grid", variable=self.show_grid_var)
|
||||
b.grid(sticky=tk.W, padx=5)
|
||||
b = tk.Checkbutton(
|
||||
self.top,
|
||||
text="Adjust canvas size to image dimensions",
|
||||
variable=self.adjust_to_dim_var,
|
||||
command=self.adjust_canvas_size,
|
||||
)
|
||||
b.grid(sticky=tk.W, padx=5)
|
||||
self.show_grid_var.set(1)
|
||||
self.adjust_to_dim_var.set(0)
|
||||
|
||||
def delete_canvas_components(self, tag_list):
|
||||
"""
|
||||
delete canvas items whose tag is in the tag list
|
||||
|
||||
:param list[string] tag_list: list of tags
|
||||
:return: nothing
|
||||
"""
|
||||
for tag in tag_list:
|
||||
for i in self.canvas.find_withtag(tag):
|
||||
self.canvas.delete(i)
|
||||
|
||||
def get_canvas_width_and_height(self):
|
||||
"""
|
||||
retrieve canvas width and height in pixels
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
canvas = self.application.canvas
|
||||
grid = canvas.find_withtag("rectangle")[0]
|
||||
x0, y0, x1, y1 = canvas.coords(grid)
|
||||
canvas_w = abs(x0 - x1)
|
||||
canvas_h = abs(y0 - y1)
|
||||
return canvas_w, canvas_h
|
||||
|
||||
def determine_cropped_image_dimension(self):
|
||||
"""
|
||||
determine the dimension of the image after being cropped
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
return
|
||||
|
||||
def upper_left(self, img):
|
||||
print("upperleft")
|
||||
tk_img = ImageTk.PhotoImage(img)
|
||||
|
||||
# crop image if it is bigger than canvas
|
||||
canvas_w, canvas_h = self.get_canvas_width_and_height()
|
||||
|
||||
cropx = img_w = tk_img.width()
|
||||
cropy = img_h = tk_img.height()
|
||||
|
||||
if img_w > canvas_w:
|
||||
|
||||
cropx -= img_w - canvas_w
|
||||
if img_h > canvas_h:
|
||||
cropy -= img_h - canvas_h
|
||||
cropped = img.crop((0, 0, cropx, cropy))
|
||||
cropped_tk = ImageTk.PhotoImage(cropped)
|
||||
|
||||
# place left corner of image to the left corner of the canvas
|
||||
self.application.croppedwallpaper = cropped_tk
|
||||
|
||||
self.delete_canvas_components(["wallpaper"])
|
||||
# self.delete_previous_wallpaper()
|
||||
|
||||
wid = self.canvas.create_image(
|
||||
(cropx / 2, cropy / 2), image=cropped_tk, tags="wallpaper"
|
||||
)
|
||||
self.application.wallpaper_id = wid
|
||||
|
||||
def center(self, img):
|
||||
"""
|
||||
place the image at the center of canvas
|
||||
|
||||
:param Image img: image object
|
||||
:return: nothing
|
||||
"""
|
||||
tk_img = ImageTk.PhotoImage(img)
|
||||
canvas_w, canvas_h = self.get_canvas_width_and_height()
|
||||
|
||||
cropx = img_w = tk_img.width()
|
||||
cropy = img_h = tk_img.height()
|
||||
|
||||
# dimension of the cropped image
|
||||
if img_w > canvas_w:
|
||||
cropx -= img_w - canvas_w
|
||||
if img_h > canvas_h:
|
||||
cropy -= img_h - canvas_h
|
||||
|
||||
x0 = (img_w - cropx) / 2
|
||||
y0 = (img_h - cropy) / 2
|
||||
x1 = x0 + cropx
|
||||
y1 = y0 + cropy
|
||||
cropped = img.crop((x0, y0, x1, y1))
|
||||
cropped_tk = ImageTk.PhotoImage(cropped)
|
||||
|
||||
# place the center of the image at the center of the canvas
|
||||
self.application.croppedwallpaper = cropped_tk
|
||||
self.delete_canvas_components(["wallpaper"])
|
||||
# self.delete_previous_wallpaper()
|
||||
wid = self.canvas.create_image(
|
||||
(canvas_w / 2, canvas_h / 2), image=cropped_tk, tags="wallpaper"
|
||||
)
|
||||
self.application.wallpaper_id = wid
|
||||
|
||||
def scaled(self, img):
|
||||
"""
|
||||
scale image based on canvas dimension
|
||||
|
||||
:param Image img: image object
|
||||
:return: nothing
|
||||
"""
|
||||
canvas_w, canvas_h = self.get_canvas_width_and_height()
|
||||
resized_image = img.resize((int(canvas_w), int(canvas_h)), Image.ANTIALIAS)
|
||||
image_tk = ImageTk.PhotoImage(resized_image)
|
||||
self.application.croppedwallpaper = image_tk
|
||||
|
||||
self.delete_canvas_components(["wallpaper"])
|
||||
# self.delete_previous_wallpaper()
|
||||
|
||||
wid = self.canvas.create_image(
|
||||
(canvas_w / 2, canvas_h / 2), image=image_tk, tags="wallpaper"
|
||||
)
|
||||
self.application.wallpaper_id = wid
|
||||
|
||||
def tiled(self, img):
|
||||
return
|
||||
|
||||
def draw_new_canvas(self, canvas_width, canvas_height):
|
||||
"""
|
||||
delete the old canvas and draw a new one
|
||||
|
||||
:param int canvas_width: canvas width in pixel
|
||||
:param int canvas_height: canvas height in pixel
|
||||
:return:
|
||||
"""
|
||||
self.delete_canvas_components(["rectangle", "gridline"])
|
||||
self.canvas.draw_grid(canvas_width, canvas_height)
|
||||
|
||||
def canvas_to_image_dimension(self, img):
|
||||
image_tk = ImageTk.PhotoImage(img)
|
||||
img_w = image_tk.width()
|
||||
img_h = image_tk.height()
|
||||
self.delete_canvas_components(["wallpaper"])
|
||||
self.draw_new_canvas(img_w, img_h)
|
||||
wid = self.canvas.create_image((img_w / 2, img_h / 2), image=image_tk)
|
||||
self.application.croppedwallpaper = image_tk
|
||||
self.application.wallpaper_id = wid
|
||||
|
||||
def show_grid(self):
|
||||
"""
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
|
||||
|
||||
if self.show_grid_var.get() == 0:
|
||||
for i in self.canvas.find_withtag("gridline"):
|
||||
self.canvas.itemconfig(i, state=tk.HIDDEN)
|
||||
elif self.show_grid_var.get() == 1:
|
||||
for i in self.canvas.find_withtag("gridline"):
|
||||
self.canvas.itemconfig(i, state=tk.NORMAL)
|
||||
self.canvas.lift(i)
|
||||
else:
|
||||
logging.error("setwallpaper.py show_grid invalid value")
|
||||
|
||||
def save_wallpaper_options(self):
|
||||
self.application.radiovar.set(self.radiovar.get())
|
||||
self.application.show_grid_var.set(self.show_grid_var.get())
|
||||
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
|
||||
|
||||
def click_apply(self):
|
||||
img_link_frame = self.top.grid_slaves(2, 0)[0]
|
||||
filename = img_link_frame.grid_slaves(0, 0)[0].get()
|
||||
if not filename:
|
||||
self.delete_canvas_components(["wallpaper"])
|
||||
self.top.destroy()
|
||||
self.application.current_wallpaper = None
|
||||
self.save_wallpaper_options()
|
||||
return
|
||||
try:
|
||||
img = Image.open(filename)
|
||||
self.application.current_wallpaper = img
|
||||
except FileNotFoundError:
|
||||
print("invalid filename, draw original white plot")
|
||||
if self.application.wallpaper_id:
|
||||
self.canvas.delete(self.application.wallpaper_id)
|
||||
self.top.destroy()
|
||||
return
|
||||
|
||||
self.application.adjust_to_dim_var.set(self.adjust_to_dim_var.get())
|
||||
if self.adjust_to_dim_var.get() == 0:
|
||||
|
||||
self.application.radiovar.set(self.radiovar.get())
|
||||
|
||||
if self.radiovar.get() == ScaleOption.UPPER_LEFT.value:
|
||||
self.upper_left(img)
|
||||
elif self.radiovar.get() == ScaleOption.CENTERED.value:
|
||||
self.center(img)
|
||||
elif self.radiovar.get() == ScaleOption.SCALED.value:
|
||||
self.scaled(img)
|
||||
elif self.radiovar.get() == ScaleOption.TILED.value:
|
||||
print("not implemented yet")
|
||||
|
||||
elif self.adjust_to_dim_var.get() == 1:
|
||||
self.canvas_to_image_dimension(img)
|
||||
|
||||
self.show_grid()
|
||||
self.top.destroy()
|
||||
|
||||
def apply_cancel(self):
|
||||
f = tk.Frame(self.top)
|
||||
|
||||
b = tk.Button(f, text="Apply", command=self.click_apply)
|
||||
b.grid(row=0, column=0)
|
||||
|
||||
b = tk.Button(f, text="Cancel", command=self.top.destroy)
|
||||
b.grid(row=0, column=1)
|
||||
|
||||
f.grid(pady=5)
|
277
coretk/coretk/sizeandscale.py
Normal file
|
@ -0,0 +1,277 @@
|
|||
"""
|
||||
size and scale
|
||||
"""
|
||||
import tkinter as tk
|
||||
from functools import partial
|
||||
|
||||
from coretk.setwallpaper import ScaleOption
|
||||
|
||||
DRAW_OBJECT_TAGS = ["edge", "node", "nodename", "linkinfo", "antenna"]
|
||||
|
||||
|
||||
class SizeAndScale:
|
||||
def __init__(self, application):
|
||||
"""
|
||||
create an instance for size and scale object
|
||||
|
||||
:param application: main application
|
||||
"""
|
||||
self.application = application
|
||||
self.top = tk.Toplevel()
|
||||
self.top.title("Canvas Size and Scale")
|
||||
self.meter_per_pixel = self.application.canvas.meters_per_pixel
|
||||
|
||||
self.size_chart()
|
||||
self.scale_chart()
|
||||
self.reference_point_chart()
|
||||
self.save_as_default()
|
||||
self.apply_cancel()
|
||||
|
||||
def pixel_scrollbar_command(self, size_frame, entry_row, entry_column, event):
|
||||
"""
|
||||
change the value shown based on scrollbar action
|
||||
|
||||
:param tkinter.Frame frame: pixel dimension frame
|
||||
:param int entry_row: row number of entry of the frame
|
||||
:param int entry_column: column number of entry of the frame
|
||||
:param event: scrollbar event
|
||||
:return: nothing
|
||||
"""
|
||||
pixel_frame = size_frame.grid_slaves(0, 0)[0]
|
||||
pixel_entry = pixel_frame.grid_slaves(entry_row, entry_column)[0]
|
||||
val = int(pixel_entry.get())
|
||||
|
||||
if event == "-1":
|
||||
new_val = val + 2
|
||||
elif event == "1":
|
||||
new_val = val - 2
|
||||
|
||||
pixel_entry.delete(0, tk.END)
|
||||
pixel_entry.insert(tk.END, str(new_val))
|
||||
|
||||
# change meter dimension
|
||||
meter_frame = size_frame.grid_slaves(1, 0)[0]
|
||||
meter_entry = meter_frame.grid_slaves(entry_row, entry_column)[0]
|
||||
meter_entry.delete(0, tk.END)
|
||||
meter_entry.insert(tk.END, str(new_val * self.meter_per_pixel))
|
||||
|
||||
def meter_scrollbar_command(self, size_frame, entry_row, entry_column, event):
|
||||
"""
|
||||
change the value shown based on scrollbar action
|
||||
|
||||
:param tkinter.Frame size_frame: size frame
|
||||
:param int entry_row: row number of entry in the frame it is contained in
|
||||
:param int entry_column: column number of entry in the frame in is contained in
|
||||
:param event: scroolbar event
|
||||
:return: nothing
|
||||
"""
|
||||
meter_frame = size_frame.grid_slaves(1, 0)[0]
|
||||
meter_entry = meter_frame.grid_slaves(entry_row, entry_column)[0]
|
||||
val = float(meter_entry.get())
|
||||
|
||||
if event == "-1":
|
||||
val += 100.0
|
||||
elif event == "1":
|
||||
val -= 100.0
|
||||
meter_entry.delete(0, tk.END)
|
||||
meter_entry.insert(tk.END, str(val))
|
||||
|
||||
# change pixel dimension
|
||||
pixel_frame = size_frame.grid_slaves(0, 0)[0]
|
||||
pixel_entry = pixel_frame.grid_slaves(entry_row, entry_column)[0]
|
||||
pixel_entry.delete(0, tk.END)
|
||||
pixel_entry.insert(tk.END, str(int(val / self.meter_per_pixel)))
|
||||
|
||||
def create_text_label(self, frame, text, row, column, sticky=None):
|
||||
"""
|
||||
create text label
|
||||
:param tkinter.Frame frame: parent frame
|
||||
:param str text: label text
|
||||
:param int row: row number
|
||||
:param int column: column number
|
||||
:param sticky: sticky value
|
||||
|
||||
:return: nothing
|
||||
"""
|
||||
text_label = tk.Label(frame, text=text)
|
||||
text_label.grid(row=row, column=column, sticky=sticky, padx=3, pady=3)
|
||||
|
||||
def create_entry(self, frame, default_value, row, column, width):
|
||||
text_var = tk.StringVar(frame, value=str(default_value))
|
||||
entry = tk.Entry(
|
||||
frame, textvariable=text_var, width=width, bg="white", state=tk.NORMAL
|
||||
)
|
||||
entry.focus()
|
||||
entry.grid(row=row, column=column, padx=3, pady=3)
|
||||
|
||||
def size_chart(self):
|
||||
label = tk.Label(self.top, text="Size")
|
||||
label.grid(sticky=tk.W, padx=5)
|
||||
|
||||
canvas = self.application.canvas
|
||||
plot = canvas.find_withtag("rectangle")
|
||||
x0, y0, x1, y1 = canvas.bbox(plot[0])
|
||||
w = abs(x0 - x1) - 2
|
||||
h = abs(y0 - y1) - 2
|
||||
|
||||
f = tk.Frame(
|
||||
self.top,
|
||||
highlightbackground="#b3b3b3",
|
||||
highlightcolor="#b3b3b3",
|
||||
highlightthickness=0.5,
|
||||
bd=0,
|
||||
)
|
||||
|
||||
f1 = tk.Frame(f)
|
||||
pw_scrollbar = tk.Scrollbar(f1, orient=tk.VERTICAL)
|
||||
pw_scrollbar.grid(row=0, column=1)
|
||||
self.create_entry(f1, w, 0, 0, 6)
|
||||
pw_scrollbar.config(command=partial(self.pixel_scrollbar_command, f, 0, 0))
|
||||
|
||||
self.create_text_label(f1, " W x ", 0, 2)
|
||||
|
||||
scrollbar = tk.Scrollbar(f1, orient=tk.VERTICAL)
|
||||
scrollbar.grid(row=0, column=4)
|
||||
self.create_entry(f1, h, 0, 3, 6)
|
||||
scrollbar.config(command=partial(self.pixel_scrollbar_command, f, 0, 3))
|
||||
self.create_text_label(f1, " H pixels ", 0, 7)
|
||||
f1.grid(sticky=tk.W, pady=3)
|
||||
|
||||
f2 = tk.Frame(f)
|
||||
scrollbar = tk.Scrollbar(f2, orient=tk.VERTICAL)
|
||||
scrollbar.grid(row=0, column=1)
|
||||
self.create_entry(f2, w * self.meter_per_pixel, 0, 0, 8)
|
||||
scrollbar.config(command=partial(self.meter_scrollbar_command, f, 0, 0))
|
||||
self.create_text_label(f2, " x ", 0, 2)
|
||||
|
||||
scrollbar = tk.Scrollbar(f2, orient=tk.VERTICAL)
|
||||
scrollbar.grid(row=0, column=4)
|
||||
self.create_entry(f2, h * self.meter_per_pixel, 0, 3, 8)
|
||||
scrollbar.config(command=partial(self.meter_scrollbar_command, f, 0, 3))
|
||||
self.create_text_label(f2, " meters ", 0, 5)
|
||||
|
||||
f2.grid(sticky=tk.W, pady=3)
|
||||
|
||||
f.grid(sticky=tk.W + tk.E, padx=5, pady=5, columnspan=2)
|
||||
|
||||
def scale_chart(self):
|
||||
label = tk.Label(self.top, text="Scale")
|
||||
label.grid(padx=5, sticky=tk.W)
|
||||
f = tk.Frame(
|
||||
self.top,
|
||||
highlightbackground="#b3b3b3",
|
||||
highlightcolor="#b3b3b3",
|
||||
highlightthickness=0.5,
|
||||
bd=0,
|
||||
)
|
||||
# self.create_text_label(f, "Scale", 0, 0, tk.W)
|
||||
# f1 = tk.Frame(f)
|
||||
self.create_text_label(f, "100 pixels = ", 0, 0)
|
||||
self.create_entry(f, self.meter_per_pixel * 100, 0, 1, 10)
|
||||
self.create_text_label(f, "meters", 0, 2)
|
||||
# f1.grid(sticky=tk.W, pady=3)
|
||||
f.grid(sticky=tk.W + tk.E, padx=5, pady=5, columnspan=2)
|
||||
|
||||
def reference_point_chart(self):
|
||||
label = tk.Label(self.top, text="Reference point")
|
||||
label.grid(padx=5, sticky=tk.W)
|
||||
|
||||
f = tk.Frame(
|
||||
self.top,
|
||||
highlightbackground="#b3b3b3",
|
||||
highlightcolor="#b3b3b3",
|
||||
highlightthickness=0.5,
|
||||
bd=0,
|
||||
)
|
||||
self.create_text_label(
|
||||
f,
|
||||
"The default reference point is (0, 0), the upper left corner of the canvas.",
|
||||
1,
|
||||
0,
|
||||
tk.W,
|
||||
)
|
||||
f1 = tk.Frame(f)
|
||||
self.create_entry(f1, 0, 0, 0, 4)
|
||||
self.create_text_label(f1, " X, ", 0, 1)
|
||||
self.create_entry(f1, 0, 0, 2, 4)
|
||||
self.create_text_label(f1, "Y = ", 0, 3)
|
||||
self.create_entry(f1, 47.5791667, 0, 4, 13)
|
||||
self.create_text_label(f1, " lat, ", 0, 5)
|
||||
self.create_entry(f1, -122.132322, 0, 6, 13)
|
||||
self.create_text_label(f1, "long", 0, 7)
|
||||
f1.grid(row=2, column=0, sticky=tk.W, pady=3)
|
||||
|
||||
f2 = tk.Frame(f)
|
||||
self.create_text_label(f2, "Altitude: ", 0, 0)
|
||||
self.create_entry(f2, 2.0, 0, 1, 11)
|
||||
self.create_text_label(f2, " meters ", 0, 2)
|
||||
f2.grid(row=3, column=0, sticky=tk.W, pady=3)
|
||||
|
||||
f.grid(sticky=tk.W, padx=5, pady=5, columnspan=2)
|
||||
|
||||
def save_as_default(self):
|
||||
var = tk.IntVar()
|
||||
button = tk.Checkbutton(self.top, text="Save as default", variable=var)
|
||||
button.grid(sticky=tk.W, padx=5, pady=5, columnspan=2)
|
||||
|
||||
def redraw_grid(self, pixel_width, pixel_height):
|
||||
"""
|
||||
redraw grid with new dimension
|
||||
|
||||
:param int pixel_width: width in pixel
|
||||
:param int pixel_height: height in pixel
|
||||
:return: nothing
|
||||
"""
|
||||
canvas = self.application.canvas
|
||||
canvas.config(scrollregion=(0, 0, pixel_width + 200, pixel_height + 200))
|
||||
|
||||
# delete old plot and redraw
|
||||
for i in canvas.find_withtag("gridline"):
|
||||
canvas.delete(i)
|
||||
for i in canvas.find_withtag("rectangle"):
|
||||
canvas.delete(i)
|
||||
|
||||
canvas.draw_grid(width=pixel_width, height=pixel_height)
|
||||
# lift anything that is drawn on the plot before
|
||||
for tag in DRAW_OBJECT_TAGS:
|
||||
for i in canvas.find_withtag(tag):
|
||||
canvas.lift(i)
|
||||
|
||||
def click_apply(self):
|
||||
size_frame = self.top.grid_slaves(1, 0)[0]
|
||||
pixel_size_frame = size_frame.grid_slaves(0, 0)[0]
|
||||
|
||||
pixel_width = int(pixel_size_frame.grid_slaves(0, 0)[0].get())
|
||||
pixel_height = int(pixel_size_frame.grid_slaves(0, 3)[0].get())
|
||||
|
||||
scale_frame = self.top.grid_slaves(3, 0)[0]
|
||||
meter_per_pixel = float(scale_frame.grid_slaves(0, 1)[0].get()) / 100
|
||||
self.application.canvas.meters_per_pixel = meter_per_pixel
|
||||
self.redraw_grid(pixel_width, pixel_height)
|
||||
print(self.application.current_wallpaper)
|
||||
print(self.application.radiovar)
|
||||
# if there is a current wallpaper showing, redraw it based on current wallpaper options
|
||||
wallpaper_tool = self.application.set_wallpaper
|
||||
current_wallpaper = self.application.current_wallpaper
|
||||
if current_wallpaper:
|
||||
if self.application.adjust_to_dim_var.get() == 0:
|
||||
if self.application.radiovar.get() == ScaleOption.UPPER_LEFT.value:
|
||||
wallpaper_tool.upper_left(current_wallpaper)
|
||||
elif self.application.radiovar.get() == ScaleOption.CENTERED.value:
|
||||
wallpaper_tool.center(current_wallpaper)
|
||||
elif self.application.radiovar.get() == ScaleOption.SCALED.value:
|
||||
wallpaper_tool.scaled(current_wallpaper)
|
||||
elif self.application.radiovar.get() == ScaleOption.TILED.value:
|
||||
print("not implemented")
|
||||
elif self.application.adjust_to_dim_var.get() == 1:
|
||||
wallpaper_tool.canvas_to_image_dimension(current_wallpaper)
|
||||
|
||||
wallpaper_tool.show_grid()
|
||||
|
||||
self.top.destroy()
|
||||
|
||||
def apply_cancel(self):
|
||||
apply_button = tk.Button(self.top, text="Apply", command=self.click_apply)
|
||||
apply_button.grid(row=7, column=0, pady=5)
|
||||
cancel_button = tk.Button(self.top, text="Cancel", command=self.top.destroy)
|
||||
cancel_button.grid(row=7, column=1, pady=5)
|
BIN
coretk/coretk/wallpaper/sample1-bg.gif
Normal file
After Width: | Height: | Size: 312 KiB |
BIN
coretk/coretk/wallpaper/sample4-bg.jpg
Normal file
After Width: | Height: | Size: 196 KiB |
53
coretk/coretk/wirelessconnection.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
"""
|
||||
Wireless connection handler
|
||||
"""
|
||||
from core.api.grpc import core_pb2
|
||||
|
||||
|
||||
class WirelessConnection:
|
||||
def __init__(self, canvas, grpc):
|
||||
self.canvas = canvas
|
||||
self.core_grpc = grpc
|
||||
self.core_mapping = canvas.grpc_manager.core_mapping
|
||||
|
||||
# map a (node_one_id, node_two_id) to a wlan canvas id
|
||||
self.map = {}
|
||||
|
||||
def add_wlan_connection(self, node_one_id, node_two_id):
|
||||
canvas_id_one = self.core_mapping.get_canvas_id_from_core_id(node_one_id)
|
||||
canvas_id_two = self.core_mapping.get_canvas_id_from_core_id(node_two_id)
|
||||
key = tuple(sorted((node_one_id, node_two_id)))
|
||||
|
||||
if key not in self.map:
|
||||
x1, y1 = self.canvas.coords(canvas_id_one)
|
||||
x2, y2 = self.canvas.coords(canvas_id_two)
|
||||
wlan_canvas_id = self.canvas.create_line(
|
||||
x1, y1, x2, y2, fill="#009933", tags="wlan", width=1.5
|
||||
)
|
||||
self.map[key] = wlan_canvas_id
|
||||
self.canvas.nodes[canvas_id_one].wlans.append(wlan_canvas_id)
|
||||
self.canvas.nodes[canvas_id_two].wlans.append(wlan_canvas_id)
|
||||
|
||||
def delete_wlan_connection(self, node_one_id, node_two_id):
|
||||
canvas_id_one = self.core_mapping.get_canvas_id_from_core_id(node_one_id)
|
||||
canvas_id_two = self.core_mapping.get_canvas_id_from_core_id(node_two_id)
|
||||
|
||||
key = tuple(sorted((node_one_id, node_two_id)))
|
||||
wlan_canvas_id = self.map[key]
|
||||
|
||||
self.canvas.nodes[canvas_id_one].wlans.remove(wlan_canvas_id)
|
||||
self.canvas.nodes[canvas_id_two].wlans.remove(wlan_canvas_id)
|
||||
|
||||
self.canvas.delete(wlan_canvas_id)
|
||||
self.map.pop(key, None)
|
||||
|
||||
def hangle_link_event(self, link_event):
|
||||
if link_event.message_type == core_pb2.MessageType.ADD:
|
||||
self.add_wlan_connection(
|
||||
link_event.link.node_one_id, link_event.link.node_two_id
|
||||
)
|
||||
|
||||
if link_event.message_type == core_pb2.MessageType.DELETE:
|
||||
self.delete_wlan_connection(
|
||||
link_event.link.node_one_id, link_event.link.node_two_id
|
||||
)
|
|
@ -194,7 +194,7 @@ class CoreGrpcClient(object):
|
|||
|
||||
def get_session_options(self, session_id):
|
||||
"""
|
||||
Retrieve session options.
|
||||
Retrieve session options as a dict with id mapping.
|
||||
|
||||
:param int session_id: id of session
|
||||
:return: response with a list of configuration groups
|
||||
|
@ -204,6 +204,18 @@ class CoreGrpcClient(object):
|
|||
request = core_pb2.GetSessionOptionsRequest(session_id=session_id)
|
||||
return self.stub.GetSessionOptions(request)
|
||||
|
||||
def get_session_options_group(self, session_id):
|
||||
"""
|
||||
Retrieve session options in a group list.
|
||||
|
||||
:param int session_id: id of session
|
||||
:return: response with a list of configuration groups
|
||||
:rtype: core_pb2.GetSessionOptionsGroupResponse
|
||||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetSessionOptionsGroupRequest(session_id=session_id)
|
||||
return self.stub.GetSessionOptionsGroup(request)
|
||||
|
||||
def set_session_options(self, session_id, config):
|
||||
"""
|
||||
Set options for a session.
|
||||
|
@ -868,17 +880,18 @@ class CoreGrpcClient(object):
|
|||
with open(file_path, "w") as xml_file:
|
||||
xml_file.write(response.data)
|
||||
|
||||
def open_xml(self, file_path):
|
||||
def open_xml(self, file_path, start=False):
|
||||
"""
|
||||
Load a local scenario XML file to open as a new session.
|
||||
|
||||
:param str file_path: path of scenario XML file
|
||||
:param bool start: True to start session, False otherwise
|
||||
:return: response with opened session id
|
||||
:rtype: core_pb2.OpenXmlResponse
|
||||
"""
|
||||
with open(file_path, "r") as xml_file:
|
||||
data = xml_file.read()
|
||||
request = core_pb2.OpenXmlRequest(data=data)
|
||||
request = core_pb2.OpenXmlRequest(data=data, start=start, file=file_path)
|
||||
return self.stub.OpenXml(request)
|
||||
|
||||
def emane_link(self, session_id, nem_one, nem_two, linked):
|
||||
|
|
|
@ -46,38 +46,33 @@ def convert_value(value):
|
|||
return value
|
||||
|
||||
|
||||
def get_config_groups(config, configurable_options):
|
||||
def get_config_options(config, configurable_options):
|
||||
"""
|
||||
Retrieve configuration groups in a form that is used by the grpc server
|
||||
Retrieve configuration options in a form that is used by the grpc server.
|
||||
|
||||
:param core.config.Configuration config: configuration
|
||||
:param dict config: configuration
|
||||
:param core.config.ConfigurableOptions configurable_options: configurable options
|
||||
:return: list of configuration groups
|
||||
:rtype: [core.api.grpc.core_pb2.ConfigGroup]
|
||||
:return: mapping of configuration ids to configuration options
|
||||
:rtype: dict[str,core.api.grpc.core_pb2.ConfigOption]
|
||||
"""
|
||||
groups = []
|
||||
config_options = []
|
||||
|
||||
results = {}
|
||||
for configuration in configurable_options.configurations():
|
||||
value = config[configuration.id]
|
||||
config_option = core_pb2.ConfigOption()
|
||||
config_option.label = configuration.label
|
||||
config_option.name = configuration.id
|
||||
config_option.value = value
|
||||
config_option.type = configuration.type.value
|
||||
config_option.select.extend(configuration.options)
|
||||
config_options.append(config_option)
|
||||
|
||||
config_option = core_pb2.ConfigOption(
|
||||
label=configuration.label,
|
||||
name=configuration.id,
|
||||
value=value,
|
||||
type=configuration.type.value,
|
||||
select=configuration.options,
|
||||
)
|
||||
results[configuration.id] = config_option
|
||||
for config_group in configurable_options.config_groups():
|
||||
start = config_group.start - 1
|
||||
stop = config_group.stop
|
||||
options = config_options[start:stop]
|
||||
config_group_proto = core_pb2.ConfigGroup(
|
||||
name=config_group.name, options=options
|
||||
)
|
||||
groups.append(config_group_proto)
|
||||
|
||||
return groups
|
||||
options = list(results.values())[start:stop]
|
||||
for option in options:
|
||||
option.group = config_group.name
|
||||
return results
|
||||
|
||||
|
||||
def get_links(session, node):
|
||||
|
@ -310,7 +305,10 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
for session_id in self.coreemu.sessions:
|
||||
session = self.coreemu.sessions[session_id]
|
||||
session_summary = core_pb2.SessionSummary(
|
||||
id=session_id, state=session.state, nodes=session.get_node_count()
|
||||
id=session_id,
|
||||
state=session.state,
|
||||
nodes=session.get_node_count(),
|
||||
file=session.file_name,
|
||||
)
|
||||
sessions.append(session_summary)
|
||||
return core_pb2.GetSessionsResponse(sessions=sessions)
|
||||
|
@ -390,20 +388,21 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
|
||||
def GetSessionOptions(self, request, context):
|
||||
"""
|
||||
Retrieve session options
|
||||
Retrieve session options.
|
||||
|
||||
:param core.api.grpc.core_pb2.GetSessionOptions request: get-session-options request
|
||||
:param core.api.grpc.core_pb2.GetSessionOptions request:
|
||||
get-session-options request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-session-options response about all session's options
|
||||
:rtype: core.api.grpc.core_pb2.GetSessionOptions
|
||||
"""
|
||||
logging.debug("get session options: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.options.get_configs()
|
||||
defaults = session.options.default_values()
|
||||
defaults.update(config)
|
||||
groups = get_config_groups(defaults, session.options)
|
||||
return core_pb2.GetSessionOptionsResponse(groups=groups)
|
||||
current_config = session.options.get_configs()
|
||||
default_config = session.options.default_values()
|
||||
default_config.update(current_config)
|
||||
config = get_config_options(default_config, session.options)
|
||||
return core_pb2.GetSessionOptionsResponse(config=config)
|
||||
|
||||
def SetSessionOptions(self, request, context):
|
||||
"""
|
||||
|
@ -1115,7 +1114,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve all mobility configurations from a session
|
||||
|
||||
:param core.api.grpc.core_pb2.GetMobilityConfigsRequest request: get-mobility-configurations request
|
||||
:param core.api.grpc.core_pb2.GetMobilityConfigsRequest request:
|
||||
get-mobility-configurations request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-mobility-configurations response that has a list of configurations
|
||||
:rtype: core.api.grpc.core_pb2.GetMobilityConfigsResponse
|
||||
|
@ -1130,33 +1130,36 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
for model_name in model_config:
|
||||
if model_name != Ns2ScriptedMobility.name:
|
||||
continue
|
||||
config = session.mobility.get_model_config(node_id, model_name)
|
||||
groups = get_config_groups(config, Ns2ScriptedMobility)
|
||||
response.configs[node_id].groups.extend(groups)
|
||||
current_config = session.mobility.get_model_config(node_id, model_name)
|
||||
config = get_config_options(current_config, Ns2ScriptedMobility)
|
||||
mapped_config = core_pb2.MappedConfig(config=config)
|
||||
response.configs[node_id].CopyFrom(mapped_config)
|
||||
return response
|
||||
|
||||
def GetMobilityConfig(self, request, context):
|
||||
"""
|
||||
Retrieve mobility configuration of a node
|
||||
|
||||
:param core.api.grpc.core_pb2.GetMobilityConfigRequest request: get-mobility-configuration request
|
||||
:param core.api.grpc.core_pb2.GetMobilityConfigRequest request:
|
||||
get-mobility-configuration request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-mobility-configuration response
|
||||
:rtype: core.api.grpc.core_pb2.GetMobilityConfigResponse
|
||||
"""
|
||||
logging.debug("get mobility config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.mobility.get_model_config(
|
||||
current_config = session.mobility.get_model_config(
|
||||
request.node_id, Ns2ScriptedMobility.name
|
||||
)
|
||||
groups = get_config_groups(config, Ns2ScriptedMobility)
|
||||
return core_pb2.GetMobilityConfigResponse(groups=groups)
|
||||
config = get_config_options(current_config, Ns2ScriptedMobility)
|
||||
return core_pb2.GetMobilityConfigResponse(config=config)
|
||||
|
||||
def SetMobilityConfig(self, request, context):
|
||||
"""
|
||||
Set mobility configuration of a node
|
||||
|
||||
:param core.api.grpc.core_pb2.SetMobilityConfigRequest request: set-mobility-configuration request
|
||||
:param core.api.grpc.core_pb2.SetMobilityConfigRequest request:
|
||||
set-mobility-configuration request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: set-mobility-configuration response
|
||||
"rtype" core.api.grpc.SetMobilityConfigResponse
|
||||
|
@ -1172,7 +1175,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Take mobility action whether to start, pause, stop or none of those
|
||||
|
||||
:param core.api.grpc.core_pb2.MobilityActionRequest request: mobility-action request
|
||||
:param core.api.grpc.core_pb2.MobilityActionRequest request: mobility-action
|
||||
request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: mobility-action response
|
||||
:rtype: core.api.grpc.core_pb2.MobilityActionResponse
|
||||
|
@ -1212,7 +1216,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve all the default services of all node types in a session
|
||||
|
||||
:param core.api.grpc.core_pb2.GetServiceDefaultsRequest request: get-default-service request
|
||||
:param core.api.grpc.core_pb2.GetServiceDefaultsRequest request:
|
||||
get-default-service request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-service-defaults response about all the available default services
|
||||
:rtype: core.api.grpc.core_pb2.GetServiceDefaultsResponse
|
||||
|
@ -1231,7 +1236,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
def SetServiceDefaults(self, request, context):
|
||||
"""
|
||||
Set new default services to the session after whipping out the old ones
|
||||
:param core.api.grpc.core_pb2.SetServiceDefaults request: set-service-defaults request
|
||||
:param core.api.grpc.core_pb2.SetServiceDefaults request: set-service-defaults
|
||||
request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: set-service-defaults response
|
||||
:rtype: core.api.grpc.core_pb2 SetServiceDefaultsResponse
|
||||
|
@ -1249,7 +1255,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve a requested service from a node
|
||||
|
||||
:param core.api.grpc.core_pb2.GetNodeServiceRequest request: get-node-service request
|
||||
:param core.api.grpc.core_pb2.GetNodeServiceRequest request: get-node-service
|
||||
request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-node-service response about the requested service
|
||||
:rtype: core.api.grpc.core_pb2.GetNodeServiceResponse
|
||||
|
@ -1277,7 +1284,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve a requested service file from a node
|
||||
|
||||
:param core.api.grpc.core_pb2.GetNodeServiceFileRequest request: get-node-service request
|
||||
:param core.api.grpc.core_pb2.GetNodeServiceFileRequest request:
|
||||
get-node-service request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-node-service response about the requested service
|
||||
:rtype: core.api.grpc.core_pb2.GetNodeServiceFileResponse
|
||||
|
@ -1301,8 +1309,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Set a node service for a node
|
||||
|
||||
:param core.api.grpc.core_pb2.SetNodeServiceRequest request: set-node-service request
|
||||
that has info to set a node service
|
||||
:param core.api.grpc.core_pb2.SetNodeServiceRequest request: set-node-service
|
||||
request that has info to set a node service
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: set-node-service response
|
||||
:rtype: core.api.grpc.core_pb2.SetNodeServiceResponse
|
||||
|
@ -1320,7 +1328,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Store the customized service file in the service config
|
||||
|
||||
:param core.api.grpc.core_pb2.SetNodeServiceFileRequest request: set-node-service-file request
|
||||
:param core.api.grpc.core_pb2.SetNodeServiceFileRequest request:
|
||||
set-node-service-file request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: set-node-service-file response
|
||||
:rtype: core.api.grpc.core_pb2.SetNodeServiceFileResponse
|
||||
|
@ -1382,11 +1391,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
logging.debug("get wlan config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.mobility.get_model_config(
|
||||
current_config = session.mobility.get_model_config(
|
||||
request.node_id, BasicRangeModel.name
|
||||
)
|
||||
groups = get_config_groups(config, BasicRangeModel)
|
||||
return core_pb2.GetWlanConfigResponse(groups=groups)
|
||||
config = get_config_options(current_config, BasicRangeModel)
|
||||
return core_pb2.GetWlanConfigResponse(config=config)
|
||||
|
||||
def SetWlanConfig(self, request, context):
|
||||
"""
|
||||
|
@ -1418,9 +1427,9 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
logging.debug("get emane config: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
config = session.emane.get_configs()
|
||||
groups = get_config_groups(config, session.emane.emane_config)
|
||||
return core_pb2.GetEmaneConfigResponse(groups=groups)
|
||||
current_config = session.emane.get_configs()
|
||||
config = get_config_options(current_config, session.emane.emane_config)
|
||||
return core_pb2.GetEmaneConfigResponse(config=config)
|
||||
|
||||
def SetEmaneConfig(self, request, context):
|
||||
"""
|
||||
|
@ -1459,7 +1468,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve EMANE model configuration of a node
|
||||
|
||||
:param core.api.grpc.core_pb2.GetEmaneModelConfigRequest request: get-EMANE-model-configuration request
|
||||
:param core.api.grpc.core_pb2.GetEmaneModelConfigRequest request:
|
||||
get-EMANE-model-configuration request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-EMANE-model-configuration response
|
||||
:rtype: core.api.grpc.core_pb2.GetEmaneModelConfigResponse
|
||||
|
@ -1468,15 +1478,16 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
session = self.get_session(request.session_id, context)
|
||||
model = session.emane.models[request.model]
|
||||
_id = get_emane_model_id(request.node_id, request.interface)
|
||||
config = session.emane.get_model_config(_id, request.model)
|
||||
groups = get_config_groups(config, model)
|
||||
return core_pb2.GetEmaneModelConfigResponse(groups=groups)
|
||||
current_config = session.emane.get_model_config(_id, request.model)
|
||||
config = get_config_options(current_config, model)
|
||||
return core_pb2.GetEmaneModelConfigResponse(config=config)
|
||||
|
||||
def SetEmaneModelConfig(self, request, context):
|
||||
"""
|
||||
Set EMANE model configuration of a node
|
||||
|
||||
:param core.api.grpc.core_pb2.SetEmaneModelConfigRequest request: set-EMANE-model-configuration request
|
||||
:param core.api.grpc.core_pb2.SetEmaneModelConfigRequest request:
|
||||
set-EMANE-model-configuration request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: set-EMANE-model-configuration response
|
||||
:rtype: core.api.grpc.core_pb2.SetEmaneModelConfigResponse
|
||||
|
@ -1491,9 +1502,11 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
Retrieve all EMANE model configurations of a session
|
||||
|
||||
:param core.api.grpc.core_pb2.GetEmaneModelConfigsRequest request: get-EMANE-model-configurations request
|
||||
:param core.api.grpc.core_pb2.GetEmaneModelConfigsRequest request:
|
||||
get-EMANE-model-configurations request
|
||||
:param grpc.ServicerContext context: context object
|
||||
:return: get-EMANE-model-configurations response that has all the EMANE configurations
|
||||
:return: get-EMANE-model-configurations response that has all the EMANE
|
||||
configurations
|
||||
:rtype: core.api.grpc.core_pb2.GetEmaneModelConfigsResponse
|
||||
"""
|
||||
logging.debug("get emane model configs: %s", request)
|
||||
|
@ -1506,11 +1519,12 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
|
||||
for model_name in model_config:
|
||||
model = session.emane.models[model_name]
|
||||
config = session.emane.get_model_config(node_id, model_name)
|
||||
config_groups = get_config_groups(config, model)
|
||||
node_configurations = response.configs[node_id]
|
||||
node_configurations.model = model_name
|
||||
node_configurations.groups.extend(config_groups)
|
||||
current_config = session.emane.get_model_config(node_id, model_name)
|
||||
config = get_config_options(current_config, model)
|
||||
model_config = core_pb2.GetEmaneModelConfigsResponse.ModelConfig(
|
||||
model=model_name, config=config
|
||||
)
|
||||
response.configs[node_id].CopyFrom(model_config)
|
||||
return response
|
||||
|
||||
def SaveXml(self, request, context):
|
||||
|
@ -1544,19 +1558,22 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
logging.debug("open xml: %s", request)
|
||||
session = self.coreemu.create_session()
|
||||
session.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
|
||||
_, temp_path = tempfile.mkstemp()
|
||||
with open(temp_path, "w") as xml_file:
|
||||
xml_file.write(request.data)
|
||||
temp = tempfile.NamedTemporaryFile(delete=False)
|
||||
temp.write(request.data.encode("utf-8"))
|
||||
temp.close()
|
||||
|
||||
try:
|
||||
session.open_xml(temp_path, start=True)
|
||||
session.open_xml(temp.name, request.start)
|
||||
session.name = os.path.basename(request.file)
|
||||
session.file_name = request.file
|
||||
return core_pb2.OpenXmlResponse(session_id=session.id, result=True)
|
||||
except IOError:
|
||||
logging.exception("error opening session file")
|
||||
self.coreemu.delete_session(session.id)
|
||||
context.abort(grpc.StatusCode.INVALID_ARGUMENT, "invalid xml file")
|
||||
finally:
|
||||
os.unlink(temp.name)
|
||||
|
||||
def GetInterfaces(self, request, context):
|
||||
"""
|
||||
|
|
|
@ -944,7 +944,7 @@ class CoreHandler(socketserver.BaseRequestHandler):
|
|||
if os.path.splitext(file_name)[1].lower() == ".xml":
|
||||
session = self.coreemu.create_session(master=False)
|
||||
try:
|
||||
session.open_xml(file_name, start=True)
|
||||
session.open_xml(file_name)
|
||||
except Exception:
|
||||
self.coreemu.delete_session(session.id)
|
||||
raise
|
||||
|
|
|
@ -18,6 +18,7 @@ from core.nodes.ipaddress import IpAddress
|
|||
from core.nodes.network import CoreNetwork, CtrlNet
|
||||
|
||||
LOCK = threading.Lock()
|
||||
CMD_HIDE = True
|
||||
|
||||
|
||||
class DistributedServer(object):
|
||||
|
@ -54,18 +55,18 @@ class DistributedServer(object):
|
|||
replace_env = env is not None
|
||||
if not wait:
|
||||
cmd += " &"
|
||||
logging.info(
|
||||
logging.debug(
|
||||
"remote cmd server(%s) cwd(%s) wait(%s): %s", self.host, cwd, wait, cmd
|
||||
)
|
||||
try:
|
||||
if cwd is None:
|
||||
result = self.conn.run(
|
||||
cmd, hide=False, env=env, replace_env=replace_env
|
||||
cmd, hide=CMD_HIDE, env=env, replace_env=replace_env
|
||||
)
|
||||
else:
|
||||
with self.conn.cd(cwd):
|
||||
result = self.conn.run(
|
||||
cmd, hide=False, env=env, replace_env=replace_env
|
||||
cmd, hide=CMD_HIDE, env=env, replace_env=replace_env
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except UnexpectedExit as e:
|
||||
|
|
|
@ -634,19 +634,23 @@ class Session(object):
|
|||
if node_two:
|
||||
node_two.lock.release()
|
||||
|
||||
def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=None):
|
||||
def add_node(self, _type=NodeTypes.DEFAULT, _id=None, node_options=None, _cls=None):
|
||||
"""
|
||||
Add a node to the session, based on the provided node data.
|
||||
|
||||
:param core.emulator.enumerations.NodeTypes _type: type of node to create
|
||||
:param int _id: id for node, defaults to None for generated id
|
||||
:param core.emulator.emudata.NodeOptions node_options: data to create node with
|
||||
:param class _cls: optional custom class to use for a created node
|
||||
:return: created node
|
||||
:raises core.CoreError: when an invalid node type is given
|
||||
"""
|
||||
|
||||
# validate node type, get class, or throw error
|
||||
node_class = self.get_node_class(_type)
|
||||
if _cls is None:
|
||||
node_class = self.get_node_class(_type)
|
||||
else:
|
||||
node_class = _cls
|
||||
|
||||
# set node start based on current session state, override and check when rj45
|
||||
start = self.state > EventTypes.DEFINITION_STATE.value
|
||||
|
@ -705,13 +709,8 @@ class Session(object):
|
|||
# set node position and broadcast it
|
||||
self.set_node_position(node, node_options)
|
||||
|
||||
# add services to default and physical nodes only
|
||||
if _type in [
|
||||
NodeTypes.DEFAULT,
|
||||
NodeTypes.PHYSICAL,
|
||||
NodeTypes.DOCKER,
|
||||
NodeTypes.LXC,
|
||||
]:
|
||||
# add services to needed nodes
|
||||
if isinstance(node, (CoreNode, PhysicalNode, DockerNode, LxcNode)):
|
||||
node.type = node_options.model
|
||||
logging.debug("set node type: %s", node.type)
|
||||
self.services.add_services(node, node.type, node_options.services)
|
||||
|
@ -820,19 +819,24 @@ class Session(object):
|
|||
:param bool start: instantiate session if true, false otherwise
|
||||
:return: nothing
|
||||
"""
|
||||
logging.info("opening xml: %s", file_name)
|
||||
|
||||
# clear out existing session
|
||||
self.clear()
|
||||
|
||||
if start:
|
||||
self.set_state(EventTypes.CONFIGURATION_STATE)
|
||||
state = EventTypes.CONFIGURATION_STATE
|
||||
else:
|
||||
state = EventTypes.DEFINITION_STATE
|
||||
self.set_state(state)
|
||||
self.name = os.path.basename(file_name)
|
||||
self.file_name = file_name
|
||||
|
||||
# write out xml file
|
||||
CoreXmlReader(self).read(file_name)
|
||||
|
||||
# start session if needed
|
||||
if start:
|
||||
self.name = os.path.basename(file_name)
|
||||
self.file_name = file_name
|
||||
self.instantiate()
|
||||
|
||||
def save_xml(self, file_name):
|
||||
|
|
|
@ -284,7 +284,7 @@ class CoreNetwork(CoreNetworkBase):
|
|||
:rtype: str
|
||||
:raises CoreCommandError: when a non-zero exit status occurs
|
||||
"""
|
||||
logging.info("network node(%s) cmd", self.name)
|
||||
logging.debug("network node(%s) cmd", self.name)
|
||||
output = utils.cmd(args, env, cwd, wait, shell)
|
||||
self.session.distributed.execute(lambda x: x.remote_cmd(args, env, cwd, wait))
|
||||
return output
|
||||
|
|
|
@ -206,7 +206,7 @@ def cmd(args, env=None, cwd=None, wait=True, shell=False):
|
|||
:raises CoreCommandError: when there is a non-zero exit status or the file to
|
||||
execute is not found
|
||||
"""
|
||||
logging.info("command cwd(%s) wait(%s): %s", cwd, wait, args)
|
||||
logging.debug("command cwd(%s) wait(%s): %s", cwd, wait, args)
|
||||
if shell is False:
|
||||
args = shlex.split(args)
|
||||
try:
|
||||
|
|
|
@ -304,10 +304,8 @@ class CoreXmlWriter(object):
|
|||
default_options = self.session.options.default_values()
|
||||
for _id in default_options:
|
||||
default_value = default_options[_id]
|
||||
# TODO: should we just save the current config regardless, since it may change?
|
||||
value = options_config[_id]
|
||||
if value != default_value:
|
||||
add_configuration(option_elements, _id, value)
|
||||
value = options_config.get(_id, default_value)
|
||||
add_configuration(option_elements, _id, value)
|
||||
|
||||
if option_elements.getchildren():
|
||||
self.scenario.append(option_elements)
|
||||
|
|
|
@ -163,7 +163,7 @@ message GetSessionOptionsRequest {
|
|||
}
|
||||
|
||||
message GetSessionOptionsResponse {
|
||||
repeated ConfigGroup groups = 1;
|
||||
map<string, ConfigOption> config = 2;
|
||||
}
|
||||
|
||||
message SetSessionOptionsRequest {
|
||||
|
@ -428,10 +428,7 @@ message GetMobilityConfigsRequest {
|
|||
}
|
||||
|
||||
message GetMobilityConfigsResponse {
|
||||
message MobilityConfig {
|
||||
repeated ConfigGroup groups = 1;
|
||||
}
|
||||
map<int32, MobilityConfig> configs = 1;
|
||||
map<int32, MappedConfig> configs = 1;
|
||||
}
|
||||
|
||||
message GetMobilityConfigRequest {
|
||||
|
@ -440,7 +437,7 @@ message GetMobilityConfigRequest {
|
|||
}
|
||||
|
||||
message GetMobilityConfigResponse {
|
||||
repeated ConfigGroup groups = 1;
|
||||
map<string, ConfigOption> config = 1;
|
||||
}
|
||||
|
||||
message SetMobilityConfigRequest {
|
||||
|
@ -551,7 +548,7 @@ message GetWlanConfigRequest {
|
|||
}
|
||||
|
||||
message GetWlanConfigResponse {
|
||||
repeated ConfigGroup groups = 1;
|
||||
map<string, ConfigOption> config = 1;
|
||||
}
|
||||
|
||||
message SetWlanConfigRequest {
|
||||
|
@ -569,7 +566,7 @@ message GetEmaneConfigRequest {
|
|||
}
|
||||
|
||||
message GetEmaneConfigResponse {
|
||||
repeated ConfigGroup groups = 1;
|
||||
map<string, ConfigOption> config = 1;
|
||||
}
|
||||
|
||||
message SetEmaneConfigRequest {
|
||||
|
@ -597,7 +594,7 @@ message GetEmaneModelConfigRequest {
|
|||
}
|
||||
|
||||
message GetEmaneModelConfigResponse {
|
||||
repeated ConfigGroup groups = 1;
|
||||
map<string, ConfigOption> config = 1;
|
||||
}
|
||||
|
||||
message SetEmaneModelConfigRequest {
|
||||
|
@ -619,7 +616,7 @@ message GetEmaneModelConfigsRequest {
|
|||
message GetEmaneModelConfigsResponse {
|
||||
message ModelConfig {
|
||||
string model = 1;
|
||||
repeated ConfigGroup groups = 2;
|
||||
map<string, ConfigOption> config = 2;
|
||||
}
|
||||
map<int32, ModelConfig> configs = 1;
|
||||
}
|
||||
|
@ -634,6 +631,8 @@ message SaveXmlResponse {
|
|||
|
||||
message OpenXmlRequest {
|
||||
string data = 1;
|
||||
bool start = 2;
|
||||
string file = 3;
|
||||
}
|
||||
|
||||
message OpenXmlResponse {
|
||||
|
@ -777,9 +776,8 @@ message NodeServiceData {
|
|||
string meta = 10;
|
||||
}
|
||||
|
||||
message ConfigGroup {
|
||||
string name = 1;
|
||||
repeated ConfigOption options = 2;
|
||||
message MappedConfig {
|
||||
map<string, ConfigOption> config = 1;
|
||||
}
|
||||
|
||||
message ConfigOption {
|
||||
|
@ -788,6 +786,7 @@ message ConfigOption {
|
|||
string value = 3;
|
||||
int32 type = 4;
|
||||
repeated string select = 5;
|
||||
string group = 6;
|
||||
}
|
||||
|
||||
message Session {
|
||||
|
@ -801,6 +800,7 @@ message SessionSummary {
|
|||
int32 id = 1;
|
||||
SessionState.Enum state = 2;
|
||||
int32 nodes = 3;
|
||||
string file = 4;
|
||||
}
|
||||
|
||||
message Node {
|
||||
|
|
|
@ -100,7 +100,7 @@ class TestGrpc:
|
|||
response = client.get_session_options(session.id)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
assert len(response.config) > 0
|
||||
|
||||
def test_get_session_location(self, grpc_server):
|
||||
# given
|
||||
|
@ -457,7 +457,7 @@ class TestGrpc:
|
|||
response = client.get_wlan_config(session.id, wlan.id)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
assert len(response.config) > 0
|
||||
|
||||
def test_set_wlan_config(self, grpc_server):
|
||||
# given
|
||||
|
@ -501,7 +501,7 @@ class TestGrpc:
|
|||
response = client.get_emane_config(session.id)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
assert len(response.config) > 0
|
||||
|
||||
def test_set_emane_config(self, grpc_server):
|
||||
# given
|
||||
|
@ -540,6 +540,9 @@ class TestGrpc:
|
|||
# then
|
||||
assert len(response.configs) == 1
|
||||
assert emane_network.id in response.configs
|
||||
model_config = response.configs[emane_network.id]
|
||||
assert model_config.model == EmaneIeee80211abgModel.name
|
||||
assert len(model_config.config) > 0
|
||||
|
||||
def test_set_emane_model_config(self, grpc_server):
|
||||
# given
|
||||
|
@ -582,7 +585,7 @@ class TestGrpc:
|
|||
)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
assert len(response.config) > 0
|
||||
|
||||
def test_get_emane_models(self, grpc_server):
|
||||
# given
|
||||
|
@ -610,6 +613,8 @@ class TestGrpc:
|
|||
# then
|
||||
assert len(response.configs) > 0
|
||||
assert wlan.id in response.configs
|
||||
mapped_config = response.configs[wlan.id]
|
||||
assert len(mapped_config.config) > 0
|
||||
|
||||
def test_get_mobility_config(self, grpc_server):
|
||||
# given
|
||||
|
@ -623,7 +628,7 @@ class TestGrpc:
|
|||
response = client.get_mobility_config(session.id, wlan.id)
|
||||
|
||||
# then
|
||||
assert len(response.groups) > 0
|
||||
assert len(response.config) > 0
|
||||
|
||||
def test_set_mobility_config(self, grpc_server):
|
||||
# given
|
||||
|
|