Merge branch 'coretk' into coretk-color
This commit is contained in:
commit
ea2bfad591
13 changed files with 160 additions and 78 deletions
|
@ -183,8 +183,7 @@ class CoreClient:
|
|||
)
|
||||
|
||||
def handle_exception_event(self, event):
|
||||
print(event)
|
||||
print(event.node_id)
|
||||
logging.info("exception event: %s", event)
|
||||
self.app.statusbar.core_alarms.append(event)
|
||||
|
||||
def join_session(self, session_id, query_location=True):
|
||||
|
@ -229,26 +228,25 @@ class CoreClient:
|
|||
|
||||
# get emane model config
|
||||
response = self.client.get_emane_model_configs(self.session_id)
|
||||
for _id in response.configs:
|
||||
config = response.configs[_id]
|
||||
for config in response.configs:
|
||||
interface = None
|
||||
node_id = _id
|
||||
if _id >= 1000:
|
||||
interface = _id % 1000
|
||||
node_id = int(_id / 1000)
|
||||
if config.interface != -1:
|
||||
interface = config.interface
|
||||
self.set_emane_model_config(
|
||||
node_id, config.model, config.config, interface
|
||||
config.node_id, config.model, config.config, interface
|
||||
)
|
||||
|
||||
# get wlan configurations
|
||||
response = self.client.get_wlan_configs(self.session_id)
|
||||
for _id in response.configs:
|
||||
mapped_config = response.configs[_id]
|
||||
self.wlan_configs[_id] = mapped_config.config
|
||||
|
||||
# save and retrieve data, needed for session nodes
|
||||
for node in session.nodes:
|
||||
# get node service config and file config
|
||||
# get wlan configs for wlan nodes
|
||||
if node.type == core_pb2.NodeType.WIRELESS_LAN:
|
||||
response = self.client.get_wlan_config(self.session_id, node.id)
|
||||
self.wlan_configs[node.id] = response.config
|
||||
# retrieve service configurations data for default nodes
|
||||
elif node.type == core_pb2.NodeType.DEFAULT:
|
||||
if node.type == core_pb2.NodeType.DEFAULT:
|
||||
for service in node.services:
|
||||
response = self.client.get_node_service(
|
||||
self.session_id, node.id, service
|
||||
|
|
|
@ -46,6 +46,7 @@ class NodeConfigDialog(Dialog):
|
|||
self.canvas_node = canvas_node
|
||||
self.node = canvas_node.core_node
|
||||
self.image = canvas_node.image
|
||||
self.image_file = None
|
||||
self.image_button = None
|
||||
self.name = tk.StringVar(value=self.node.name)
|
||||
self.type = tk.StringVar(value=self.node.model)
|
||||
|
@ -201,6 +202,7 @@ class NodeConfigDialog(Dialog):
|
|||
if file_path:
|
||||
self.image = Images.create(file_path, nodeutils.ICON_SIZE)
|
||||
self.image_button.config(image=self.image)
|
||||
self.image_file = file_path
|
||||
|
||||
def config_apply(self):
|
||||
# update core node
|
||||
|
@ -211,6 +213,10 @@ class NodeConfigDialog(Dialog):
|
|||
if NodeUtils.is_container_node(self.node.type) and server != "localhost":
|
||||
self.node.server = server
|
||||
|
||||
# set custom icon
|
||||
if self.image_file:
|
||||
self.node.icon = self.image_file
|
||||
|
||||
# update canvas node
|
||||
self.canvas_node.image = self.image
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import tkinter as tk
|
|||
from PIL import Image, ImageTk
|
||||
|
||||
from core.api.grpc import core_pb2
|
||||
from coretk import nodeutils
|
||||
from coretk.dialogs.shapemod import ShapeDialog
|
||||
from coretk.graph import tags
|
||||
from coretk.graph.edges import CanvasEdge, CanvasWirelessEdge
|
||||
|
@ -12,6 +13,7 @@ from coretk.graph.linkinfo import LinkInfo, Throughput
|
|||
from coretk.graph.node import CanvasNode
|
||||
from coretk.graph.shape import Shape
|
||||
from coretk.graph.shapeutils import ShapeType, is_draw_shape
|
||||
from coretk.images import Images
|
||||
from coretk.nodeutils import NodeUtils
|
||||
|
||||
ZOOM_IN = 1.1
|
||||
|
@ -136,6 +138,11 @@ class CanvasGraph(tk.Canvas):
|
|||
valid_y = y1 <= y <= y2
|
||||
return valid_x and valid_y
|
||||
|
||||
def valid_position(self, x1, y1, x2, y2):
|
||||
valid_topleft = self.inside_canvas(x1, y1)
|
||||
valid_bottomright = self.inside_canvas(x2, y2)
|
||||
return valid_topleft and valid_bottomright
|
||||
|
||||
def draw_grid(self):
|
||||
"""
|
||||
Create grid.
|
||||
|
@ -186,12 +193,19 @@ class CanvasGraph(tk.Canvas):
|
|||
"""
|
||||
# draw existing nodes
|
||||
for core_node in session.nodes:
|
||||
logging.info("drawing core node: %s", core_node)
|
||||
# peer to peer node is not drawn on the GUI
|
||||
if NodeUtils.is_ignore_node(core_node.type):
|
||||
continue
|
||||
|
||||
# draw nodes on the canvas
|
||||
image = NodeUtils.node_icon(core_node.type, core_node.model)
|
||||
if core_node.icon:
|
||||
try:
|
||||
image = Images.create(core_node.icon, nodeutils.ICON_SIZE)
|
||||
except OSError:
|
||||
logging.error("invalid icon: %s", core_node.icon)
|
||||
|
||||
x = core_node.position.x
|
||||
y = core_node.position.y
|
||||
node = CanvasNode(self.master, x, y, core_node, image)
|
||||
|
@ -530,6 +544,13 @@ class CanvasGraph(tk.Canvas):
|
|||
"""
|
||||
x, y = self.canvas_xy(event)
|
||||
if not self.inside_canvas(x, y):
|
||||
if self.select_box:
|
||||
self.select_box.delete()
|
||||
self.select_box = None
|
||||
if is_draw_shape(self.annotation_type) and self.shape_drawing:
|
||||
shape = self.shapes.pop(self.selected)
|
||||
shape.delete()
|
||||
self.shape_drawing = False
|
||||
return
|
||||
|
||||
x_offset = x - self.cursor[0]
|
||||
|
@ -595,10 +616,7 @@ class CanvasGraph(tk.Canvas):
|
|||
if self.selected is None or self.selected in self.shapes:
|
||||
actual_x, actual_y = self.get_actual_coords(x, y)
|
||||
core_node = self.core.create_node(
|
||||
int(actual_x),
|
||||
int(actual_y),
|
||||
self.node_draw.node_type,
|
||||
self.node_draw.model,
|
||||
actual_x, actual_y, self.node_draw.node_type, self.node_draw.model
|
||||
)
|
||||
node = CanvasNode(self.master, x, y, core_node, self.node_draw.image)
|
||||
self.core.canvas_nodes[core_node.id] = node
|
||||
|
|
|
@ -103,10 +103,19 @@ class CanvasNode:
|
|||
self.motion(x_offset, y_offset, update=False)
|
||||
|
||||
def motion(self, x_offset, y_offset, update=True):
|
||||
original_position = self.canvas.coords(self.id)
|
||||
self.canvas.move(self.id, x_offset, y_offset)
|
||||
x, y = self.canvas.coords(self.id)
|
||||
|
||||
# check new position
|
||||
bbox = self.canvas.bbox(self.id)
|
||||
if not self.canvas.valid_position(*bbox):
|
||||
self.canvas.coords(self.id, original_position)
|
||||
return
|
||||
|
||||
# move test and selection box
|
||||
self.canvas.move(self.text_id, x_offset, y_offset)
|
||||
self.canvas.move_selection(self.id, x_offset, y_offset)
|
||||
x, y = self.canvas.coords(self.id)
|
||||
|
||||
# move antennae
|
||||
for antenna_id in self.antennae:
|
||||
|
@ -131,8 +140,8 @@ class CanvasNode:
|
|||
|
||||
# set actual coords for node and update core is running
|
||||
real_x, real_y = self.canvas.get_actual_coords(x, y)
|
||||
self.core_node.position.x = int(real_x)
|
||||
self.core_node.position.y = int(real_y)
|
||||
self.core_node.position.x = real_x
|
||||
self.core_node.position.y = real_y
|
||||
if self.app.core.is_runtime() and update:
|
||||
self.app.core.edit_node(self.core_node)
|
||||
|
||||
|
@ -155,11 +164,6 @@ class CanvasNode:
|
|||
else:
|
||||
self.show_config()
|
||||
|
||||
def update_coords(self):
|
||||
x, y = self.canvas.coords(self.id)
|
||||
self.core_node.position.x = int(x)
|
||||
self.core_node.position.y = int(y)
|
||||
|
||||
def create_context(self):
|
||||
is_wlan = self.core_node.type == NodeType.WIRELESS_LAN
|
||||
is_emane = self.core_node.type == NodeType.EMANE
|
||||
|
@ -169,6 +173,8 @@ class CanvasNode:
|
|||
context.add_command(label="Configure", command=self.show_config)
|
||||
if NodeUtils.is_container_node(self.core_node.type):
|
||||
context.add_command(label="Services", state=tk.DISABLED)
|
||||
if is_wlan:
|
||||
context.add_command(label="WLAN Config", command=self.show_wlan_config)
|
||||
if is_wlan and self.core_node.id in self.app.core.mobility_players:
|
||||
context.add_command(
|
||||
label="Mobility Player", command=self.show_mobility_player
|
||||
|
|
|
@ -136,7 +136,13 @@ class Shape:
|
|||
self.canvas.delete(self.id)
|
||||
|
||||
def motion(self, x_offset, y_offset):
|
||||
original_position = self.canvas.coords(self.id)
|
||||
self.canvas.move(self.id, x_offset, y_offset)
|
||||
coords = self.canvas.coords(self.id)
|
||||
if not self.canvas.valid_position(*coords):
|
||||
self.canvas.coords(self.id, original_position)
|
||||
return
|
||||
|
||||
self.canvas.move_selection(self.id, x_offset, y_offset)
|
||||
if self.text_id is not None:
|
||||
self.canvas.move(self.text_id, x_offset, y_offset)
|
||||
|
|
|
@ -827,6 +827,18 @@ class CoreGrpcClient:
|
|||
)
|
||||
return self.stub.ServiceAction(request)
|
||||
|
||||
def get_wlan_configs(self, session_id):
|
||||
"""
|
||||
Get all wlan configurations.
|
||||
|
||||
:param int session_id: session id
|
||||
:return: response with a dict of node ids to wlan configurations
|
||||
:rtype: core_pb2.GetWlanConfigsResponse
|
||||
:raises grpc.RpcError: when session doesn't exist
|
||||
"""
|
||||
request = core_pb2.GetWlanConfigsRequest(session_id=session_id)
|
||||
return self.stub.GetWlanConfigs(request)
|
||||
|
||||
def get_wlan_config(self, session_id, node_id):
|
||||
"""
|
||||
Get wlan configuration for a node.
|
||||
|
|
|
@ -221,6 +221,22 @@ def get_emane_model_id(node_id, interface_id):
|
|||
return node_id
|
||||
|
||||
|
||||
def parse_emane_model_id(_id):
|
||||
"""
|
||||
Parses EMANE model id to get true node id and interface id.
|
||||
|
||||
:param _id: id to parse
|
||||
:return: node id and interface id
|
||||
:rtype: tuple
|
||||
"""
|
||||
interface = -1
|
||||
node_id = _id
|
||||
if _id >= 1000:
|
||||
interface = _id % 1000
|
||||
node_id = int(_id / 1000)
|
||||
return node_id, interface
|
||||
|
||||
|
||||
def convert_link(session, link_data):
|
||||
"""
|
||||
Convert link_data into core protobuf Link
|
||||
|
|
|
@ -393,15 +393,14 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
position = core_pb2.Position(
|
||||
x=node.position.x, y=node.position.y, z=node.position.z
|
||||
)
|
||||
|
||||
services = getattr(node, "services", [])
|
||||
if services is None:
|
||||
services = []
|
||||
services = [x.name for x in services]
|
||||
|
||||
emane_model = None
|
||||
if isinstance(node, EmaneNet):
|
||||
emane_model = node.model.name
|
||||
image = getattr(node, "image", None)
|
||||
|
||||
node_proto = core_pb2.Node(
|
||||
id=node.id,
|
||||
|
@ -411,6 +410,8 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
type=node_type.value,
|
||||
position=position,
|
||||
services=services,
|
||||
icon=node.icon,
|
||||
image=image,
|
||||
)
|
||||
if isinstance(node, (DockerNode, LxcNode)):
|
||||
node_proto.image = node.image
|
||||
|
@ -488,13 +489,7 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
:return: node event that contains node id, name, model, position, and services
|
||||
:rtype: core.api.grpc.core_pb2.NodeEvent
|
||||
"""
|
||||
x = None
|
||||
if event.x_position is not None:
|
||||
x = int(event.x_position)
|
||||
y = None
|
||||
if event.y_position is not None:
|
||||
y = int(event.y_position)
|
||||
position = core_pb2.Position(x=x, y=y)
|
||||
position = core_pb2.Position(x=event.x_position, y=event.y_position)
|
||||
services = event.services or ""
|
||||
services = services.split("|")
|
||||
node_proto = core_pb2.Node(
|
||||
|
@ -1163,13 +1158,6 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
logging.debug("get node service file: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
node = self.get_node(session, request.node_id, context)
|
||||
service = None
|
||||
for current_service in node.services:
|
||||
if current_service.name == request.service:
|
||||
service = current_service
|
||||
break
|
||||
if not service:
|
||||
context.abort(grpc.StatusCode.NOT_FOUND, "service not found")
|
||||
file_data = session.services.get_service_file(
|
||||
node, request.service, request.file
|
||||
)
|
||||
|
@ -1248,6 +1236,31 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
|
||||
return core_pb2.ServiceActionResponse(result=result)
|
||||
|
||||
def GetWlanConfigs(self, request, context):
|
||||
"""
|
||||
Retrieve all wireless-lan configurations.
|
||||
|
||||
:param core.api.grpc.core_pb2.GetWlanConfigsRequest request: request
|
||||
:param context: core.api.grpc.core_pb2.GetWlanConfigResponse
|
||||
:return: all wlan configurations
|
||||
:rtype: core.api.grpc.core_pb2.GetWlanConfigsResponse
|
||||
"""
|
||||
logging.debug("get wlan configs: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
response = core_pb2.GetWlanConfigsResponse()
|
||||
for node_id in session.mobility.node_configurations:
|
||||
model_config = session.mobility.node_configurations[node_id]
|
||||
if node_id == -1:
|
||||
continue
|
||||
for model_name in model_config:
|
||||
if model_name != BasicRangeModel.name:
|
||||
continue
|
||||
current_config = session.mobility.get_model_config(node_id, model_name)
|
||||
config = get_config_options(current_config, BasicRangeModel)
|
||||
mapped_config = core_pb2.MappedConfig(config=config)
|
||||
response.configs[node_id].CopyFrom(mapped_config)
|
||||
return response
|
||||
|
||||
def GetWlanConfig(self, request, context):
|
||||
"""
|
||||
Retrieve wireless-lan configuration of a node
|
||||
|
@ -1381,21 +1394,26 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
|||
"""
|
||||
logging.debug("get emane model configs: %s", request)
|
||||
session = self.get_session(request.session_id, context)
|
||||
response = core_pb2.GetEmaneModelConfigsResponse()
|
||||
for node_id in session.emane.node_configurations:
|
||||
model_config = session.emane.node_configurations[node_id]
|
||||
if node_id == -1:
|
||||
|
||||
configs = []
|
||||
for _id in session.emane.node_configurations:
|
||||
if _id == -1:
|
||||
continue
|
||||
|
||||
for model_name in model_config:
|
||||
model_configs = session.emane.node_configurations[_id]
|
||||
for model_name in model_configs:
|
||||
model = session.emane.models[model_name]
|
||||
current_config = session.emane.get_model_config(node_id, model_name)
|
||||
current_config = session.emane.get_model_config(_id, model_name)
|
||||
config = get_config_options(current_config, model)
|
||||
node_id, interface = grpcutils.parse_emane_model_id(_id)
|
||||
model_config = core_pb2.GetEmaneModelConfigsResponse.ModelConfig(
|
||||
model=model_name, config=config
|
||||
node_id=node_id,
|
||||
model=model_name,
|
||||
interface=interface,
|
||||
config=config,
|
||||
)
|
||||
response.configs[node_id].CopyFrom(model_config)
|
||||
return response
|
||||
configs.append(model_config)
|
||||
return core_pb2.GetEmaneModelConfigsResponse(configs=configs)
|
||||
|
||||
def SaveXml(self, request, context):
|
||||
"""
|
||||
|
|
|
@ -5,7 +5,6 @@ import signal
|
|||
import sys
|
||||
|
||||
import core.services
|
||||
from core.emulator.emudata import IdGen
|
||||
from core.emulator.session import Session
|
||||
from core.services.coreservices import ServiceManager
|
||||
|
||||
|
@ -49,7 +48,6 @@ class CoreEmu:
|
|||
self.config = config
|
||||
|
||||
# session management
|
||||
self.session_id_gen = IdGen()
|
||||
self.sessions = {}
|
||||
|
||||
# load services
|
||||
|
@ -79,7 +77,6 @@ class CoreEmu:
|
|||
:return: nothing
|
||||
"""
|
||||
logging.info("shutting down all sessions")
|
||||
self.session_id_gen.id = 0
|
||||
sessions = self.sessions.copy()
|
||||
self.sessions.clear()
|
||||
for _id in sessions:
|
||||
|
@ -96,11 +93,9 @@ class CoreEmu:
|
|||
:rtype: EmuSession
|
||||
"""
|
||||
if not _id:
|
||||
while True:
|
||||
_id = self.session_id_gen.next()
|
||||
if _id not in self.sessions:
|
||||
break
|
||||
|
||||
_id = 1
|
||||
while _id in self.sessions:
|
||||
_id += 1
|
||||
session = _cls(_id, config=self.config)
|
||||
logging.info("created session: %s", _id)
|
||||
self.sessions[_id] = session
|
||||
|
|
|
@ -396,7 +396,7 @@ class CoreServices:
|
|||
"""
|
||||
Add services to a node.
|
||||
|
||||
:param core.coreobj.PyCoreNode node: node to add services to
|
||||
:param core.nodes.base.CoreNode node: node to add services to
|
||||
:param str node_type: node type to add services to
|
||||
:param list[str] services: names of services to add to node
|
||||
:return: nothing
|
||||
|
|
|
@ -117,14 +117,8 @@ class NodeElement:
|
|||
|
||||
def add_position(self):
|
||||
x = self.node.position.x
|
||||
if x is not None:
|
||||
x = int(x)
|
||||
y = self.node.position.y
|
||||
if y is not None:
|
||||
y = int(y)
|
||||
z = self.node.position.z
|
||||
if z is not None:
|
||||
z = int(z)
|
||||
lat, lon, alt = None, None, None
|
||||
if x is not None and y is not None:
|
||||
lat, lon, alt = self.session.location.getgeo(x, y, z)
|
||||
|
@ -751,8 +745,8 @@ class CoreXmlReader:
|
|||
|
||||
position_element = device_element.find("position")
|
||||
if position_element is not None:
|
||||
x = get_int(position_element, "x")
|
||||
y = get_int(position_element, "y")
|
||||
x = get_float(position_element, "x")
|
||||
y = get_float(position_element, "y")
|
||||
if all([x, y]):
|
||||
options.set_position(x, y)
|
||||
|
||||
|
@ -773,8 +767,8 @@ class CoreXmlReader:
|
|||
|
||||
position_element = network_element.find("position")
|
||||
if position_element is not None:
|
||||
x = get_int(position_element, "x")
|
||||
y = get_int(position_element, "y")
|
||||
x = get_float(position_element, "x")
|
||||
y = get_float(position_element, "y")
|
||||
if all([x, y]):
|
||||
options.set_position(x, y)
|
||||
|
||||
|
|
|
@ -101,6 +101,8 @@ service CoreApi {
|
|||
}
|
||||
|
||||
// wlan rpc
|
||||
rpc GetWlanConfigs (GetWlanConfigsRequest) returns (GetWlanConfigsResponse) {
|
||||
}
|
||||
rpc GetWlanConfig (GetWlanConfigRequest) returns (GetWlanConfigResponse) {
|
||||
}
|
||||
rpc SetWlanConfig (SetWlanConfigRequest) returns (SetWlanConfigResponse) {
|
||||
|
@ -585,6 +587,14 @@ message ServiceActionResponse {
|
|||
bool result = 1;
|
||||
}
|
||||
|
||||
message GetWlanConfigsRequest {
|
||||
int32 session_id = 1;
|
||||
}
|
||||
|
||||
message GetWlanConfigsResponse {
|
||||
map<int32, MappedConfig> configs = 1;
|
||||
}
|
||||
|
||||
message GetWlanConfigRequest {
|
||||
int32 session_id = 1;
|
||||
int32 node_id = 2;
|
||||
|
@ -654,10 +664,12 @@ message GetEmaneModelConfigsRequest {
|
|||
|
||||
message GetEmaneModelConfigsResponse {
|
||||
message ModelConfig {
|
||||
string model = 1;
|
||||
map<string, ConfigOption> config = 2;
|
||||
int32 node_id = 1;
|
||||
string model = 2;
|
||||
int32 interface = 3;
|
||||
map<string, ConfigOption> config = 4;
|
||||
}
|
||||
map<int32, ModelConfig> configs = 1;
|
||||
repeated ModelConfig configs = 1;
|
||||
}
|
||||
|
||||
message SaveXmlRequest {
|
||||
|
@ -949,9 +961,9 @@ message SessionLocation {
|
|||
}
|
||||
|
||||
message Position {
|
||||
int32 x = 1;
|
||||
int32 y = 2;
|
||||
int32 z = 3;
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
float z = 3;
|
||||
float lat = 4;
|
||||
float lon = 5;
|
||||
float alt = 6;
|
||||
|
|
|
@ -708,10 +708,11 @@ class TestGrpc:
|
|||
|
||||
# then
|
||||
assert len(response.configs) == 1
|
||||
assert emane_network.id in response.configs
|
||||
model_config = response.configs[emane_network.id]
|
||||
model_config = response.configs[0]
|
||||
assert emane_network.id == model_config.node_id
|
||||
assert model_config.model == EmaneIeee80211abgModel.name
|
||||
assert len(model_config.config) > 0
|
||||
assert model_config.interface == -1
|
||||
|
||||
def test_set_emane_model_config(self, grpc_server):
|
||||
# given
|
||||
|
|
Loading…
Reference in a new issue