From c3f6541c8e179fdd2d2e418769bd7139d79e9c93 Mon Sep 17 00:00:00 2001 From: Amir Dahal Date: Sat, 12 Apr 2025 21:22:30 +0530 Subject: [PATCH 1/4] RadioController.send_tnc_data supports fragmentation internally --- src/benlink/controller.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/benlink/controller.py b/src/benlink/controller.py index a9d12aa..e0a4d24 100644 --- a/src/benlink/controller.py +++ b/src/benlink/controller.py @@ -290,14 +290,17 @@ async def rc_battery_level(self) -> int: return await self._conn.get_rc_battery_level() async def send_tnc_data(self, data: bytes) -> None: - if len(data) > 50: - raise ValueError("Data too long -- TODO: implement fragmentation") - - await self._conn.send_tnc_data_fragment(TncDataFragment( - is_final_fragment=True, - fragment_id=0, - data=data - )) + chunk_size = 50 + fragment_id = 0 + for i in range(0, len(data), chunk_size): + chunk = data[i:i+chunk_size] + is_final = (i + chunk_size) >= len(data) # Only last fragment is final + fragment_id += 1 + await self._conn.send_tnc_data_fragment(TncDataFragment( + data=chunk, + fragment_id=fragment_id, + is_final_fragment=is_final + )) def add_event_handler(self, handler: EventHandler) -> t.Callable[[], None]: return self._conn.add_event_handler(handler) From 474398894199c965d2538d521564294e23217f92 Mon Sep 17 00:00:00 2001 From: Amir Dahal Date: Sat, 12 Apr 2025 21:27:40 +0530 Subject: [PATCH 2/4] . --- src/benlink/controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/benlink/controller.py b/src/benlink/controller.py index e0a4d24..00a019d 100644 --- a/src/benlink/controller.py +++ b/src/benlink/controller.py @@ -295,12 +295,12 @@ async def send_tnc_data(self, data: bytes) -> None: for i in range(0, len(data), chunk_size): chunk = data[i:i+chunk_size] is_final = (i + chunk_size) >= len(data) # Only last fragment is final - fragment_id += 1 await self._conn.send_tnc_data_fragment(TncDataFragment( data=chunk, fragment_id=fragment_id, is_final_fragment=is_final )) + fragment_id += 1 def add_event_handler(self, handler: EventHandler) -> t.Callable[[], None]: return self._conn.add_event_handler(handler) From ed978657e5215e30942246dac99ba225e2374782 Mon Sep 17 00:00:00 2001 From: Amir Dahal <68531462+amirdahal@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:23:02 +0530 Subject: [PATCH 3/4] Radio.send_tnc_data [send every packet twice] --- src/benlink/controller.py | 40 +++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/benlink/controller.py b/src/benlink/controller.py index 00a019d..ea80c48 100644 --- a/src/benlink/controller.py +++ b/src/benlink/controller.py @@ -150,6 +150,7 @@ def handle_event(event): from dataclasses import dataclass import typing as t import sys +import asyncio from .command import ( CommandConnection, @@ -291,16 +292,35 @@ async def rc_battery_level(self) -> int: async def send_tnc_data(self, data: bytes) -> None: chunk_size = 50 - fragment_id = 0 - for i in range(0, len(data), chunk_size): - chunk = data[i:i+chunk_size] - is_final = (i + chunk_size) >= len(data) # Only last fragment is final - await self._conn.send_tnc_data_fragment(TncDataFragment( - data=chunk, - fragment_id=fragment_id, - is_final_fragment=is_final - )) - fragment_id += 1 + max_retries = 2 # Total transmissions = 1 original + 1 retry + + # Send twice because first packet always fails + for retry in range(max_retries): + fragment_id = 0 + + for i in range(0, len(packet), chunk_size): + chunk = packet[i:i+chunk_size] + is_final = (i + chunk_size) >= len(packet) + + payload = TncDataFragment( + data=chunk, + fragment_id=fragment_id, + is_final_fragment=is_final, + ) + + command = SendTncDataFragment(payload) + + await self._conn.send_tnc_data_fragment(command) + if fragment_id == 0: + # Add small delay between fragments + await asyncio.sleep(0.01) + await self._conn.send_tnc_data_fragment(command) # Repeat because first fragment always fails + + fragment_id += 1 + + # Add delay between retransmissions if not the last attempt + if retry < max_retries - 1: + await asyncio.sleep(0.1) def add_event_handler(self, handler: EventHandler) -> t.Callable[[], None]: return self._conn.add_event_handler(handler) From f47f9f579a279817ea361d5a16fd26351fe8b60c Mon Sep 17 00:00:00 2001 From: Amir Dahal <68531462+amirdahal@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:25:51 +0530 Subject: [PATCH 4/4] Resend first fragment twice only if number of fragment > 1 --- src/benlink/controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/benlink/controller.py b/src/benlink/controller.py index ea80c48..676a85f 100644 --- a/src/benlink/controller.py +++ b/src/benlink/controller.py @@ -293,14 +293,14 @@ async def rc_battery_level(self) -> int: async def send_tnc_data(self, data: bytes) -> None: chunk_size = 50 max_retries = 2 # Total transmissions = 1 original + 1 retry - + total_fragments = (len(data) + chunk_size - 1) // chunk_size # Send twice because first packet always fails for retry in range(max_retries): fragment_id = 0 - for i in range(0, len(packet), chunk_size): - chunk = packet[i:i+chunk_size] - is_final = (i + chunk_size) >= len(packet) + for i in range(0, len(data), chunk_size): + chunk = data[i:i+chunk_size] + is_final = (i + chunk_size) >= len(data) payload = TncDataFragment( data=chunk, @@ -311,7 +311,7 @@ async def send_tnc_data(self, data: bytes) -> None: command = SendTncDataFragment(payload) await self._conn.send_tnc_data_fragment(command) - if fragment_id == 0: + if fragment_id == 0 and total_fragments > 1: # Add small delay between fragments await asyncio.sleep(0.01) await self._conn.send_tnc_data_fragment(command) # Repeat because first fragment always fails