diff --git a/examples/ble_dual_node_echo.py b/examples/ble_dual_node_echo.py index 38a7298..e441c97 100755 --- a/examples/ble_dual_node_echo.py +++ b/examples/ble_dual_node_echo.py @@ -38,6 +38,8 @@ import time APP_NAME = "ble_reticulum_poc" APP_ASPECT = "echo" +DEFAULT_MESSAGE_CHUNK_SIZE = 300 +LINK_PAYLOAD_BUDGET = 420 RNS = None running = True @@ -46,7 +48,7 @@ active_links_lock = threading.Lock() temporary_config_dir = None message_file_text = None message_file_path = None -message_chunk_size = 900 +message_chunk_size = DEFAULT_MESSAGE_CHUNK_SIZE @@ -299,24 +301,46 @@ def utf8_chunks(text, max_bytes): yield chunk +def file_chunk_prefix(index, total, chunk_bytes): + return ( + f"file_chunk {index}/{total} from {NODE_NAME} " + f"bytes={chunk_bytes} file={os.path.basename(message_file_path)} data=" + ) + + +def file_data_chunk_size(total, total_bytes): + prefix = file_chunk_prefix(total, total, total_bytes) + send_epoch_suffix = " send_epoch=1778951576.861234" + metadata_bytes = len(prefix.encode("utf-8")) + len(send_epoch_suffix.encode("utf-8")) + safe_size = max(1, LINK_PAYLOAD_BUDGET - metadata_bytes) + return min(message_chunk_size, safe_size) + + def send_message_file(link): if message_file_text is None: return - chunks = list(utf8_chunks(message_file_text, message_chunk_size)) - total = max(1, len(chunks)) total_bytes = len(message_file_text.encode("utf-8")) + estimated_total = max(1, (total_bytes + message_chunk_size - 1) // message_chunk_size) + data_chunk_size = file_data_chunk_size(estimated_total, total_bytes) + chunks = list(utf8_chunks(message_file_text, data_chunk_size)) + total = max(1, len(chunks)) + data_chunk_size = file_data_chunk_size(total, total_bytes) + chunks = list(utf8_chunks(message_file_text, data_chunk_size)) + total = max(1, len(chunks)) + if data_chunk_size < message_chunk_size: + log( + f"Requested message chunk size {message_chunk_size} exceeds Reticulum link budget; " + f"using {data_chunk_size} data bytes per chunk" + ) log(f"Sending file {message_file_path} as {total} chunk(s), {total_bytes} bytes") for index, chunk_text in enumerate(chunks): if not running or link.status != RNS.Link.ACTIVE: return - send_link_packet( - link, - f"file_chunk {index + 1}/{total} from {NODE_NAME} " - f"bytes={len(chunk_text.encode('utf-8'))} file={os.path.basename(message_file_path)} data={chunk_text}", - ) + chunk_bytes = len(chunk_text.encode("utf-8")) + send_link_packet(link, f"{file_chunk_prefix(index + 1, total, chunk_bytes)}{chunk_text}") time.sleep(0.1) @@ -465,8 +489,8 @@ def parse_args(): parser.add_argument( "--message-chunk-size", type=int, - default=900, - help="Maximum UTF-8 bytes per file chunk packet", + default=DEFAULT_MESSAGE_CHUNK_SIZE, + help="Requested maximum UTF-8 data bytes per file chunk packet", ) parser.add_argument( "--announce-only-when-disconnected",