mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
route startup and fanout through radio runtime
This commit is contained in:
@@ -77,7 +77,7 @@ app/
|
||||
|
||||
- `RadioManager.start_connection_monitor()` checks health every 5s.
|
||||
- `RadioManager.post_connect_setup()` delegates to `services/radio_lifecycle.py`.
|
||||
- Routers and shared dependencies should reach radio state through `services/radio_runtime.py`, not by importing `app.radio.radio_manager` directly.
|
||||
- Routers, startup/lifespan code, and fanout helpers should reach radio state through `services/radio_runtime.py`, not by importing `app.radio.radio_manager` directly.
|
||||
- Shared reconnect/setup helpers in `services/radio_lifecycle.py` are used by startup, the monitor, and manual reconnect/reboot flows before broadcasting healthy state.
|
||||
- Setup still includes handler registration, key export, time sync, contact/channel sync, polling/advert tasks.
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ def _build_radio_info() -> str:
|
||||
Matches the reference format: ``"freq,bw,sf,cr"`` (comma-separated raw
|
||||
values). Falls back to ``"0,0,0,0"`` when unavailable.
|
||||
"""
|
||||
from app.radio import radio_manager
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
try:
|
||||
if radio_manager.meshcore and radio_manager.meshcore.self_info:
|
||||
@@ -329,7 +329,7 @@ class CommunityMqttPublisher(BaseMqttPublisher):
|
||||
def _build_client_kwargs(self, settings: object) -> dict[str, Any]:
|
||||
s: CommunityMqttSettings = settings # type: ignore[assignment]
|
||||
from app.keystore import get_private_key, get_public_key
|
||||
from app.radio import radio_manager
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
private_key = get_private_key()
|
||||
public_key = get_public_key()
|
||||
@@ -401,7 +401,8 @@ class CommunityMqttPublisher(BaseMqttPublisher):
|
||||
if self._cached_device_info is not None:
|
||||
return self._cached_device_info
|
||||
|
||||
from app.radio import RadioDisconnectedError, RadioOperationBusyError, radio_manager
|
||||
from app.radio import RadioDisconnectedError, RadioOperationBusyError
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
fallback = {"model": "unknown", "firmware_version": "unknown"}
|
||||
try:
|
||||
@@ -448,7 +449,8 @@ class CommunityMqttPublisher(BaseMqttPublisher):
|
||||
) < _STATS_MIN_CACHE_SECS and self._cached_stats is not None:
|
||||
return self._cached_stats
|
||||
|
||||
from app.radio import RadioDisconnectedError, RadioOperationBusyError, radio_manager
|
||||
from app.radio import RadioDisconnectedError, RadioOperationBusyError
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
try:
|
||||
async with radio_manager.radio_operation("community_stats_fetch", blocking=False) as mc:
|
||||
@@ -489,7 +491,7 @@ class CommunityMqttPublisher(BaseMqttPublisher):
|
||||
) -> None:
|
||||
"""Build and publish the enriched retained status message."""
|
||||
from app.keystore import get_public_key
|
||||
from app.radio import radio_manager
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
public_key = get_public_key()
|
||||
if public_key is None:
|
||||
|
||||
@@ -25,7 +25,7 @@ _BACKOFF_MIN = 5
|
||||
|
||||
def _broadcast_health() -> None:
|
||||
"""Push updated health (including MQTT status) to all WS clients."""
|
||||
from app.radio import radio_manager
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
from app.websocket import broadcast_health
|
||||
|
||||
broadcast_health(radio_manager.is_connected, radio_manager.connection_info)
|
||||
|
||||
@@ -109,7 +109,7 @@ async def _publish_community_packet(
|
||||
"""Format and publish a raw packet to the community broker."""
|
||||
try:
|
||||
from app.keystore import get_public_key
|
||||
from app.radio import radio_manager
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
public_key = get_public_key()
|
||||
if public_key is None:
|
||||
|
||||
10
app/main.py
10
app/main.py
@@ -10,7 +10,7 @@ from fastapi.responses import JSONResponse
|
||||
from app.config import setup_logging
|
||||
from app.database import db
|
||||
from app.frontend_static import register_frontend_missing_fallback, register_frontend_static_routes
|
||||
from app.radio import RadioDisconnectedError, radio_manager
|
||||
from app.radio import RadioDisconnectedError
|
||||
from app.radio_sync import (
|
||||
stop_message_polling,
|
||||
stop_periodic_advert,
|
||||
@@ -30,6 +30,7 @@ from app.routers import (
|
||||
statistics,
|
||||
ws,
|
||||
)
|
||||
from app.services.radio_runtime import radio_runtime as radio_manager
|
||||
|
||||
setup_logging()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -37,13 +38,8 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
async def _startup_radio_connect_and_setup() -> None:
|
||||
"""Connect/setup the radio in the background so HTTP serving can start immediately."""
|
||||
from app.services.radio_lifecycle import reconnect_and_prepare_radio
|
||||
|
||||
try:
|
||||
connected = await reconnect_and_prepare_radio(
|
||||
radio_manager,
|
||||
broadcast_on_success=True,
|
||||
)
|
||||
connected = await radio_manager.reconnect_and_prepare(broadcast_on_success=True)
|
||||
if connected:
|
||||
logger.info("Connected to radio")
|
||||
else:
|
||||
|
||||
@@ -83,6 +83,15 @@ class RadioRuntime:
|
||||
async with self.manager.radio_operation(name, **kwargs) as mc:
|
||||
yield mc
|
||||
|
||||
async def start_connection_monitor(self) -> None:
|
||||
await self.manager.start_connection_monitor()
|
||||
|
||||
async def stop_connection_monitor(self) -> None:
|
||||
await self.manager.stop_connection_monitor()
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
await self.manager.disconnect()
|
||||
|
||||
async def prepare_connected(self, *, broadcast_on_success: bool = True) -> None:
|
||||
from app.services.radio_lifecycle import prepare_connected_radio
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from contextlib import asynccontextmanager
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
@@ -73,3 +74,20 @@ async def test_radio_operation_delegates_to_current_manager():
|
||||
assert mc == "meshcore"
|
||||
|
||||
assert manager.calls == [("sync_contacts", {"pause_polling": True})]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lifecycle_passthrough_methods_delegate_to_current_manager():
|
||||
manager = _Manager(meshcore="meshcore", is_connected=True)
|
||||
manager.start_connection_monitor = AsyncMock()
|
||||
manager.stop_connection_monitor = AsyncMock()
|
||||
manager.disconnect = AsyncMock()
|
||||
runtime = RadioRuntime(manager)
|
||||
|
||||
await runtime.start_connection_monitor()
|
||||
await runtime.stop_connection_monitor()
|
||||
await runtime.disconnect()
|
||||
|
||||
manager.start_connection_monitor.assert_awaited_once()
|
||||
manager.stop_connection_monitor.assert_awaited_once()
|
||||
manager.disconnect.assert_awaited_once()
|
||||
|
||||
Reference in New Issue
Block a user