Merge branch 'coretk' into coretk-color

This commit is contained in:
Huy Pham 2019-12-13 15:53:51 -08:00
commit ea2bfad591
13 changed files with 160 additions and 78 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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):
"""

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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