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, | ||||
|         handler: Callable[[core_pb2.Event], None], | ||||
|         events: List[core_pb2.Event] = None, | ||||
|     ) -> grpc.Channel: | ||||
|     ) -> grpc.Future: | ||||
|         """ | ||||
|         Listen for session events. | ||||
| 
 | ||||
|  | @ -484,7 +484,7 @@ class CoreGrpcClient: | |||
| 
 | ||||
|     def throughputs( | ||||
|         self, session_id: int, handler: Callable[[core_pb2.ThroughputsEvent], None] | ||||
|     ) -> grpc.Channel: | ||||
|     ) -> grpc.Future: | ||||
|         """ | ||||
|         Listen for throughput events with information for interfaces and bridges. | ||||
| 
 | ||||
|  | @ -498,6 +498,21 @@ class CoreGrpcClient: | |||
|         start_streamer(stream, handler) | ||||
|         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( | ||||
|         self, session_id: int, node: core_pb2.Node, source: str = None | ||||
|     ) -> core_pb2.AddNodeResponse: | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import logging | ||||
| import time | ||||
| from pathlib import Path | ||||
| from typing import Any, Dict, List, Tuple, Type, Union | ||||
| 
 | ||||
| import grpc | ||||
|  | @ -20,6 +21,25 @@ from core.services.coreservices import CoreService | |||
| 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]: | ||||
|     """ | ||||
|     Convert node protobuf message to data for creating a node. | ||||
|  |  | |||
|  | @ -681,6 +681,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): | |||
|             last_stats = stats | ||||
|             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( | ||||
|         self, request: core_pb2.AddNodeRequest, context: ServicerContext | ||||
|     ) -> 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.configservices_pb2 import ConfigService, ConfigServiceConfig | ||||
| from core.api.grpc.core_pb2 import ( | ||||
|     CpuUsageEvent, | ||||
|     Event, | ||||
|     ExceptionEvent, | ||||
|     Hook, | ||||
|  | @ -55,6 +56,7 @@ if TYPE_CHECKING: | |||
|     from core.gui.app import Application | ||||
| 
 | ||||
| GUI_SOURCE = "gui" | ||||
| CPU_USAGE_DELAY = 3 | ||||
| 
 | ||||
| 
 | ||||
| class CoreClient: | ||||
|  | @ -92,8 +94,9 @@ class CoreClient: | |||
|         self.hooks: Dict[str, Hook] = {} | ||||
|         self.emane_config: Dict[str, ConfigOption] = {} | ||||
|         self.mobility_players: Dict[int, MobilityPlayer] = {} | ||||
|         self.handling_throughputs: Optional[grpc.Channel] = None | ||||
|         self.handling_events: Optional[grpc.Channel] = None | ||||
|         self.handling_throughputs: Optional[grpc.Future] = None | ||||
|         self.handling_cpu_usage: Optional[grpc.Future] = None | ||||
|         self.handling_events: Optional[grpc.Future] = None | ||||
|         self.xml_dir: Optional[str] = None | ||||
|         self.xml_file: Optional[str] = None | ||||
| 
 | ||||
|  | @ -111,6 +114,7 @@ class CoreClient: | |||
|                 ) | ||||
|                 if throughputs_enabled: | ||||
|                     self.enable_throughputs() | ||||
|             self.setup_cpu_usage() | ||||
|         return self._client | ||||
| 
 | ||||
|     def reset(self) -> None: | ||||
|  | @ -258,6 +262,20 @@ class CoreClient: | |||
|             self.handling_events.cancel() | ||||
|             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: | ||||
|         if event.session_id != self.session_id: | ||||
|             logging.warning( | ||||
|  | @ -269,6 +287,9 @@ class CoreClient: | |||
|         logging.debug("handling throughputs event: %s", 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: | ||||
|         logging.info("exception event: %s", event) | ||||
|         self.app.statusbar.add_alert(event) | ||||
|  | @ -479,6 +500,8 @@ class CoreClient: | |||
|         """ | ||||
|         try: | ||||
|             self.client.connect() | ||||
|             self.setup_cpu_usage() | ||||
| 
 | ||||
|             # get service information | ||||
|             response = self.client.get_services() | ||||
|             for service in response.services: | ||||
|  |  | |||
|  | @ -1,10 +1,7 @@ | |||
| """ | ||||
| status bar | ||||
| """ | ||||
| import sched | ||||
| import tkinter as tk | ||||
| from pathlib import Path | ||||
| from threading import Thread | ||||
| from tkinter import ttk | ||||
| from typing import TYPE_CHECKING, List, Optional | ||||
| 
 | ||||
|  | @ -16,41 +13,6 @@ if TYPE_CHECKING: | |||
|     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): | ||||
|     def __init__(self, master: tk.Widget, app: "Application") -> None: | ||||
|         super().__init__(master) | ||||
|  | @ -64,8 +26,6 @@ class StatusBar(ttk.Frame): | |||
|         self.running: bool = False | ||||
|         self.core_alarms: List[ExceptionEvent] = [] | ||||
|         self.draw() | ||||
|         self.cpu_usage: CpuUsage = CpuUsage(self) | ||||
|         self.cpu_usage.start() | ||||
| 
 | ||||
|     def draw(self) -> None: | ||||
|         self.columnconfigure(0, weight=7) | ||||
|  |  | |||
|  | @ -305,10 +305,11 @@ class Toolbar(ttk.Frame): | |||
|             self.set_runtime() | ||||
|             self.app.core.set_metadata() | ||||
|             self.app.core.show_mobility_players() | ||||
|         elif response.exceptions: | ||||
|         else: | ||||
|             enable_buttons(self.design_frame, enabled=True) | ||||
|             message = "\n".join(response.exceptions) | ||||
|             self.app.show_error("Start Session Error", message) | ||||
|             if response.exceptions: | ||||
|                 message = "\n".join(response.exceptions) | ||||
|                 self.app.show_error("Start Session Error", message) | ||||
| 
 | ||||
|     def set_runtime(self) -> None: | ||||
|         enable_buttons(self.runtime_frame, enabled=True) | ||||
|  |  | |||
|  | @ -51,6 +51,8 @@ service CoreApi { | |||
|     } | ||||
|     rpc Throughputs (ThroughputsRequest) returns (stream ThroughputsEvent) { | ||||
|     } | ||||
|     rpc CpuUsage (CpuUsageRequest) returns (stream CpuUsageEvent) { | ||||
|     } | ||||
| 
 | ||||
|     // node rpc | ||||
|     rpc AddNode (AddNodeRequest) returns (AddNodeResponse) { | ||||
|  | @ -347,6 +349,14 @@ message ThroughputsEvent { | |||
|     repeated InterfaceThroughput iface_throughputs = 3; | ||||
| } | ||||
| 
 | ||||
| message CpuUsageRequest { | ||||
|     int32 delay = 1; | ||||
| } | ||||
| 
 | ||||
| message CpuUsageEvent { | ||||
|     double usage = 1; | ||||
| } | ||||
| 
 | ||||
| message InterfaceThroughput { | ||||
|     int32 node_id = 1; | ||||
|     int32 iface_id = 2; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue