2023-12-05 14:40:04 +00:00
|
|
|
import threading
|
|
|
|
import data_frame_factory
|
|
|
|
import queue
|
2023-12-05 18:01:48 +00:00
|
|
|
import arq_session
|
2023-12-13 22:07:06 +00:00
|
|
|
import helpers
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-05 18:01:48 +00:00
|
|
|
class ARQSessionIRS(arq_session.ARQSession):
|
2023-12-05 14:40:04 +00:00
|
|
|
|
|
|
|
STATE_CONN_REQ_RECEIVED = 0
|
2023-12-13 21:25:22 +00:00
|
|
|
STATE_WAITING_INFO = 1
|
|
|
|
STATE_WAITING_DATA = 2
|
|
|
|
STATE_FAILED = 3
|
2023-12-05 14:40:04 +00:00
|
|
|
STATE_ENDED = 10
|
|
|
|
|
|
|
|
RETRIES_CONNECT = 3
|
2023-12-13 22:37:40 +00:00
|
|
|
RETRIES_TRANSFER = 3 # we need to increase this
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
TIMEOUT_CONNECT = 6
|
2023-12-09 12:28:32 +00:00
|
|
|
TIMEOUT_DATA = 6
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-12 19:46:22 +00:00
|
|
|
def __init__(self, config: dict, tx_frame_queue: queue.Queue, dxcall: str, session_id: int):
|
2023-12-05 18:01:48 +00:00
|
|
|
super().__init__(config, tx_frame_queue, dxcall)
|
|
|
|
|
2023-12-05 17:50:39 +00:00
|
|
|
self.id = session_id
|
2023-12-11 18:02:50 +00:00
|
|
|
self.speed = 0
|
2023-12-12 21:33:17 +00:00
|
|
|
self.frames_per_burst = 3
|
2023-12-11 18:02:50 +00:00
|
|
|
self.version = 1
|
|
|
|
self.snr = 0
|
2023-12-12 21:33:17 +00:00
|
|
|
self.dx_snr = 0
|
2023-12-13 22:37:40 +00:00
|
|
|
self.retries = self.RETRIES_TRANSFER
|
2023-12-05 14:40:04 +00:00
|
|
|
|
|
|
|
self.state = self.STATE_CONN_REQ_RECEIVED
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
self.event_info_received = threading.Event()
|
2023-12-05 14:40:04 +00:00
|
|
|
self.event_data_received = threading.Event()
|
|
|
|
|
|
|
|
self.frame_factory = data_frame_factory.DataFrameFactory(self.config)
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
self.received_frame = None
|
|
|
|
self.received_data = None
|
|
|
|
self.received_bytes = 0
|
|
|
|
self.received_crc = None
|
|
|
|
|
2023-12-05 14:40:04 +00:00
|
|
|
def generate_id(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def set_state(self, state):
|
2023-12-09 12:28:32 +00:00
|
|
|
self.log(f"ARQ Session IRS {self.id} state {self.state}")
|
2023-12-05 14:40:04 +00:00
|
|
|
self.state = state
|
|
|
|
|
|
|
|
def set_modem_decode_modes(self, modes):
|
|
|
|
pass
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
def _all_data_received(self):
|
|
|
|
return self.received_bytes == len(self.received_data)
|
|
|
|
|
2023-12-13 22:07:06 +00:00
|
|
|
def _final_crc_check(self):
|
|
|
|
return self.received_crc == helpers.get_crc_32(bytes(self.received_data)).hex()
|
|
|
|
|
2023-12-13 21:25:22 +00:00
|
|
|
def handshake_session(self):
|
|
|
|
if self.state in [self.STATE_CONN_REQ_RECEIVED, self.STATE_WAITING_INFO]:
|
|
|
|
self.send_open_ack()
|
|
|
|
self.set_state(self.STATE_WAITING_INFO)
|
|
|
|
return True
|
|
|
|
return False
|
2023-12-13 13:33:09 +00:00
|
|
|
|
2023-12-13 21:25:22 +00:00
|
|
|
def handshake_info(self):
|
|
|
|
if self.state == self.STATE_WAITING_INFO and not self.event_info_received.wait(self.TIMEOUT_CONNECT):
|
2023-12-13 13:33:09 +00:00
|
|
|
return False
|
|
|
|
|
|
|
|
self.send_info_ack()
|
|
|
|
self.set_state(self.STATE_WAITING_DATA)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def send_info_ack(self):
|
|
|
|
info_ack = self.frame_factory.build_arq_session_info_ack(
|
|
|
|
self.id, self.received_crc, self.snr,
|
|
|
|
self.speed_level, self.frames_per_burst)
|
|
|
|
self.transmit_frame(info_ack)
|
|
|
|
|
|
|
|
|
|
|
|
def receive_data(self):
|
2023-12-13 22:37:40 +00:00
|
|
|
self.retries = self.RETRIES_TRANSFER
|
|
|
|
while self.retries > 0 and not self._all_data_received():
|
2023-12-11 18:02:50 +00:00
|
|
|
if self.event_data_received.wait(self.TIMEOUT_DATA):
|
2023-12-13 13:33:09 +00:00
|
|
|
self.process_incoming_data()
|
|
|
|
self.send_data_ack_nack(True)
|
2023-12-13 22:37:40 +00:00
|
|
|
self.retries = self.RETRIES_TRANSFER
|
2023-12-13 10:51:54 +00:00
|
|
|
else:
|
2023-12-13 13:33:09 +00:00
|
|
|
self.send_data_ack_nack(False)
|
2023-12-13 22:37:40 +00:00
|
|
|
self.retries -= 1
|
2023-12-13 13:33:09 +00:00
|
|
|
|
|
|
|
if self._all_data_received():
|
2023-12-13 22:07:06 +00:00
|
|
|
if self._final_crc_check():
|
|
|
|
self.set_state(self.STATE_ENDED)
|
|
|
|
else:
|
|
|
|
self.logger.warning("CRC check failed.")
|
|
|
|
self.set_state(self.STATE_FAILED)
|
|
|
|
|
|
|
|
else:
|
2023-12-13 13:33:09 +00:00
|
|
|
self.set_state(self.STATE_FAILED)
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
|
|
|
|
def runner(self):
|
2023-12-13 21:25:22 +00:00
|
|
|
|
|
|
|
if not self.handshake_session():
|
|
|
|
return False
|
|
|
|
|
|
|
|
if not self.handshake_info():
|
2023-12-13 13:33:09 +00:00
|
|
|
return False
|
2023-12-13 21:25:22 +00:00
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
if not self.receive_data():
|
|
|
|
return False
|
2023-12-13 15:56:11 +00:00
|
|
|
return True
|
2023-12-09 13:16:53 +00:00
|
|
|
|
2023-12-05 14:40:04 +00:00
|
|
|
def run(self):
|
2023-12-13 21:25:22 +00:00
|
|
|
self.set_state(self.STATE_CONN_REQ_RECEIVED)
|
2023-12-13 17:27:55 +00:00
|
|
|
self.thread = threading.Thread(target=self.runner,
|
|
|
|
name=f"ARQ IRS Session {self.id}", daemon=False)
|
2023-12-09 13:16:53 +00:00
|
|
|
self.thread.start()
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
def send_open_ack(self):
|
2023-12-12 21:05:32 +00:00
|
|
|
ack_frame = self.frame_factory.build_arq_session_open_ack(
|
|
|
|
self.id,
|
|
|
|
self.dxcall,
|
|
|
|
self.version,
|
|
|
|
self.snr)
|
2023-12-11 18:02:50 +00:00
|
|
|
self.transmit_frame(ack_frame)
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-13 10:51:54 +00:00
|
|
|
def send_data_ack_nack(self, ack: bool):
|
2023-12-13 13:33:09 +00:00
|
|
|
if ack:
|
|
|
|
builder = self.frame_factory.build_arq_burst_ack
|
|
|
|
else:
|
|
|
|
builder = self.frame_factory.build_arq_burst_nack
|
|
|
|
|
|
|
|
frame = builder (
|
2023-12-13 13:59:22 +00:00
|
|
|
self.id, self.received_bytes,
|
2023-12-13 13:33:09 +00:00
|
|
|
self.speed_level, self.frames_per_burst, self.snr)
|
|
|
|
|
|
|
|
self.transmit_frame(frame)
|
2023-12-11 18:02:50 +00:00
|
|
|
|
2023-12-12 21:33:17 +00:00
|
|
|
def calibrate_speed_settings(self):
|
2023-12-13 22:37:40 +00:00
|
|
|
|
|
|
|
# decrement speed level after the 2nd retry
|
|
|
|
if self.RETRIES_TRANSFER - self.retries >= 2:
|
|
|
|
self.speed -= 1
|
|
|
|
self.speed = max(self.speed, 0)
|
|
|
|
return
|
|
|
|
|
|
|
|
# increment speed level after 2 successfully sent bursts and fitting snr
|
|
|
|
# TODO
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-12-12 21:33:17 +00:00
|
|
|
self.speed = self.speed
|
|
|
|
self.frames_per_burst = self.frames_per_burst
|
|
|
|
|
|
|
|
def on_info_received(self, frame):
|
2023-12-13 21:25:22 +00:00
|
|
|
if self.state != self.STATE_WAITING_INFO:
|
2023-12-13 17:27:55 +00:00
|
|
|
self.logger.warning("Discarding received INFO.")
|
|
|
|
return
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
self.received_data = bytearray(frame['total_length'])
|
|
|
|
self.received_crc = frame['total_crc']
|
2023-12-12 21:33:17 +00:00
|
|
|
self.dx_snr = frame['snr']
|
|
|
|
|
|
|
|
self.calibrate_speed_settings()
|
2023-12-13 13:33:09 +00:00
|
|
|
self.set_modem_decode_modes(None)
|
2023-12-13 13:59:22 +00:00
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
self.event_info_received.set()
|
2023-12-12 21:33:17 +00:00
|
|
|
|
2023-12-11 18:02:50 +00:00
|
|
|
def on_data_received(self, frame):
|
2023-12-05 14:40:04 +00:00
|
|
|
if self.state != self.STATE_WAITING_DATA:
|
2023-12-13 17:27:55 +00:00
|
|
|
self.logger.warning(f"ARQ Session: Received data while in state {self.state}. Ignoring.")
|
|
|
|
return
|
2023-12-13 13:33:09 +00:00
|
|
|
|
2023-12-13 13:59:22 +00:00
|
|
|
self.received_frame = frame
|
2023-12-05 14:40:04 +00:00
|
|
|
self.event_data_received.set()
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
def process_incoming_data(self):
|
|
|
|
if self.received_frame['offset'] != self.received_bytes:
|
2023-12-13 15:56:11 +00:00
|
|
|
self.logger.info(f"Discarding data frame due to wrong offset", frame=self.frame_received)
|
2023-12-13 13:33:09 +00:00
|
|
|
return False
|
|
|
|
|
2023-12-13 17:27:55 +00:00
|
|
|
remaining_data_length = len(self.received_data) - self.received_bytes
|
2023-12-13 15:56:11 +00:00
|
|
|
|
|
|
|
# Is this the last data part?
|
2023-12-13 17:27:55 +00:00
|
|
|
if remaining_data_length <= len(self.received_frame['data']):
|
2023-12-13 15:56:11 +00:00
|
|
|
# we only want the remaining length, not the entire frame data
|
|
|
|
data_part = self.received_frame['data'][:remaining_data_length]
|
|
|
|
else:
|
|
|
|
# we want the entire frame data
|
|
|
|
data_part = self.received_frame['data']
|
|
|
|
|
|
|
|
self.received_data[self.received_frame['offset']:] = data_part
|
|
|
|
self.received_bytes += len(data_part)
|
|
|
|
|
2023-12-13 13:33:09 +00:00
|
|
|
return True
|
|
|
|
|
2023-12-12 21:05:32 +00:00
|
|
|
def on_burst_ack_received(self, ack):
|
2023-12-05 14:40:04 +00:00
|
|
|
self.event_transfer_ack_received.set()
|
|
|
|
self.speed_level = ack['speed_level']
|
|
|
|
|
2023-12-12 21:05:32 +00:00
|
|
|
def on_burst_nack_received(self, nack):
|
2023-12-05 14:40:04 +00:00
|
|
|
self.speed_level = nack['speed_level']
|
|
|
|
|
|
|
|
def on_disconnect_received(self):
|
|
|
|
self.abort()
|
|
|
|
|
|
|
|
def abort(self):
|
|
|
|
self.state = self.STATE_DISCONNECTED
|