feat(darc_mowas_service): Re-transmit bit identical messages

For the emergency communication, we need to ensure that all messages are
transmitted (and received) correctly. We already check if a repeater
acks our message, however a missing ack does not tell us if the message
was lost, or the ack. For that, we repeat the message in this case,
leading to potentially content-wise duplicated receptions on the network.

To let the network depulicate the traffic, the re-transmissions need to
be bit identical with the original message. For that, we compute a
timestamp on initial send and use the same timestamp on
re-transmissions (per message). This further allows clients to
chronologically sort the received messages, making the result easier to
read.

Signed-off-by: Felix Moessbauer <felix.moessbauer@gmail.com>
This commit is contained in:
Felix Moessbauer
2026-05-16 16:19:22 +02:00
parent db8a519b95
commit 48b3f4e6bd
@@ -19,7 +19,7 @@ import time
import xml.dom.minidom
from asyncio import AbstractEventLoop
from dataclasses import dataclass
from datetime import datetime
from datetime import datetime, timedelta
from typing import Any, cast
import aiohttp
@@ -176,11 +176,14 @@ class DARC_MoWaS_Service(BaseServicePlugin):
Send all chunks, each with async retry if configured.
Note, that we cannot guarantee that the messages arrive in-order,
but as the chunks have a (x/n) identifier at the end, the user still
should be able to grasp the message correctly.
should be able to grasp the message correctly. We further add
an ascending timestamp to each message (also used on re-transmission) to
let the client restore the order, as well as deduplicate retransmissions.
Ideally this chunking should be implemented at protocol level to
guarantee the atomicity and order of the full message.
"""
ts_now = datetime.now()
for i, chunk in enumerate(chunks):
asyncio.create_task(
self._send_chunk_with_retry(
@@ -188,6 +191,7 @@ class DARC_MoWaS_Service(BaseServicePlugin):
chunk,
i,
len(chunks),
ts_now + timedelta(seconds=1)
)
)
@@ -197,6 +201,7 @@ class DARC_MoWaS_Service(BaseServicePlugin):
chunk: str,
index: int,
total: int,
timestamp: datetime,
) -> None:
"""Send a chunk and retry until acked or retries exhausted."""
tracker = getattr(self.bot, "transmission_tracker", None)
@@ -210,6 +215,7 @@ class DARC_MoWaS_Service(BaseServicePlugin):
chunk,
command_id=cmd_id,
skip_user_rate_limit=True,
timestamp=timestamp
):
self.logger.warning("Send failed for '%s'", channel)
return