moved cpu usage to a grpc call that the gui will listen to, fixed grpc stream typing to be grpc.Future, fixed pygui issue for start callback when a start fails, but there are no exceptions
This commit is contained in:
parent
3544d00431
commit
fff4bd7963
7 changed files with 85 additions and 47 deletions
|
@ -467,7 +467,7 @@ class CoreGrpcClient:
|
||||||
session_id: int,
|
session_id: int,
|
||||||
handler: Callable[[core_pb2.Event], None],
|
handler: Callable[[core_pb2.Event], None],
|
||||||
events: List[core_pb2.Event] = None,
|
events: List[core_pb2.Event] = None,
|
||||||
) -> grpc.Channel:
|
) -> grpc.Future:
|
||||||
"""
|
"""
|
||||||
Listen for session events.
|
Listen for session events.
|
||||||
|
|
||||||
|
@ -484,7 +484,7 @@ class CoreGrpcClient:
|
||||||
|
|
||||||
def throughputs(
|
def throughputs(
|
||||||
self, session_id: int, handler: Callable[[core_pb2.ThroughputsEvent], None]
|
self, session_id: int, handler: Callable[[core_pb2.ThroughputsEvent], None]
|
||||||
) -> grpc.Channel:
|
) -> grpc.Future:
|
||||||
"""
|
"""
|
||||||
Listen for throughput events with information for interfaces and bridges.
|
Listen for throughput events with information for interfaces and bridges.
|
||||||
|
|
||||||
|
@ -498,6 +498,21 @@ class CoreGrpcClient:
|
||||||
start_streamer(stream, handler)
|
start_streamer(stream, handler)
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
def cpu_usage(
|
||||||
|
self, delay: int, handler: Callable[[core_pb2.CpuUsageEvent], None]
|
||||||
|
) -> grpc.Future:
|
||||||
|
"""
|
||||||
|
Listen for cpu usage events with the given repeat delay.
|
||||||
|
|
||||||
|
:param delay: delay between receiving events
|
||||||
|
:param handler: handler for every event
|
||||||
|
:return: stream processing events, can be used to cancel stream
|
||||||
|
"""
|
||||||
|
request = core_pb2.CpuUsageRequest(delay=delay)
|
||||||
|
stream = self.stub.CpuUsage(request)
|
||||||
|
start_streamer(stream, handler)
|
||||||
|
return stream
|
||||||
|
|
||||||
def add_node(
|
def add_node(
|
||||||
self, session_id: int, node: core_pb2.Node, source: str = None
|
self, session_id: int, node: core_pb2.Node, source: str = None
|
||||||
) -> core_pb2.AddNodeResponse:
|
) -> core_pb2.AddNodeResponse:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Tuple, Type, Union
|
from typing import Any, Dict, List, Tuple, Type, Union
|
||||||
|
|
||||||
import grpc
|
import grpc
|
||||||
|
@ -20,6 +21,25 @@ from core.services.coreservices import CoreService
|
||||||
WORKERS = 10
|
WORKERS = 10
|
||||||
|
|
||||||
|
|
||||||
|
class CpuUsage:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.stat_file: Path = Path("/proc/stat")
|
||||||
|
self.prev_idle: int = 0
|
||||||
|
self.prev_total: int = 0
|
||||||
|
|
||||||
|
def run(self) -> float:
|
||||||
|
lines = self.stat_file.read_text().splitlines()[0]
|
||||||
|
values = [int(x) for x in lines.split()[1:]]
|
||||||
|
idle = sum(values[3:5])
|
||||||
|
non_idle = sum(values[:3] + values[5:8])
|
||||||
|
total = idle + non_idle
|
||||||
|
total_diff = total - self.prev_total
|
||||||
|
idle_diff = idle - self.prev_idle
|
||||||
|
self.prev_idle = idle
|
||||||
|
self.prev_total = total
|
||||||
|
return (total_diff - idle_diff) / total_diff
|
||||||
|
|
||||||
|
|
||||||
def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOptions]:
|
def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOptions]:
|
||||||
"""
|
"""
|
||||||
Convert node protobuf message to data for creating a node.
|
Convert node protobuf message to data for creating a node.
|
||||||
|
|
|
@ -681,6 +681,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
|
||||||
last_stats = stats
|
last_stats = stats
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
|
def CpuUsage(
|
||||||
|
self, request: core_pb2.CpuUsageRequest, context: ServicerContext
|
||||||
|
) -> None:
|
||||||
|
cpu_usage = grpcutils.CpuUsage()
|
||||||
|
while self._is_running(context):
|
||||||
|
usage = cpu_usage.run()
|
||||||
|
yield core_pb2.CpuUsageEvent(usage=usage)
|
||||||
|
time.sleep(request.delay)
|
||||||
|
|
||||||
def AddNode(
|
def AddNode(
|
||||||
self, request: core_pb2.AddNodeRequest, context: ServicerContext
|
self, request: core_pb2.AddNodeRequest, context: ServicerContext
|
||||||
) -> core_pb2.AddNodeResponse:
|
) -> core_pb2.AddNodeResponse:
|
||||||
|
|
|
@ -16,6 +16,7 @@ from core.api.grpc import client
|
||||||
from core.api.grpc.common_pb2 import ConfigOption
|
from core.api.grpc.common_pb2 import ConfigOption
|
||||||
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
|
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
|
||||||
from core.api.grpc.core_pb2 import (
|
from core.api.grpc.core_pb2 import (
|
||||||
|
CpuUsageEvent,
|
||||||
Event,
|
Event,
|
||||||
ExceptionEvent,
|
ExceptionEvent,
|
||||||
Hook,
|
Hook,
|
||||||
|
@ -55,6 +56,7 @@ if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
||||||
GUI_SOURCE = "gui"
|
GUI_SOURCE = "gui"
|
||||||
|
CPU_USAGE_DELAY = 3
|
||||||
|
|
||||||
|
|
||||||
class CoreClient:
|
class CoreClient:
|
||||||
|
@ -92,8 +94,9 @@ class CoreClient:
|
||||||
self.hooks: Dict[str, Hook] = {}
|
self.hooks: Dict[str, Hook] = {}
|
||||||
self.emane_config: Dict[str, ConfigOption] = {}
|
self.emane_config: Dict[str, ConfigOption] = {}
|
||||||
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
self.mobility_players: Dict[int, MobilityPlayer] = {}
|
||||||
self.handling_throughputs: Optional[grpc.Channel] = None
|
self.handling_throughputs: Optional[grpc.Future] = None
|
||||||
self.handling_events: Optional[grpc.Channel] = None
|
self.handling_cpu_usage: Optional[grpc.Future] = None
|
||||||
|
self.handling_events: Optional[grpc.Future] = None
|
||||||
self.xml_dir: Optional[str] = None
|
self.xml_dir: Optional[str] = None
|
||||||
self.xml_file: Optional[str] = None
|
self.xml_file: Optional[str] = None
|
||||||
|
|
||||||
|
@ -111,6 +114,7 @@ class CoreClient:
|
||||||
)
|
)
|
||||||
if throughputs_enabled:
|
if throughputs_enabled:
|
||||||
self.enable_throughputs()
|
self.enable_throughputs()
|
||||||
|
self.setup_cpu_usage()
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
@ -258,6 +262,20 @@ class CoreClient:
|
||||||
self.handling_events.cancel()
|
self.handling_events.cancel()
|
||||||
self.handling_events = None
|
self.handling_events = None
|
||||||
|
|
||||||
|
def cancel_cpu_usage(self) -> None:
|
||||||
|
if self.handling_cpu_usage:
|
||||||
|
self.handling_cpu_usage.cancel()
|
||||||
|
self.handling_cpu_usage = None
|
||||||
|
|
||||||
|
def setup_cpu_usage(self) -> None:
|
||||||
|
if self.handling_cpu_usage and self.handling_cpu_usage.running():
|
||||||
|
return
|
||||||
|
if self.handling_cpu_usage:
|
||||||
|
self.handling_cpu_usage.cancel()
|
||||||
|
self.handling_cpu_usage = self._client.cpu_usage(
|
||||||
|
CPU_USAGE_DELAY, self.handle_cpu_event
|
||||||
|
)
|
||||||
|
|
||||||
def handle_throughputs(self, event: ThroughputsEvent) -> None:
|
def handle_throughputs(self, event: ThroughputsEvent) -> None:
|
||||||
if event.session_id != self.session_id:
|
if event.session_id != self.session_id:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
|
@ -269,6 +287,9 @@ class CoreClient:
|
||||||
logging.debug("handling throughputs event: %s", event)
|
logging.debug("handling throughputs event: %s", event)
|
||||||
self.app.after(0, self.app.canvas.set_throughputs, event)
|
self.app.after(0, self.app.canvas.set_throughputs, event)
|
||||||
|
|
||||||
|
def handle_cpu_event(self, event: CpuUsageEvent) -> None:
|
||||||
|
self.app.after(0, self.app.statusbar.set_cpu, event.usage)
|
||||||
|
|
||||||
def handle_exception_event(self, event: ExceptionEvent) -> None:
|
def handle_exception_event(self, event: ExceptionEvent) -> None:
|
||||||
logging.info("exception event: %s", event)
|
logging.info("exception event: %s", event)
|
||||||
self.app.statusbar.add_alert(event)
|
self.app.statusbar.add_alert(event)
|
||||||
|
@ -479,6 +500,8 @@ class CoreClient:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.client.connect()
|
self.client.connect()
|
||||||
|
self.setup_cpu_usage()
|
||||||
|
|
||||||
# get service information
|
# get service information
|
||||||
response = self.client.get_services()
|
response = self.client.get_services()
|
||||||
for service in response.services:
|
for service in response.services:
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
"""
|
"""
|
||||||
status bar
|
status bar
|
||||||
"""
|
"""
|
||||||
import sched
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from pathlib import Path
|
|
||||||
from threading import Thread
|
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import TYPE_CHECKING, List, Optional
|
from typing import TYPE_CHECKING, List, Optional
|
||||||
|
|
||||||
|
@ -16,41 +13,6 @@ if TYPE_CHECKING:
|
||||||
from core.gui.app import Application
|
from core.gui.app import Application
|
||||||
|
|
||||||
|
|
||||||
class CpuUsage:
|
|
||||||
def __init__(self, statusbar: "StatusBar") -> None:
|
|
||||||
self.scheduler: sched.scheduler = sched.scheduler()
|
|
||||||
self.running: bool = False
|
|
||||||
self.thread: Optional[Thread] = None
|
|
||||||
self.prev_idle: int = 0
|
|
||||||
self.prev_total: int = 0
|
|
||||||
self.stat_file: Path = Path("/proc/stat")
|
|
||||||
self.statusbar: "StatusBar" = statusbar
|
|
||||||
|
|
||||||
def start(self) -> None:
|
|
||||||
self.running = True
|
|
||||||
self.thread = Thread(target=self._start, daemon=True)
|
|
||||||
self.thread.start()
|
|
||||||
|
|
||||||
def _start(self):
|
|
||||||
self.scheduler.enter(0, 0, self.run)
|
|
||||||
self.scheduler.run()
|
|
||||||
|
|
||||||
def run(self) -> None:
|
|
||||||
lines = self.stat_file.read_text().splitlines()[0]
|
|
||||||
values = [int(x) for x in lines.split()[1:]]
|
|
||||||
idle = sum(values[3:5])
|
|
||||||
non_idle = sum(values[:3] + values[5:8])
|
|
||||||
total = idle + non_idle
|
|
||||||
total_diff = total - self.prev_total
|
|
||||||
idle_diff = idle - self.prev_idle
|
|
||||||
cpu_percent = (total_diff - idle_diff) / total_diff
|
|
||||||
self.statusbar.after(0, self.statusbar.set_cpu, cpu_percent)
|
|
||||||
self.prev_idle = idle
|
|
||||||
self.prev_total = total
|
|
||||||
if self.running:
|
|
||||||
self.scheduler.enter(3, 0, self.run)
|
|
||||||
|
|
||||||
|
|
||||||
class StatusBar(ttk.Frame):
|
class StatusBar(ttk.Frame):
|
||||||
def __init__(self, master: tk.Widget, app: "Application") -> None:
|
def __init__(self, master: tk.Widget, app: "Application") -> None:
|
||||||
super().__init__(master)
|
super().__init__(master)
|
||||||
|
@ -64,8 +26,6 @@ class StatusBar(ttk.Frame):
|
||||||
self.running: bool = False
|
self.running: bool = False
|
||||||
self.core_alarms: List[ExceptionEvent] = []
|
self.core_alarms: List[ExceptionEvent] = []
|
||||||
self.draw()
|
self.draw()
|
||||||
self.cpu_usage: CpuUsage = CpuUsage(self)
|
|
||||||
self.cpu_usage.start()
|
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
self.columnconfigure(0, weight=7)
|
self.columnconfigure(0, weight=7)
|
||||||
|
|
|
@ -305,10 +305,11 @@ class Toolbar(ttk.Frame):
|
||||||
self.set_runtime()
|
self.set_runtime()
|
||||||
self.app.core.set_metadata()
|
self.app.core.set_metadata()
|
||||||
self.app.core.show_mobility_players()
|
self.app.core.show_mobility_players()
|
||||||
elif response.exceptions:
|
else:
|
||||||
enable_buttons(self.design_frame, enabled=True)
|
enable_buttons(self.design_frame, enabled=True)
|
||||||
message = "\n".join(response.exceptions)
|
if response.exceptions:
|
||||||
self.app.show_error("Start Session Error", message)
|
message = "\n".join(response.exceptions)
|
||||||
|
self.app.show_error("Start Session Error", message)
|
||||||
|
|
||||||
def set_runtime(self) -> None:
|
def set_runtime(self) -> None:
|
||||||
enable_buttons(self.runtime_frame, enabled=True)
|
enable_buttons(self.runtime_frame, enabled=True)
|
||||||
|
|
|
@ -51,6 +51,8 @@ service CoreApi {
|
||||||
}
|
}
|
||||||
rpc Throughputs (ThroughputsRequest) returns (stream ThroughputsEvent) {
|
rpc Throughputs (ThroughputsRequest) returns (stream ThroughputsEvent) {
|
||||||
}
|
}
|
||||||
|
rpc CpuUsage (CpuUsageRequest) returns (stream CpuUsageEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
// node rpc
|
// node rpc
|
||||||
rpc AddNode (AddNodeRequest) returns (AddNodeResponse) {
|
rpc AddNode (AddNodeRequest) returns (AddNodeResponse) {
|
||||||
|
@ -347,6 +349,14 @@ message ThroughputsEvent {
|
||||||
repeated InterfaceThroughput iface_throughputs = 3;
|
repeated InterfaceThroughput iface_throughputs = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CpuUsageRequest {
|
||||||
|
int32 delay = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CpuUsageEvent {
|
||||||
|
double usage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message InterfaceThroughput {
|
message InterfaceThroughput {
|
||||||
int32 node_id = 1;
|
int32 node_id = 1;
|
||||||
int32 iface_id = 2;
|
int32 iface_id = 2;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue