mirror of
https://github.com/meshcore-dev/meshcore_py.git
synced 2026-06-11 11:56:18 +00:00
G3: F02 — inject reconnect callback for send_appstart after reconnect
ConnectionManager._attempt_reconnect called self.connection.connect() directly, bypassing MeshCore.connect() which runs send_appstart(). Firmware requires CMD_APP_START after every transport-level connection to initialize the session. Without it, the reconnected transport has no active session — sends go unanswered, tcp_no_response fires after 5 attempts, handle_disconnect re-enters _attempt_reconnect, and the reconnect storm begins. Fix: add an optional reconnect_callback parameter to ConnectionManager.__init__. MeshCore passes self._on_reconnect which calls send_appstart() after the transport reconnects. The callback is invoked inside _attempt_reconnect immediately after a successful connect(), before the CONNECTED event is emitted. Callback failures are logged as warnings but do not break the reconnect — the transport is up regardless. Default None keeps the API backwards-compatible for direct ConnectionManager users. Refs: Forensics report finding F02
This commit is contained in:
@@ -48,11 +48,13 @@ class ConnectionManager:
|
|||||||
event_dispatcher=None,
|
event_dispatcher=None,
|
||||||
auto_reconnect: bool = False,
|
auto_reconnect: bool = False,
|
||||||
max_reconnect_attempts: int = 3,
|
max_reconnect_attempts: int = 3,
|
||||||
|
reconnect_callback: Optional[Callable[[], Awaitable[None]]] = None,
|
||||||
):
|
):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.event_dispatcher = event_dispatcher
|
self.event_dispatcher = event_dispatcher
|
||||||
self.auto_reconnect = auto_reconnect
|
self.auto_reconnect = auto_reconnect
|
||||||
self.max_reconnect_attempts = max_reconnect_attempts
|
self.max_reconnect_attempts = max_reconnect_attempts
|
||||||
|
self._reconnect_callback = reconnect_callback
|
||||||
|
|
||||||
self._reconnect_attempts = 0
|
self._reconnect_attempts = 0
|
||||||
self._is_connected = False
|
self._is_connected = False
|
||||||
@@ -139,6 +141,16 @@ class ConnectionManager:
|
|||||||
if result is not None:
|
if result is not None:
|
||||||
self._is_connected = True
|
self._is_connected = True
|
||||||
self._reconnect_attempts = 0
|
self._reconnect_attempts = 0
|
||||||
|
|
||||||
|
# Invoke reconnect callback (e.g. send_appstart) if provided
|
||||||
|
if self._reconnect_callback is not None:
|
||||||
|
try:
|
||||||
|
await self._reconnect_callback()
|
||||||
|
except Exception as cb_err:
|
||||||
|
logger.warning(
|
||||||
|
f"Reconnect callback failed: {cb_err}"
|
||||||
|
)
|
||||||
|
|
||||||
await self._emit_event(
|
await self._emit_event(
|
||||||
EventType.CONNECTED,
|
EventType.CONNECTED,
|
||||||
{"connection_info": result, "reconnected": True},
|
{"connection_info": result, "reconnected": True},
|
||||||
|
|||||||
@@ -28,10 +28,17 @@ class MeshCore:
|
|||||||
auto_reconnect: bool = False,
|
auto_reconnect: bool = False,
|
||||||
max_reconnect_attempts: int = 3,
|
max_reconnect_attempts: int = 3,
|
||||||
):
|
):
|
||||||
# Wrap connection with ConnectionManager
|
# Wrap connection with ConnectionManager.
|
||||||
|
# The reconnect callback ensures send_appstart() runs after every
|
||||||
|
# transport-level reconnect, which is required by firmware to
|
||||||
|
# initialize the session (F02).
|
||||||
self.dispatcher = EventDispatcher()
|
self.dispatcher = EventDispatcher()
|
||||||
self.connection_manager = ConnectionManager(
|
self.connection_manager = ConnectionManager(
|
||||||
cx, self.dispatcher, auto_reconnect, max_reconnect_attempts
|
cx,
|
||||||
|
self.dispatcher,
|
||||||
|
auto_reconnect,
|
||||||
|
max_reconnect_attempts,
|
||||||
|
reconnect_callback=self._on_reconnect,
|
||||||
)
|
)
|
||||||
self.cx = self.connection_manager # For backward compatibility
|
self.cx = self.connection_manager # For backward compatibility
|
||||||
|
|
||||||
@@ -174,6 +181,15 @@ class MeshCore:
|
|||||||
return None
|
return None
|
||||||
return mc
|
return mc
|
||||||
|
|
||||||
|
async def _on_reconnect(self):
|
||||||
|
"""Callback invoked by ConnectionManager after a successful reconnect.
|
||||||
|
|
||||||
|
Firmware requires CMD_APP_START after every transport-level connection
|
||||||
|
to initialize the session. MeshCore.connect() does this on the initial
|
||||||
|
connection; this callback ensures it also happens on reconnects (F02).
|
||||||
|
"""
|
||||||
|
await self.commands.send_appstart()
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
await self.dispatcher.start()
|
await self.dispatcher.start()
|
||||||
result = await self.connection_manager.connect()
|
result = await self.connection_manager.connect()
|
||||||
|
|||||||
Reference in New Issue
Block a user