Skip to content

Conversation

@patrickelectric
Copy link
Member

@patrickelectric patrickelectric commented Dec 19, 2025

Summary by Sourcery

Introduce a Health Monitor backend service and frontend UI to track and surface system and vehicle health issues.

New Features:

  • Add Health Monitor backend service exposing health summaries, history, and configuration via a FastAPI API.
  • Add frontend Health Monitor view with filtering and tabbed display of active issues and historical events.
  • Expose the Health Monitor service through navigation menus, routing, and nginx/Vite proxy configuration.

Enhancements:

  • Implement reusable monitoring utilities for disks, memory, kernel errors, USB devices, network packet loss, extension resource usage, voltage, factory mode, and software update availability.
  • Add a reusable disk usage treemap component for visualizing directory sizes.

Build:

  • Register the health_monitor Python package in the core workspace and declare its dependencies.

Tests:

  • Add unit tests for disk evaluation, event formatting, and result merging in the Health Monitor service.

@sourcery-ai
Copy link

sourcery-ai bot commented Dec 19, 2025

Reviewer's Guide

Introduce a new Health Monitor backend service and frontend UI to aggregate system/vehicle health checks, expose them via an HTTP API, publish events over Zenoh, and surface them through a new /tools/health-monitor tool in the UI with filtering and history views.

Sequence diagram for frontend fetching health summary and history

sequenceDiagram
  actor User
  participant HealthMonitorView
  participant HealthMonitorStore
  participant Axios as back_axios
  participant Nginx
  participant HealthAPI as HealthMonitorAPI

  User->>HealthMonitorView: Open /tools/health-monitor
  HealthMonitorView->>HealthMonitorStore: mounted() refresh()
  HealthMonitorStore->>HealthMonitorStore: setLoading(true)

  HealthMonitorStore->>Axios: GET /health-monitor/v1.0/health/summary
  Axios->>Nginx: HTTP request
  Nginx->>HealthAPI: Proxy /health-monitor/...
  HealthAPI-->>Nginx: 200 HealthSummary
  Nginx-->>Axios: 200 HealthSummary
  Axios-->>HealthMonitorStore: response.data
  HealthMonitorStore->>HealthMonitorStore: setSummary(HealthSummary)
  HealthMonitorStore->>HealthMonitorStore: setLoading(false)

  HealthMonitorStore->>Axios: GET /health-monitor/v1.0/health/history?limit=200
  Axios->>Nginx: HTTP request
  Nginx->>HealthAPI: Proxy /health-monitor/...
  HealthAPI-->>Nginx: 200 HealthHistory
  Nginx-->>Axios: 200 HealthHistory
  Axios-->>HealthMonitorStore: response.data
  HealthMonitorStore->>HealthMonitorStore: setHistory(HealthHistory)

  HealthMonitorView->>HealthMonitorView: Compute filteredActive, filteredHistory
  HealthMonitorView-->>User: Render tables with filters
Loading

Class diagram for health_monitor backend service

