CI / backend (pull_request) Canceled after 0s
CI / shell (pull_request) Canceled after 0s
CI / frontend (pull_request) Canceled after 0s
CI / arm64-smoke (pull_request) Canceled after 0s
CI / backend (push) Canceled after 0s
CI / shell (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / arm64-smoke (push) Canceled after 0s
79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import deque
|
|
from dataclasses import dataclass
|
|
from datetime import UTC, datetime
|
|
from time import monotonic
|
|
|
|
import psutil
|
|
|
|
from app.dmx.backends import BackendStatus
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class TelemetryEvent:
|
|
created_at: datetime
|
|
category: str
|
|
level: str
|
|
message: str
|
|
|
|
|
|
class TelemetryService:
|
|
def __init__(self) -> None:
|
|
self.started_at = monotonic()
|
|
self.events: deque[TelemetryEvent] = deque(maxlen=1000)
|
|
self.last_sent_at: datetime | None = None
|
|
self.last_error: str | None = None
|
|
self.reconnect_count = 0
|
|
self.frames_sent = 0
|
|
self.send_errors = 0
|
|
|
|
def record_send_success(self, status: BackendStatus) -> None:
|
|
self.last_sent_at = datetime.now(UTC)
|
|
self.last_error = None
|
|
self.reconnect_count = status.reconnect_count
|
|
self.frames_sent = status.frames_sent
|
|
self.send_errors = status.send_errors
|
|
|
|
def record_send_failure(self, error: str, status: BackendStatus) -> None:
|
|
self.last_error = error
|
|
self.reconnect_count = status.reconnect_count
|
|
self.frames_sent = status.frames_sent
|
|
self.send_errors = status.send_errors
|
|
self.events.append(
|
|
TelemetryEvent(datetime.now(UTC), "DMX", "ERROR", error)
|
|
)
|
|
|
|
def snapshot(self, queue_depth: int = 0) -> dict[str, object]:
|
|
temperature = None
|
|
try:
|
|
readings = psutil.sensors_temperatures()
|
|
if readings:
|
|
first = next(iter(readings.values()))
|
|
if first:
|
|
temperature = float(first[0].current)
|
|
except Exception:
|
|
temperature = None
|
|
|
|
return {
|
|
"uptime_seconds": round(monotonic() - self.started_at, 2),
|
|
"cpu_percent": psutil.cpu_percent(interval=None),
|
|
"ram_percent": psutil.virtual_memory().percent,
|
|
"temperature_c": temperature,
|
|
"queue_depth": queue_depth,
|
|
"reconnect_count": self.reconnect_count,
|
|
"last_error": self.last_error,
|
|
"last_sent_at": self.last_sent_at,
|
|
"frames_sent": self.frames_sent,
|
|
"send_errors": self.send_errors,
|
|
"events": [
|
|
{
|
|
"created_at": event.created_at.isoformat(),
|
|
"category": event.category,
|
|
"level": event.level,
|
|
"message": event.message,
|
|
}
|
|
for event in list(self.events)[-50:]
|
|
],
|
|
}
|