fix: Switch to delay-based delivery — wantAck firmware retries cause duplicates

This commit is contained in:
K7ZVX 2026-05-07 21:50:09 +00:00
commit 914c21e167

View file

@ -1,93 +1,53 @@
"""Response handling - delays and message delivery.""" """Response handling - delays and message delivery."""
import asyncio import asyncio
import logging import logging
import random import random
from typing import Optional from typing import Optional
from .config import ResponseConfig from .config import ResponseConfig
from .connector import MeshConnector from .connector import MeshConnector
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Responder: class Responder:
"""Handles response delivery with pacing.""" """Handles response delivery with pacing."""
def __init__(self, config: ResponseConfig, connector: MeshConnector): def __init__(self, config: ResponseConfig, connector: MeshConnector):
self.config = config self.config = config
self.connector = connector self.connector = connector
async def send_response( async def send_response(
self, self,
messages: list[str] | str, messages: list[str] | str,
destination: Optional[str] = None, destination: Optional[str] = None,
channel: int = 0, channel: int = 0,
) -> bool: ) -> bool:
"""Send response messages with ACK-accelerated delivery. """Send response messages with randomized delay pacing."""
if isinstance(messages, str):
DMs: Send -> wait for ACK -> if ACK, send next immediately. messages = [messages]
If no ACK, retry once -> if still no ACK, abort.
Broadcasts: delay-based pacing (no ACK available). if not messages:
""" return True
if isinstance(messages, str):
messages = [messages] success = True
if not messages: for i, msg in enumerate(messages):
return True if i > 0:
delay = random.uniform(self.config.delay_min, self.config.delay_max)
success = True await asyncio.sleep(delay)
is_dm = destination is not None
sent = self.connector.send_message(
for i, msg in enumerate(messages): text=msg,
if is_dm and hasattr(self.connector, 'send_and_wait_ack'): destination=destination,
# Send and wait for ACK channel=channel,
ack = await asyncio.get_event_loop().run_in_executor( )
None, if not sent:
self.connector.send_and_wait_ack, logger.error(f"Failed to send message {i+1}/{len(messages)}")
msg, destination, channel, 30.0, success = False
) break
if ack: logger.debug(f"Sent msg {i+1}/{len(messages)}: {msg[:50]}...")
# ACK received - next message sends immediately (no delay)
logger.debug(f"ACK msg {i+1}/{len(messages)}: {msg[:50]}...") return success
continue
# No ACK - retry same message once after short pause
logger.warning(f"No ACK for msg {i+1}/{len(messages)}, retrying...")
await asyncio.sleep(2.0)
ack = await asyncio.get_event_loop().run_in_executor(
None,
self.connector.send_and_wait_ack,
msg, destination, channel, 30.0,
)
if ack:
logger.debug(f"ACK on retry msg {i+1}/{len(messages)}")
continue
# Double failure - abort
logger.error(f"No ACK after retry msg {i+1}/{len(messages)}, aborting remaining")
success = False
break
else:
# Broadcasts or fallback: delay-based pacing
if i > 0:
delay = random.uniform(self.config.delay_min, self.config.delay_max)
await asyncio.sleep(delay)
sent = self.connector.send_message(
text=msg,
destination=destination,
channel=channel,
)
if not sent:
logger.error(f"Failed to send message {i+1}/{len(messages)}")
success = False
break
logger.debug(f"Sent msg {i+1}/{len(messages)}: {msg[:50]}...")
return success