Skip to content

Commit

Permalink
Merge pull request #12 from UCSD-E4E/release/v0.2.0
Browse files Browse the repository at this point in the history
Release/v0.2.0
  • Loading branch information
TylerFlar authored Jan 3, 2025
2 parents cc77e04 + 37aff75 commit 89d1f00
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 22 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "radio-telemetry-tracker-drone-comms-package"
version = "0.1.2"
version = "0.2.0"
description = ""
authors = ["Tyler Flar <[email protected]>"]
license = "Other"
Expand Down
2 changes: 1 addition & 1 deletion radio_telemetry_tracker_drone_comms_package/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Package initialization for the Radio Telemetry Tracker Drone Comms Package.
"""

__version__ = "0.1.2"
__version__ = "0.2.0"

from radio_telemetry_tracker_drone_comms_package.data_models import (
ConfigRequestData,
Expand Down
2 changes: 2 additions & 0 deletions radio_telemetry_tracker_drone_comms_package/data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
class SyncRequestData:
"""Data container for synchronization request packets."""

ack_timeout: float
max_retries: int
packet_id: int | None = None
timestamp: int | None = None

Expand Down
22 changes: 16 additions & 6 deletions radio_telemetry_tracker_drone_comms_package/drone_comms.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,16 +553,21 @@ def unregister_error_handler(self, callback: Callable[[ErrorData], None]) -> boo
# Public send methods
# --------------------------------------------------------------------------

def send_sync_request(self, _: SyncRequestData) -> tuple[int, bool, int]:
def send_sync_request(self, data: SyncRequestData) -> tuple[int, bool, int]:
"""Send a sync request packet.
Args:
data: Sync request data to send
Returns:
tuple: (packet_id, need_ack, timestamp)
"""
packet = RadioPacket()
packet.syn_rqt.base.packet_id = self.generate_packet_id()
packet.syn_rqt.base.need_ack = True
packet.syn_rqt.base.timestamp = _current_timestamp_us()
packet.syn_rqt.ack_timeout = data.ack_timeout
packet.syn_rqt.max_retries = data.max_retries
self.enqueue_packet(packet)
return (
packet.syn_rqt.base.packet_id,
Expand All @@ -581,7 +586,7 @@ def send_sync_response(self, data: SyncResponseData) -> tuple[int, bool, int]:
"""
packet = RadioPacket()
packet.syn_rsp.base.packet_id = self.generate_packet_id()
packet.syn_rsp.base.need_ack = False
packet.syn_rsp.base.need_ack = True
packet.syn_rsp.base.timestamp = _current_timestamp_us()
packet.syn_rsp.success = data.success
self.enqueue_packet(packet)
Expand Down Expand Up @@ -632,7 +637,7 @@ def send_config_response(self, data: ConfigResponseData) -> tuple[int, bool, int
"""
packet = RadioPacket()
packet.cfg_rsp.base.packet_id = self.generate_packet_id()
packet.cfg_rsp.base.need_ack = False
packet.cfg_rsp.base.need_ack = True
packet.cfg_rsp.base.timestamp = _current_timestamp_us()
packet.cfg_rsp.success = data.success
self.enqueue_packet(packet)
Expand Down Expand Up @@ -745,7 +750,7 @@ def send_start_response(self, data: StartResponseData) -> tuple[int, bool, int]:
"""
packet = RadioPacket()
packet.str_rsp.base.packet_id = self.generate_packet_id()
packet.str_rsp.base.need_ack = False
packet.str_rsp.base.need_ack = True
packet.str_rsp.base.timestamp = _current_timestamp_us()
packet.str_rsp.success = data.success
self.enqueue_packet(packet)
Expand Down Expand Up @@ -783,7 +788,7 @@ def send_stop_response(self, data: StopResponseData) -> tuple[int, bool, int]:
"""
packet = RadioPacket()
packet.stp_rsp.base.packet_id = self.generate_packet_id()
packet.stp_rsp.base.need_ack = False
packet.stp_rsp.base.need_ack = True
packet.stp_rsp.base.timestamp = _current_timestamp_us()
packet.stp_rsp.success = data.success
self.enqueue_packet(packet)
Expand All @@ -801,7 +806,7 @@ def send_error(self, _: ErrorData) -> tuple[int, bool, int]:
"""
packet = RadioPacket()
packet.err_pkt.base.packet_id = self.generate_packet_id()
packet.err_pkt.base.need_ack = False
packet.err_pkt.base.need_ack = True
packet.err_pkt.base.timestamp = _current_timestamp_us()
self.enqueue_packet(packet)
return (
Expand All @@ -818,6 +823,8 @@ def _extract_sync_request(self, packet: SyncRequestPacket) -> SyncRequestData:
return SyncRequestData(
packet_id=packet.base.packet_id,
timestamp=packet.base.timestamp,
ack_timeout=packet.ack_timeout,
max_retries=packet.max_retries,
)

def _extract_sync_response(self, packet: SyncResponsePacket) -> SyncResponseData:
Expand Down Expand Up @@ -923,6 +930,9 @@ def _extract_error(self, packet: ErrorPacket) -> ErrorData:
# --------------------------------------------------------------------------

def _handle_sync_request(self, data: SyncRequestData) -> None:
# Update ack_timeout and max_retries with values from sync request
self.ack_timeout = data.ack_timeout
self.max_retries = data.max_retries
self._invoke_handlers(self._sync_request_handlers, data)

def _handle_sync_response(self, data: SyncResponseData) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ message AckPacket {

message SyncRequestPacket {
BasePacket base = 1;
float ack_timeout = 2;
int32 max_retries = 3;
}

message SyncResponsePacket {
Expand Down
13 changes: 11 additions & 2 deletions tests/test_data_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@
TEST_TARGET_FREQUENCIES = [100, 200, 300]
TEST_SYNC_PACKET_ID = 123
TEST_SYNC_TIMESTAMP = 456789
TEST_SYNC_ACK_TIMEOUT = 2.0
TEST_SYNC_MAX_RETRIES = 5


def test_sync_request_data() -> None:
"""Test SyncRequestData initialization and attribute access."""
data = SyncRequestData(packet_id=TEST_SYNC_PACKET_ID, timestamp=TEST_SYNC_TIMESTAMP)
"""Test SyncRequestData model."""
data = SyncRequestData(
packet_id=TEST_SYNC_PACKET_ID,
timestamp=TEST_SYNC_TIMESTAMP,
ack_timeout=TEST_SYNC_ACK_TIMEOUT,
max_retries=TEST_SYNC_MAX_RETRIES,
)
assert data.packet_id == TEST_SYNC_PACKET_ID # noqa: S101
assert data.timestamp == TEST_SYNC_TIMESTAMP # noqa: S101
assert data.ack_timeout == TEST_SYNC_ACK_TIMEOUT # noqa: S101
assert data.max_retries == TEST_SYNC_MAX_RETRIES # noqa: S101


def test_config_request_data() -> None:
Expand Down
11 changes: 7 additions & 4 deletions tests/test_drone_comms.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RadioConfig,
)
from radio_telemetry_tracker_drone_comms_package.proto.packets_pb2 import RadioPacket
from tests.test_data_models import TEST_SYNC_ACK_TIMEOUT, TEST_SYNC_MAX_RETRIES


class MockInterface(Protocol):
Expand Down Expand Up @@ -88,11 +89,13 @@ def test_drone_comms_init_invalid_interface() -> None:


def test_drone_comms_send_sync_request(drone_comms: DroneComms) -> None:
"""Test sending sync request returns valid packet ID and timestamp."""
pid, need_ack, timestamp = drone_comms.send_sync_request(SyncRequestData())
"""Test sending sync request packet."""
pid, need_ack, timestamp = drone_comms.send_sync_request(
SyncRequestData(ack_timeout=TEST_SYNC_ACK_TIMEOUT, max_retries=TEST_SYNC_MAX_RETRIES),
)
assert pid > 0 # noqa: S101
assert need_ack is True # noqa: S101
assert pid != 0 # noqa: S101
assert timestamp != 0 # noqa: S101
assert timestamp > 0 # noqa: S101


def test_drone_comms_send_config_request(drone_comms: DroneComms) -> None:
Expand Down
49 changes: 41 additions & 8 deletions tools/gcs_fds_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class GCSFDSCLI(cmd.Cmd):
intro = "Welcome to the GCS/FDS CLI. Type help or ? to list commands.\n"
prompt = "(gcs-fds) "

# Default values for sync request
DEFAULT_ACK_TIMEOUT = 2.0
DEFAULT_MAX_RETRIES = 5

# Argument indices for sync request
ACK_TIMEOUT_ARG_IDX = 1
MAX_RETRIES_ARG_IDX = 2

def __init__(self, radio_config: RadioConfig) -> None:
"""Initialize the CLI with radio configuration.
Expand Down Expand Up @@ -98,7 +106,17 @@ def do_start(self, arg: str) -> None: # noqa: ARG002
logger.info("Already started.")
return

self.drone_comms = DroneComms(radio_config=self.radio_config)
def on_ack_timeout(packet_id: int) -> None:
logger.warning("Packet %d acknowledgment timed out", packet_id)

def on_ack_success(packet_id: int) -> None:
logger.info("Packet %d successfully acknowledged", packet_id)

self.drone_comms = DroneComms(
radio_config=self.radio_config,
on_ack_callback=on_ack_timeout,
on_ack_success=on_ack_success,
)
self.drone_comms.start()
self.started = True
logger.info("Started drone communications.")
Expand Down Expand Up @@ -158,10 +176,7 @@ def do_unregister(self, arg: str) -> None:
logger.info("Unknown packet type: %s", pkt_type)
return

if (
pkt_type not in self.registered_callbacks
or not self.registered_callbacks[pkt_type]
):
if pkt_type not in self.registered_callbacks or not self.registered_callbacks[pkt_type]:
logger.info("No callbacks registered for '%s'.", pkt_type)
return

Expand All @@ -180,13 +195,31 @@ def do_unregister(self, arg: str) -> None:
else:
logger.info("Failed to unregister callbacks (unexpected error).")

def do_send_sync_request(self, arg: str) -> None: # noqa: ARG002
def do_send_sync_request(self, arg: str) -> None:
"""Send a sync request packet.
Args:
arg: Command argument (unused)
arg: Command argument in format: [ack_timeout] [max_retries]
"""
pid, ack, ts = self.drone_comms.send_sync_request(SyncRequestData())
# Default values
data = SyncRequestData(
ack_timeout=self.DEFAULT_ACK_TIMEOUT,
max_retries=self.DEFAULT_MAX_RETRIES,
)

# Parse arguments if provided
parts = arg.split()
if parts:
try:
if len(parts) >= self.ACK_TIMEOUT_ARG_IDX:
data.ack_timeout = float(parts[0])
if len(parts) >= self.MAX_RETRIES_ARG_IDX:
data.max_retries = int(parts[1])
except ValueError:
logger.exception("Invalid argument format")
return

pid, ack, ts = self.drone_comms.send_sync_request(data)
logger.info(
"Sent SyncRequest (packet_id=%s, need_ack=%s, timestamp=%s)",
pid,
Expand Down

0 comments on commit 89d1f00

Please sign in to comment.