classDiagram
  class HealthProblem {
    +str id
    +str severity
    +str title
    +str details
    +str source
    +int timestamp
    +Dict metadata
    +int first_seen_ms
    +int last_seen_ms
  }

  class HealthEvent {
    +str id
    +str severity
    +str title
    +str details
    +str source
    +int timestamp
    +Dict metadata
    +int first_seen_ms
    +int last_seen_ms
    +str type
  }

  class HealthSummary {
    +List~HealthProblem~ active
    +int updated_at
  }

  class HealthHistory {
    +List~HealthEvent~ events
  }

  class HealthCheckResult {
    +Dict~str,HealthProblem~ active
    +Dict~str,HealthProblem~ resolved
  }

  class ProblemRecord {
    +HealthProblem problem
    +int first_seen_ms
    +int last_seen_ms
  }

  class HealthStateTracker {
    -Dict~str,ProblemRecord~ _active
    +diff_and_update(new_active, resolved_info) List~HealthEvent~
    +active_problems() List~HealthProblem~
    +_event_from_problem(event_type, problem, first_seen_ms, last_seen_ms) HealthEvent
    +_build_resolved_problem(problem, timestamp) HealthProblem
  }

  class KernelErrorTracker {
    +set ERROR_LEVELS
    -int _window_ms
    -int _last_sequence
    -int _last_error_ms
    -str _last_message
    -str _last_level
    +evaluate(messages, now) HealthCheckResult
  }

  class UsbTracker {
    -Dict~str,Dict~str,Any~~ _known_devices
    -Dict~str,Dict~str,Any~~ _disconnected_devices
    -bool _initialized
    +evaluate(serial_ports, now) HealthCheckResult
    +_problem_id(device_key) str
    +_device_key(port) str
    +_safe_str(value) str
  }

  class VersionComparator {
    +fix_version(tag) str
    +is_semver(tag) bool
    +compare(a, b) int
    +latest_semver(tags) str
    +_parse_semver(tag) Tuple
  }

  class HealthConfig {
    +float interval_sec
    +int history_limit
    +int disk_free_bytes
    +float disk_free_percent
    +float memory_warn_percent
    +float memory_error_percent
    +int kernel_error_window_ms
    +float packet_loss_ratio
    +int packet_loss_count
    +float extension_cpu_percent
    +float extension_memory_percent
    +float extension_disk_percent
    +float voltage_low
    +float voltage_high
  }

  class HealthMonitor {
    -HealthStateTracker _state
    -deque _history
    -KernelErrorTracker _kernel_tracker
    -UsbTracker _usb_tracker
    -ZenohSession _zenoh
    -MavlinkMessenger _mavlink
    -asyncio.Event _stop_event
    -aiohttp.ClientSession _http_session
    +stop()
    +run()
    +evaluate_once()
    +summary() HealthSummary
    +history_view(limit) HealthHistory
    +config() HealthConfig
    +_fetch_json(url, timeout) Any
    +_get_vehicle_sysid() int
    +_get_vehicle_voltage(vehicle_sysid) float
    +_is_factory_mode() bool
    +_bootstrap_tag() str
    +_publish_event(event)
  }

  HealthEvent --|> HealthProblem
  HealthSummary "*" o-- HealthProblem
  HealthHistory "*" o-- HealthEvent
  HealthStateTracker "1" o-- "*" ProblemRecord
  HealthStateTracker ..> HealthCheckResult
  KernelErrorTracker ..> HealthCheckResult
  UsbTracker ..> HealthCheckResult
  HealthMonitor ..> HealthStateTracker
  HealthMonitor ..> KernelErrorTracker
  HealthMonitor ..> UsbTracker
  HealthMonitor ..> HealthSummary
  HealthMonitor ..> HealthHistory
  HealthMonitor ..> HealthConfig
  HealthMonitor ..> HealthCheckResult
Loading

Class diagram for Health Monitor frontend store and view

classDiagram
  class HealthProblemTS {
    +str id
    +HealthSeverity severity
    +str title
    +str details
    +HealthSource source
    +number timestamp
    +Record metadata
    +number first_seen_ms
    +number last_seen_ms
  }

  class HealthEventTS {
    +HealthEventType type
    +str id
    +HealthSeverity severity
    +str title
    +str details
    +HealthSource source
    +number timestamp
    +Record metadata
    +number first_seen_ms
    +number last_seen_ms
  }

  class HealthSummaryTS {
    +HealthProblemTS[] active
    +number updated_at
  }

  class HealthHistoryTS {
    +HealthEventTS[] events
  }

  class HealthMonitorStore {
    +string API_URL
    +HealthSummaryTS summary
    +HealthHistoryTS history
    +boolean loading
    +string error
    +setSummary(value) void
    +setHistory(value) void
    +setLoading(value) void
    +setError(message) void
    +fetchSummary() Promise~void~
    +fetchHistory(limit) Promise~void~
  }

  class HealthMonitorView {
    +number activeTab
    +string selectedSeverity
    +string selectedSource
    +string search
    +string historySeverity
    +string historySource
    +string historySearch
    +number refreshTimer
    +activeHeaders
    +historyHeaders
    +summary
    +history
    +loading
    +error
    +severityOptions
    +sourceOptions
    +filteredActive
    +filteredHistory
    +mounted()
    +beforeDestroy()
    +refresh()
    +matchesFilters(item, severity, source, text) boolean
    +severityColor(severity) string
    +eventColor(eventType) string
    +formatTimestamp(timestamp) string
  }

  HealthEventTS --|> HealthProblemTS
  HealthSummaryTS "*" o-- HealthProblemTS
  HealthHistoryTS "*" o-- HealthEventTS
  HealthMonitorStore --> HealthSummaryTS
  HealthMonitorStore --> HealthHistoryTS
  HealthMonitorView --> HealthMonitorStore
  HealthMonitorView --> HealthSummaryTS
  HealthMonitorView --> HealthHistoryTS
