proc_stats: add annotations
Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
c977948c2c
commit
12246029ef
|
@ -3,6 +3,8 @@
|
||||||
# Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
|
# Copyright (C) 2021 Eric Callahan <arksine.code@gmail.com>
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
@ -12,6 +14,20 @@ from collections import deque
|
||||||
from tornado.ioloop import IOLoop, PeriodicCallback
|
from tornado.ioloop import IOLoop, PeriodicCallback
|
||||||
from tornado.locks import Lock
|
from tornado.locks import Lock
|
||||||
|
|
||||||
|
# Annotation imports
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Deque,
|
||||||
|
Any,
|
||||||
|
Tuple,
|
||||||
|
Optional,
|
||||||
|
Dict,
|
||||||
|
)
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from confighelper import ConfigHelper
|
||||||
|
from websockets import WebRequest
|
||||||
|
from . import shell_command
|
||||||
|
|
||||||
VC_GEN_CMD_FILE = "/usr/bin/vcgencmd"
|
VC_GEN_CMD_FILE = "/usr/bin/vcgencmd"
|
||||||
STATM_FILE_PATH = "/proc/self/smaps_rollup"
|
STATM_FILE_PATH = "/proc/self/smaps_rollup"
|
||||||
TEMPERATURE_PATH = "/sys/class/thermal/thermal_zone0/temp"
|
TEMPERATURE_PATH = "/sys/class/thermal/thermal_zone0/temp"
|
||||||
|
@ -31,16 +47,17 @@ THROTTLED_FLAGS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProcStats:
|
class ProcStats:
|
||||||
def __init__(self, config):
|
def __init__(self, config: ConfigHelper) -> None:
|
||||||
self.server = config.get_server()
|
self.server = config.get_server()
|
||||||
self.ioloop = IOLoop.current()
|
self.ioloop = IOLoop.current()
|
||||||
self.stat_update_cb = PeriodicCallback(
|
self.stat_update_cb = PeriodicCallback(
|
||||||
self._handle_stat_update, STAT_UPDATE_TIME_MS)
|
self._handle_stat_update, STAT_UPDATE_TIME_MS) # type: ignore
|
||||||
self.vcgencmd = None
|
self.vcgencmd: Optional[shell_command.ShellCommand] = None
|
||||||
if os.path.exists(VC_GEN_CMD_FILE):
|
if os.path.exists(VC_GEN_CMD_FILE):
|
||||||
logging.info("Detected 'vcgencmd', throttle checking enabled")
|
logging.info("Detected 'vcgencmd', throttle checking enabled")
|
||||||
shell_command = self.server.load_component(config, "shell_command")
|
shell_cmd: shell_command.ShellCommandFactory
|
||||||
self.vcgencmd = shell_command.build_shell_command(
|
shell_cmd = self.server.load_component(config, "shell_command")
|
||||||
|
self.vcgencmd = shell_cmd.build_shell_command(
|
||||||
"vcgencmd get_throttled")
|
"vcgencmd get_throttled")
|
||||||
self.server.register_notification("proc_stats:cpu_throttled")
|
self.server.register_notification("proc_stats:cpu_throttled")
|
||||||
else:
|
else:
|
||||||
|
@ -53,16 +70,19 @@ class ProcStats:
|
||||||
self.server.register_event_handler(
|
self.server.register_event_handler(
|
||||||
"server:klippy_shutdown", self._handle_shutdown)
|
"server:klippy_shutdown", self._handle_shutdown)
|
||||||
self.server.register_notification("proc_stats:proc_stat_update")
|
self.server.register_notification("proc_stats:proc_stat_update")
|
||||||
self.proc_stat_queue = deque(maxlen=30)
|
self.proc_stat_queue: Deque[Dict[str, Any]] = deque(maxlen=30)
|
||||||
self.last_update_time = time.time()
|
self.last_update_time = time.time()
|
||||||
self.last_proc_time = time.process_time()
|
self.last_proc_time = time.process_time()
|
||||||
self.throttle_check_lock = Lock()
|
self.throttle_check_lock = Lock()
|
||||||
self.total_throttled = self.last_throttled = 0
|
self.total_throttled: int = 0
|
||||||
self.update_sequence = 0
|
self.last_throttled: int = 0
|
||||||
|
self.update_sequence: int = 0
|
||||||
self.stat_update_cb.start()
|
self.stat_update_cb.start()
|
||||||
|
|
||||||
async def _handle_stat_request(self, web_request):
|
async def _handle_stat_request(self,
|
||||||
ts = None
|
web_request: WebRequest
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
ts: Optional[Dict[str, Any]] = None
|
||||||
if self.vcgencmd is not None:
|
if self.vcgencmd is not None:
|
||||||
ts = await self._check_throttled_state()
|
ts = await self._check_throttled_state()
|
||||||
return {
|
return {
|
||||||
|
@ -71,7 +91,7 @@ class ProcStats:
|
||||||
'cpu_temp': self._get_cpu_temperature()
|
'cpu_temp': self._get_cpu_temperature()
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _handle_shutdown(self):
|
async def _handle_shutdown(self) -> None:
|
||||||
msg = "\nMoonraker System Usage Statistics:"
|
msg = "\nMoonraker System Usage Statistics:"
|
||||||
for stats in self.proc_stat_queue:
|
for stats in self.proc_stat_queue:
|
||||||
msg += f"\n{self._format_stats(stats)}"
|
msg += f"\n{self._format_stats(stats)}"
|
||||||
|
@ -81,7 +101,7 @@ class ProcStats:
|
||||||
ts = await self._check_throttled_state()
|
ts = await self._check_throttled_state()
|
||||||
logging.info(f"Throttled Flags: {' '.join(ts['flags'])}")
|
logging.info(f"Throttled Flags: {' '.join(ts['flags'])}")
|
||||||
|
|
||||||
async def _handle_stat_update(self):
|
async def _handle_stat_update(self) -> None:
|
||||||
update_time = time.time()
|
update_time = time.time()
|
||||||
proc_time = time.process_time()
|
proc_time = time.process_time()
|
||||||
time_diff = update_time - self.last_update_time
|
time_diff = update_time - self.last_update_time
|
||||||
|
@ -115,8 +135,9 @@ class ProcStats:
|
||||||
self.last_throttled = cur_throttled
|
self.last_throttled = cur_throttled
|
||||||
self.total_throttled |= cur_throttled
|
self.total_throttled |= cur_throttled
|
||||||
|
|
||||||
async def _check_throttled_state(self):
|
async def _check_throttled_state(self) -> Dict[str, Any]:
|
||||||
async with self.throttle_check_lock:
|
async with self.throttle_check_lock:
|
||||||
|
assert self.vcgencmd is not None
|
||||||
try:
|
try:
|
||||||
resp = await self.vcgencmd.run_with_response(
|
resp = await self.vcgencmd.run_with_response(
|
||||||
timeout=.5, log_complete=False)
|
timeout=.5, log_complete=False)
|
||||||
|
@ -129,7 +150,7 @@ class ProcStats:
|
||||||
flags.append(desc)
|
flags.append(desc)
|
||||||
return {'bits': ts, 'flags': flags}
|
return {'bits': ts, 'flags': flags}
|
||||||
|
|
||||||
def _get_memory_usage(self):
|
def _get_memory_usage(self) -> Tuple[Optional[int], Optional[str]]:
|
||||||
try:
|
try:
|
||||||
mem_data = self.smaps.read_text()
|
mem_data = self.smaps.read_text()
|
||||||
rss_match = re.search(r"Rss:\s+(\d+)\s+(\w+)", mem_data)
|
rss_match = re.search(r"Rss:\s+(\d+)\s+(\w+)", mem_data)
|
||||||
|
@ -141,24 +162,23 @@ class ProcStats:
|
||||||
return None, None
|
return None, None
|
||||||
return mem, units
|
return mem, units
|
||||||
|
|
||||||
def _get_cpu_temperature(self):
|
def _get_cpu_temperature(self) -> Optional[float]:
|
||||||
temp = None
|
temp = None
|
||||||
if self.temp_file.exists():
|
if self.temp_file.exists():
|
||||||
try:
|
try:
|
||||||
temp = int(self.temp_file.read_text().strip())
|
res = int(self.temp_file.read_text().strip())
|
||||||
temp = temp / 1000.
|
temp = res / 1000.
|
||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
|
def _format_stats(self, stats: Dict[str, Any]) -> str:
|
||||||
def _format_stats(self, stats):
|
|
||||||
return f"System Time: {stats['time']:2f}, " \
|
return f"System Time: {stats['time']:2f}, " \
|
||||||
f"Usage: {stats['cpu_usage']}%, " \
|
f"Usage: {stats['cpu_usage']}%, " \
|
||||||
f"Memory: {stats['memory']} {stats['mem_units']}"
|
f"Memory: {stats['memory']} {stats['mem_units']}"
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
self.stat_update_cb.stop()
|
self.stat_update_cb.stop()
|
||||||
|
|
||||||
def load_component(config):
|
def load_component(config: ConfigHelper) -> ProcStats:
|
||||||
return ProcStats(config)
|
return ProcStats(config)
|
||||||
|
|
Loading…
Reference in New Issue