diff --git a/gui/src/components/main.vue b/gui/src/components/main.vue index e3a67be5..5c6a903d 100644 --- a/gui/src/components/main.vue +++ b/gui/src/components/main.vue @@ -20,7 +20,7 @@ import infoScreen from "./infoScreen.vue"; import main_modem_healthcheck from "./main_modem_healthcheck.vue"; import Dynamic_components2 from "./dynamic_components2.vue"; -import { stopTransmission } from "../js/sock"; +import { stopTransmission } from "../js/api"; function stopAllTransmissions() { console.log("stopping transmissions"); diff --git a/gui/src/js/api.js b/gui/src/js/api.js index 7f9c790f..e2d4b43e 100644 --- a/gui/src/js/api.js +++ b/gui/src/js/api.js @@ -97,6 +97,12 @@ export function sendModemARQRaw(mycall, dxcall, data, uuid) { }); } +export function stopTransmission() { + return apiPost("/modem/stop_transmission"); +} + + + export function sendModemTestFrame() { return apiPost("/modem/send_test_frame"); } diff --git a/gui/src/js/sock.js b/gui/src/js/sock.js index c9477852..7cc2ae52 100644 --- a/gui/src/js/sock.js +++ b/gui/src/js/sock.js @@ -719,11 +719,6 @@ function sendResponseSharedFile(dxcallsign, sharedFile, sharedFileData) { sendResponse(dxcallsign, 255, 1, sharedFile + "/" + sharedFileData, "res-2"); } */ -//STOP TRANSMISSION -export function stopTransmission() { - var command = '{"type" : "arq", "command": "stop_transmission"}'; - writeTncCommand(command); -} // Get RX BUffer export function getRxBuffer() { diff --git a/modem/arq_session.py b/modem/arq_session.py index 434f4edf..5548aa86 100644 --- a/modem/arq_session.py +++ b/modem/arq_session.py @@ -45,6 +45,8 @@ class ARQSession(): self.id = None + self.final = False # class wide final state for stopping transmissions on command + def log(self, message, isWarning = False): msg = f"[{type(self).__name__}]: {message}" logger = self.logger.warn if isWarning else self.logger.info @@ -88,3 +90,4 @@ class ARQSession(): return self.log(f"Ignoring unknow transition from state {self.state.name} with frame {frame['frame_type']}") + diff --git a/modem/arq_session_irs.py b/modem/arq_session_irs.py index 8058cb97..bd40559b 100644 --- a/modem/arq_session_irs.py +++ b/modem/arq_session_irs.py @@ -16,29 +16,39 @@ class IRS_State(Enum): class ARQSessionIRS(arq_session.ARQSession): - TIMEOUT_CONNECT = 3 + TIMEOUT_CONNECT = 10 TIMEOUT_DATA = 12 STATE_TRANSITION = { IRS_State.NEW: { FRAME_TYPE.ARQ_SESSION_OPEN.value : 'send_open_ack', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' }, IRS_State.OPEN_ACK_SENT: { FRAME_TYPE.ARQ_SESSION_OPEN.value: 'send_open_ack', FRAME_TYPE.ARQ_SESSION_INFO.value: 'send_info_ack', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' + }, IRS_State.INFO_ACK_SENT: { FRAME_TYPE.ARQ_SESSION_INFO.value: 'send_info_ack', FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' + }, IRS_State.BURST_REPLY_SENT: { FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' + }, IRS_State.ENDED: { FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' + }, IRS_State.FAILED: { FRAME_TYPE.ARQ_BURST_FRAME.value: 'receive_data', + FRAME_TYPE.ARQ_STOP.value: 'send_stop_ack' }, } @@ -107,16 +117,10 @@ class ARQSessionIRS(arq_session.ARQSession): info_ack = self.frame_factory.build_arq_session_info_ack( self.id, self.total_crc, self.snr[0], - self.speed_level, self.frames_per_burst) + self.speed_level, self.frames_per_burst, flag_final=self.final) self.launch_transmit_and_wait(info_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling) self.set_state(IRS_State.INFO_ACK_SENT) - def send_burst_nack(self): - self.calibrate_speed_settings() - self.set_decode_mode() - nack = self.frame_factory.build_arq_burst_ack(self.id, self.received_bytes, self.speed_level, self.frames_per_burst, self.snr[0]) - self.launch_transmit_and_wait(nack, self.TIMEOUT_DATA, mode=FREEDV_MODE.signalling) - self.log("NACK sent") def process_incoming_data(self, frame): if frame['offset'] != self.received_bytes: @@ -148,7 +152,7 @@ class ARQSessionIRS(arq_session.ARQSession): if not self.all_data_received(): ack = self.frame_factory.build_arq_burst_ack( self.id, self.received_bytes, - self.speed_level, self.frames_per_burst, self.snr[0]) + self.speed_level, self.frames_per_burst, self.snr[0], flag_final=self.final) # increase ack counter self.transmitted_acks += 1 @@ -199,3 +203,11 @@ class ARQSessionIRS(arq_session.ARQSession): if self.snr[0] >= self.SPEED_LEVEL_DICT[new_speed_level]["min_snr"]: self.speed_level = new_speed_level + def stop_transmission(self): + self.log(f"Stopping transmission...., setting final flag") + self.final = True + + def send_stop_ack(self, stop_frame): + stop_ack = self.frame_factory.build_arq_stop_ack(self.id) + self.launch_transmit_and_wait(stop_ack, self.TIMEOUT_CONNECT, mode=FREEDV_MODE.signalling) + self.set_state(IRS_State.FAILED) \ No newline at end of file diff --git a/modem/arq_session_iss.py b/modem/arq_session_iss.py index 4c57896f..25277170 100644 --- a/modem/arq_session_iss.py +++ b/modem/arq_session_iss.py @@ -34,8 +34,10 @@ class ARQSessionISS(arq_session.ARQSession): ISS_State.BURST_SENT: { FRAME_TYPE.ARQ_SESSION_INFO_ACK.value: 'send_data', FRAME_TYPE.ARQ_BURST_ACK.value: 'send_data', - FRAME_TYPE.ARQ_BURST_NACK.value: 'send_data', }, + ISS_State.FAILED:{ + FRAME_TYPE.ARQ_STOP_ACK.value: 'transmission_failed' + } } def __init__(self, config: dict, modem, dxcall: str, data: bytearray): @@ -54,7 +56,7 @@ class ARQSessionISS(arq_session.ARQSession): return random.randint(1,255) def transmit_wait_and_retry(self, frame_or_burst, timeout, retries, mode): - while retries > 0: + while retries > 0 and not self.final: self.event_frame_received = threading.Event() if isinstance(frame_or_burst, list): burst = frame_or_burst else: burst = [frame_or_burst] @@ -129,4 +131,10 @@ class ARQSessionISS(arq_session.ARQSession): def transmission_failed(self): self.set_state(ISS_State.FAILED) self.log("Transmission failed!") - self.event_manager.send_arq_session_finished(True, self.id, self.dxcall, len(self.data),False) \ No newline at end of file + self.event_manager.send_arq_session_finished(True, self.id, self.dxcall, len(self.data),False) + + def stop_transmission(self): + self.log(f"Stopping transmission...") + stop_frame = self.frame_factory.build_arq_stop(self.id) + self.launch_twr(stop_frame, self.TIMEOUT_CONNECT_ACK, self.RETRIES_CONNECT, mode=FREEDV_MODE.signalling) + self.set_state(ISS_State.FAILED) diff --git a/modem/command_arq_raw.py b/modem/command_arq_raw.py index 98550546..bde0bda6 100644 --- a/modem/command_arq_raw.py +++ b/modem/command_arq_raw.py @@ -4,6 +4,8 @@ import api_validations import base64 from queue import Queue from arq_session_iss import ARQSessionISS + + class ARQRawCommand(TxCommand): def set_params_from_api(self, apiParams): diff --git a/modem/data_frame_factory.py b/modem/data_frame_factory.py index b7583501..0f9ffa4e 100644 --- a/modem/data_frame_factory.py +++ b/modem/data_frame_factory.py @@ -127,6 +127,16 @@ class DataFrameFactory: "flag": 1, } + self.template_list[FR_TYPE.ARQ_STOP.value] = { + "frame_length": self.LENGTH_SIG0_FRAME, + "session_id": 1, + } + + self.template_list[FR_TYPE.ARQ_STOP_ACK.value] = { + "frame_length": self.LENGTH_SIG0_FRAME, + "session_id": 1, + } + # arq burst frame self.template_list[FR_TYPE.ARQ_BURST_FRAME.value] = { "frame_length": None, @@ -146,23 +156,6 @@ class DataFrameFactory: "flag": 1, } - # arq burst nack - self.template_list[FR_TYPE.ARQ_BURST_NACK.value] = { - "frame_length": self.LENGTH_SIG1_FRAME, - "session_id": 1, - "offset":4, - "speed_level": 1, - "frames_per_burst": 1, - "snr": 1, - } - - # arq data ack nack - self.template_list[FR_TYPE.ARQ_DATA_ACK_NACK.value] = { - "frame_length": self.LENGTH_SIG1_FRAME, - "session_id": 1, - "state": 1, - "snr": 1, - } def construct(self, frametype, content, frame_length = LENGTH_SIG1_FRAME): frame_template = self.template_list[frametype.value] @@ -222,9 +215,12 @@ class DataFrameFactory: elif key in ["session_id", "speed_level", "frames_per_burst", "version", - "snr", "offset", "total_length", "state"]: + "offset", "total_length", "state"]: extracted_data[key] = int.from_bytes(data, 'big') + elif key in ["snr"]: + extracted_data[key] = helpers.snr_from_bytes(data) + elif key == "flag": data = int.from_bytes(data, "big") @@ -342,7 +338,8 @@ class DataFrameFactory: "origin": helpers.callsign_to_bytes(self.myfullcall), "destination_crc": helpers.get_crc_24(destination), "version": bytes([version]), - "snr": snr.to_bytes(1, 'big'), } + "snr": helpers.snr_to_bytes(1), + } return self.construct(FR_TYPE.ARQ_SESSION_OPEN_ACK, payload) def build_arq_session_info(self, session_id: int, total_length: int, total_crc: bytes, snr): @@ -350,10 +347,23 @@ class DataFrameFactory: "session_id": session_id.to_bytes(1, 'big'), "total_length": total_length.to_bytes(4, 'big'), "total_crc": total_crc, - "snr": snr.to_bytes(1, 'big'), + "snr": helpers.snr_to_bytes(1), } return self.construct(FR_TYPE.ARQ_SESSION_INFO, payload) + def build_arq_stop(self, session_id: int): + payload = { + "session_id": session_id.to_bytes(1, 'big'), + } + return self.construct(FR_TYPE.ARQ_STOP, payload) + + def build_arq_stop_ack(self, session_id: int): + payload = { + "session_id": session_id.to_bytes(1, 'big'), + } + return self.construct(FR_TYPE.ARQ_STOP_ACK, payload) + + def build_arq_session_info_ack(self, session_id, total_crc, snr, speed_level, frames_per_burst, flag_final=False): flag = 0b00000000 if flag_final: @@ -363,7 +373,7 @@ class DataFrameFactory: "frame_length": self.LENGTH_SIG0_FRAME, "session_id": session_id.to_bytes(1, 'big'), "total_crc": bytes.fromhex(total_crc), - "snr": snr.to_bytes(1, 'big'), + "snr": helpers.snr_to_bytes(1), "speed_level": speed_level.to_bytes(1, 'big'), "frames_per_burst": frames_per_burst.to_bytes(1, 'big'), "flag": flag.to_bytes(1, 'big'), diff --git a/modem/frame_dispatcher.py b/modem/frame_dispatcher.py index 8662b035..60b23e37 100644 --- a/modem/frame_dispatcher.py +++ b/modem/frame_dispatcher.py @@ -24,12 +24,11 @@ class DISPATCHER(): FR_TYPE.ARQ_CONNECTION_CLOSE.value: {"class": ARQFrameHandler, "name": "ARQ CLOSE SESSION"}, FR_TYPE.ARQ_CONNECTION_HB.value: {"class": ARQFrameHandler, "name": "ARQ HEARTBEAT"}, FR_TYPE.ARQ_CONNECTION_OPEN.value: {"class": ARQFrameHandler, "name": "ARQ OPEN SESSION"}, - FR_TYPE.ARQ_STOP.value: {"class": ARQFrameHandler, "name": "ARQ STOP TX"}, + FR_TYPE.ARQ_STOP.value: {"class": ARQFrameHandler, "name": "ARQ STOP"}, + FR_TYPE.ARQ_STOP_ACK.value: {"class": ARQFrameHandler, "name": "ARQ STOP ACK"}, FR_TYPE.BEACON.value: {"class": FrameHandler, "name": "BEACON"}, FR_TYPE.ARQ_BURST_FRAME.value:{"class": ARQFrameHandler, "name": "BURST FRAME"}, FR_TYPE.ARQ_BURST_ACK.value: {"class": ARQFrameHandler, "name": "BURST ACK"}, - FR_TYPE.ARQ_BURST_NACK.value: {"class": ARQFrameHandler, "name": "BURST NACK"}, - FR_TYPE.ARQ_DATA_ACK_NACK.value: {"class": ARQFrameHandler, "name": "DATA ACK NACK"}, FR_TYPE.CQ.value: {"class": CQFrameHandler, "name": "CQ"}, FR_TYPE.PING_ACK.value: {"class": FrameHandler, "name": "PING ACK"}, FR_TYPE.PING.value: {"class": PingFrameHandler, "name": "PING"}, diff --git a/modem/frame_handler_arq_session.py b/modem/frame_handler_arq_session.py index 51d9926f..6e18a2de 100644 --- a/modem/frame_handler_arq_session.py +++ b/modem/frame_handler_arq_session.py @@ -38,8 +38,9 @@ class ARQFrameHandler(frame_handler.FrameHandler): FR.ARQ_SESSION_OPEN_ACK.value, FR.ARQ_SESSION_INFO_ACK.value, FR.ARQ_BURST_ACK.value, - FR.ARQ_DATA_ACK_NACK.value - ]: + FR.ARQ_STOP.value, + FR.ARQ_STOP_ACK.value + ]: session = self.states.get_arq_iss_session(session_id) else: diff --git a/modem/modem_frametypes.py b/modem/modem_frametypes.py index e3fb5452..582a7907 100644 --- a/modem/modem_frametypes.py +++ b/modem/modem_frametypes.py @@ -10,14 +10,13 @@ class FRAME_TYPE(Enum): ARQ_CONNECTION_HB = 2 ARQ_CONNECTION_CLOSE = 3 ARQ_STOP = 10 - ARQ_SESSION_OPEN = 11 - ARQ_SESSION_OPEN_ACK = 12 - ARQ_SESSION_INFO = 13 - ARQ_SESSION_INFO_ACK = 14 + ARQ_STOP_ACK = 11 + ARQ_SESSION_OPEN = 12 + ARQ_SESSION_OPEN_ACK = 13 + ARQ_SESSION_INFO = 14 + ARQ_SESSION_INFO_ACK = 15 ARQ_BURST_FRAME = 20 ARQ_BURST_ACK = 21 - ARQ_BURST_NACK = 22 - ARQ_DATA_ACK_NACK = 23 MESH_BROADCAST = 100 MESH_SIGNALLING_PING = 101 MESH_SIGNALLING_PING_ACK = 102 diff --git a/modem/server.py b/modem/server.py index c83ac98e..53baf14a 100644 --- a/modem/server.py +++ b/modem/server.py @@ -215,6 +215,19 @@ def post_modem_send_raw(): # server_commands.modem_arq_send_raw(request.json) return "Not implemented yet" +@app.route('/modem/stop_transmission', methods=['POST']) +def post_modem_send_raw_stop(): + + if request.method not in ['POST']: + return api_response({"info": "endpoint for SENDING a STOP command via POST"}) + if not app.state_manager.is_modem_running: + api_abort('Modem not running', 503) + print("stop") + app.state_manager.set_final_to_arq_transmissions() + # server_commands.modem_arq_send_raw(request.json) + return api_response(request.json) + + # @app.route('/modem/arq_connect', methods=['POST']) # @app.route('/modem/arq_disconnect', methods=['POST']) # @app.route('/modem/send_raw', methods=['POST']) diff --git a/modem/state_manager.py b/modem/state_manager.py index f59cab10..3f692c56 100644 --- a/modem/state_manager.py +++ b/modem/state_manager.py @@ -144,6 +144,12 @@ class StateManager: raise RuntimeError(f"ARQ ISS Session '{id}' not found!") del self.arq_irs_sessions[id] + def set_final_to_arq_transmissions(self): + for id in self.arq_irs_sessions: + self.arq_irs_sessions[id].stop_transmission() + for id in self.arq_iss_sessions: + self.arq_iss_sessions[id].stop_transmission() + def add_activity(self, activity_data): # Generate a random 8-byte string as hex activity_id = np.random.bytes(8).hex()