Loading

File-Level Changes

Change Details Files
Expose Health Monitor as a first-class core service and route it through nginx, frontend router, and service metadata.
  • Add Health Monitor entry to the tools menu and Vue router pointing to a new HealthMonitor view.
  • Register health_monitor_service metadata in frontend service types.
  • Proxy /health-monitor requests to the new backend port via nginx and Vite dev-server config.
  • Add health_monitor as a workspace dependency in core pyproject.toml.
core/frontend/src/menus.ts
core/frontend/src/router/index.ts
core/frontend/src/types/frontend_services.ts
core/tools/nginx/nginx.conf
core/frontend/vite.config.js
core/pyproject.toml
Implement Health Monitor backend service providing periodic health evaluations, event publishing, and an HTTP API for summary/history/config.
  • Define core health domain models (problems, events, summaries, histories) and utilities for merging check results.
  • Implement HealthStateTracker to diff new health states versus previous state and emit problem_detected/problem_updated/problem_resolved events with first/last seen timestamps.
  • Implement multiple concrete health checks (disk, memory, kernel errors, USB disconnections, network packet loss, extension resource usage, factory mode, version updates, voltage, SYSID mismatch) plus version comparison helpers and extension container helpers.
  • Implement asynchronous HealthMonitor service that periodically calls linux2rest, Kraken, VersionChooser, Mavlink, aggregates all check results, updates HealthStateTracker, publishes events via Zenoh, and stores a bounded history deque.
  • Expose FastAPI endpoints /v1.0/health/summary, /history, and /config (versioned), plus a root endpoint, and run via uvicorn main() with Sentry initialization and graceful startup/shutdown hooks.
  • Provide basic tests for disk evaluation, event formatting, merge_results, and tracker integration.
  • Add a dedicated pyproject.toml for the health_monitor service with FastAPI, Pydantic, uvicorn, and commonwealth dependencies.
core/services/health_monitor/monitor.py
core/services/health_monitor/main.py
core/services/health_monitor/tests/test_monitor.py
core/services/health_monitor/pyproject.toml
Add frontend Health Monitor Vue view, Vuex store, and shared types to consume the health_monitor API and display active issues and history.
  • Create HealthMonitor.vue with two-tab layout (Active Issues, History) using Vuetify data tables, filter controls (severity, source, text search), severity/event color badges, and periodic auto-refresh.
  • Add Vuex module health_monitor to call /health-monitor/v1.0/health/summary and /history, manage loading/error, and expose summary/history state.
  • Introduce TypeScript types for health-monitor data structures (HealthProblem, HealthEvent, HealthSummary, HealthHistory, enums for severity/source/event types).
core/frontend/src/views/HealthMonitor.vue
core/frontend/src/store/health_monitor.ts
core/frontend/src/types/health-monitor.ts
Refactor disk usage frontend by adding a reusable treemap component for visualizing disk nodes.
  • Introduce DiskTreemap.vue that renders an ApexCharts treemap for a DiskNode hierarchy, formats byte sizes, shows an empty-state message, and emits selection events with path/dir info.
core/frontend/src/components/disk/DiskTreemap.vue

Possibly linked issues

  • #0: PR introduces the Health Monitor service and UI implementing most of the proposed general health monitoring features.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Signed-off-by: Patrick José Pereira <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant