-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprotocol.py
More file actions
98 lines (79 loc) · 3.36 KB
/
protocol.py
File metadata and controls
98 lines (79 loc) · 3.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# urst/protocol.py
import time
from urst.constants import (
DEFAULT_TIMEOUT_MS,
FRAME_ACK,
FRAME_DATA,
FRAME_FRAG,
FRAME_NACK,
MAX_RETRIES,
)
from urst.transport import URSTTransport
__version__ = "0.1.1"
# =============================================================================
# ACKNOWLEDGMENT PROTOCOL - RELIABLE DELIVERY
# =============================================================================
class URSTProtocol(URSTTransport):
"""Protocol layer with acknowledgments and retries"""
def __init__(self, uart, default_timeout_ms=DEFAULT_TIMEOUT_MS, max_retries=MAX_RETRIES):
super().__init__(uart)
self._expected_seq = 0
self._last_received_seq = -1
self._default_timeout_ms = default_timeout_ms
self._max_retries = max_retries
def send_with_ack(self, data, frame_type=FRAME_DATA, timeout_ms=None):
"""Send data and wait for acknowledgment"""
seq_num = self._get_next_seq()
effective_timeout = self._default_timeout_ms if timeout_ms is None else timeout_ms
for attempt in range(self._max_retries + 1):
# Send frame
if not self.send_typed_frame(data, frame_type, seq_num):
continue
# Wait for ACK
if self._wait_for_ack(seq_num, effective_timeout):
return True
return False # Failed after all retries
def handle_incoming_data(self):
"""Process incoming frames and return data if valid"""
frame = self.recv_typed_frame()
if not frame:
return None
payload, seq_num, frame_type = frame
# Handle ACK/NACK frames (no payload expected)
if frame_type in (FRAME_ACK, FRAME_NACK):
return frame # Return for ACK processing
# Handle application data frames only; FRAME_FRAG payloads are delivered to handler
if frame_type in (FRAME_DATA, FRAME_FRAG):
if seq_num == self._expected_seq:
# Expected sequence - send ACK and accept
self._send_ack(seq_num)
self._expected_seq = (self._expected_seq + 1) & 0xFF
self._last_received_seq = seq_num
return (payload, seq_num, frame_type)
elif seq_num == self._last_received_seq:
# Duplicate - send ACK but don't process
self._send_ack(seq_num)
return None
else:
# Out of sequence - send NACK
self._send_nack(seq_num)
return None
return None
def _wait_for_ack(self, expected_seq, timeout_ms):
"""Wait for ACK with timeout"""
start_time = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start_time) < timeout_ms:
frame = self.recv_typed_frame()
if frame:
payload, seq_num, frame_type = frame
if frame_type == FRAME_ACK and seq_num == expected_seq:
return True
elif frame_type == FRAME_NACK and seq_num == expected_seq:
return False
return False # Timeout
def _send_ack(self, seq_num):
"""Send acknowledgment"""
self.send_typed_frame(None, FRAME_ACK, seq_num)
def _send_nack(self, seq_num):
"""Send negative acknowledgment"""
self.send_typed_frame(None, FRAME_NACK, seq_num)