diff --git a/modem/command.py b/modem/command.py new file mode 100644 index 00000000..804d78f3 --- /dev/null +++ b/modem/command.py @@ -0,0 +1,25 @@ +from data_frame_factory import DataFrameFactory +from modem.modem import RF + +class TxCommand(): + + def __init__(self, modem: RF, apiParams): + self.setParamsFromApi(apiParams) + self.modem = modem + self.frame_factory = DataFrameFactory(modem) + + def setParamsFromApi(self, apiParams): + pass + + def getPayload(self): + pass + + def execute(self, modem): + pass + + def transmit(self, frame): + # MODEM_TRANSMIT_QUEUE.put([c2_mode, copies, repeat_delay, frame_to_tx]) + + self.modem.modem_transmit_queue.put( + + ) diff --git a/modem/command_cq.py b/modem/command_cq.py new file mode 100644 index 00000000..79f2787d --- /dev/null +++ b/modem/command_cq.py @@ -0,0 +1,3 @@ +from command import TxCommand + +class CQCommand(TxCommand): diff --git a/modem/command_ping.py b/modem/command_ping.py new file mode 100644 index 00000000..91d0728f --- /dev/null +++ b/modem/command_ping.py @@ -0,0 +1,11 @@ +from command import TxCommand + +class PingCommand(TxCommand): + + def setParamsFromApi(self, apiParams): + self.dxcall = apiParams['dxcall'] + return super().setParamsFromApi() + + def execute(self): + self.frame_factory.build_ping(self.dxcall) + \ No newline at end of file diff --git a/modem/data_frame_factory.py b/modem/data_frame_factory.py index fb49c772..63827af4 100644 --- a/modem/data_frame_factory.py +++ b/modem/data_frame_factory.py @@ -1,23 +1,23 @@ from modem_frametypes import FRAME_TYPE as FR_TYPE import helpers +import codec2 class DataFrameFactory: - def __init__(self, modem_config, modem_state, **data): - self.modem_config = modem_config - self.modem_state = modem_state - self.data = data + def __init__(self, modem): + self.modem_config = modem.config + self.modem_state = modem.state - self.myfullcall = f"{modem_config['STATION']['mycall']}-{modem_config['STATION']['myssid']}" + self.myfullcall = f"{self.modem_config['STATION']['mycall']}-{self.modem_config['STATION']['myssid']}" def build(self): build_method = getattr(self, self.type.name) return build_method() - def build_ping(self): + def build_ping(self, dxcallsign): ping_frame = bytearray(self.length_sig0_frame) ping_frame[:1] = bytes(self.type.value) - ping_frame[1:4] = helpers.get_crc_24(self.data['dxcallsign']) + ping_frame[1:4] = helpers.get_crc_24(dxcallsign) ping_frame[4:7] = helpers.get_crc_24(self.myfullcall) ping_frame[7:13] = helpers.callsign_to_bytes(self.myfullcall) return ping_frame @@ -25,20 +25,20 @@ class DataFrameFactory: def build_cq(self): cq_frame = bytearray(self.length_sig0_frame) cq_frame[:1] = bytes([FR_TYPE.CQ.value]) - cq_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) + cq_frame[1:7] = helpers.callsign_to_bytes(self.myfullcall) cq_frame[7:11] = helpers.encode_grid(self.mygrid) return cq_frame def build_fec_is_writing(self): fec_frame = bytearray(14) fec_frame[:1] = bytes([FR_TYPE.IS_WRITING.value]) - fec_frame[1:7] = helpers.callsign_to_bytes(mycallsign) + fec_frame[1:7] = helpers.callsign_to_bytes(self.myfullcall) return fec_frame def build_qrv(self): qrv_frame = bytearray(self.length_sig0_frame) qrv_frame[:1] = bytes([FR_TYPE.QRV.value]) - qrv_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) + qrv_frame[1:7] = helpers.callsign_to_bytes(self.myfullcall) qrv_frame[7:11] = helpers.encode_grid(self.mygrid) qrv_frame[11:12] = helpers.snr_to_bytes(snr) return qrv_frame @@ -46,16 +46,16 @@ class DataFrameFactory: def build_beacon(self): beacon_frame = bytearray(self.length_sig0_frame) beacon_frame[:1] = bytes([FR_TYPE.BEACON.value]) - beacon_frame[1:7] = helpers.callsign_to_bytes(self.mycallsign) + beacon_frame[1:7] = helpers.callsign_to_bytes(self.myfullcall) beacon_frame[7:11] = helpers.encode_grid(self.mygrid) return beacon_frame def build_fec_wakeup(self): mode_int_wakeup = codec2.freedv_get_mode_value_by_name("sig0") - payload_per_wakeup_frame = modem.get_bytes_per_frame(mode_int_wakeup) - 2 + payload_per_wakeup_frame = self.modem.get_bytes_per_frame(mode_int_wakeup) - 2 fec_wakeup_frame = bytearray(payload_per_wakeup_frame) fec_wakeup_frame[:1] = bytes([FR_TYPE.FEC_WAKEUP.value]) - fec_wakeup_frame[1:7] = helpers.callsign_to_bytes(mycallsign) + fec_wakeup_frame[1:7] = helpers.callsign_to_bytes(self.myfullcall) fec_wakeup_frame[7:8] = bytes([mode_int]) fec_wakeup_frame[8:9] = bytes([1]) # n payload bursts return fec_wakeup_frame diff --git a/modem/frame_dispatcher.py b/modem/frame_dispatcher.py index 4fb14132..b29ba088 100644 --- a/modem/frame_dispatcher.py +++ b/modem/frame_dispatcher.py @@ -27,7 +27,6 @@ class DISPATCHER(): self._initialize_handlers(config, event_queue, states) self._initialize_dispatchers() self._initialize_queues() - self._start_worker_threads() def _initialize_handlers(self, config, event_queue, states): """Initializes various data handlers.""" @@ -109,34 +108,19 @@ class DISPATCHER(): threading.Thread(target=self.worker_transmit, name="Transmit Worker", daemon=True).start() threading.Thread(target=self.worker_receive, name="Receive Worker", daemon=True).start() + def start(self): + self._start_worker_threads() + def worker_transmit(self) -> None: """Dispatch incoming UI instructions for transmitting operations""" while True: data = self.data_queue_transmit.get() - # if we are already in ARQ_STATE, or we're receiving codec2 traffic - # let's wait with processing data - # this should avoid weird toggle states where both stations - # stuck in IRS - # - # send transmission queued information once - if self.states.is_arq_state or self.states.is_codec2_traffic: - self.log.debug( - "[Modem] TX DISPATCHER - waiting with processing command ", - is_arq_state=self.states.is_arq_state, + self.log.debug( + "[Modem] TX DISPATCHER - got a transmit command", + command=data, ) - self.send_data_to_socket_queue( - freedata="modem-message", - command=data[0], - status="queued", - ) - - # now stay in while loop until state released - while self.states.is_arq_state or self.states.is_codec2_traffic: - threading.Event().wait(0.01) - - # and finally sleep some time - threading.Event().wait(1.0) + # Dispatch commands known to command_dispatcher if data[0] in self.command_dispatcher: diff --git a/modem/server.py b/modem/server.py index 4960372f..cfe4a19e 100644 --- a/modem/server.py +++ b/modem/server.py @@ -12,6 +12,9 @@ import state_manager import ujson as json import websocket_manager as wsm import api_validations as validations +from tx_command.tx_command import TxCommand +from tx_command.ping_command import PingCommand +from queues import DATA_QUEUE_TRANSMIT as tx_cmd_queue app = Flask(__name__) CORS(app) @@ -72,6 +75,12 @@ def validate(req, param, validator, isRequired = True): if not validator(req[param]): api_abort(f"Value of '{param}' is invalid.", 400) +# Takes a transmit command and puts it in the transmit command queue +def enqueue_tx_command(cmd_class, params = {}): + command = cmd_class(modem, params) + tx_cmd_queue.put(command) + app.logger.info(f"Command {type(command).__name__} enqueued.") + ## REST API @app.route('/', methods=['GET']) def index(): @@ -138,8 +147,8 @@ def post_ping(): if not app.state_manager.is_modem_running: api_abort('Modem not running', 503) validate(request.json, 'dxcall', validations.validate_freedata_callsign) - server_commands.ping_ping(request.json['dxcall']) - return api_response(request.json) + enqueue_tx_command(PingCommand, request.json) + return 'ok' @app.route('/modem/send_test_frame', methods=['POST']) def post_send_test_frame(): diff --git a/modem/service_manager.py b/modem/service_manager.py index a64217d8..457aeecc 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -72,6 +72,7 @@ class SM: self.modem = modem.RF(self.config, self.modem_events, self.modem_fft, self.modem_service, self.states) #self.data_handler = data_handler.DATA(self.config, self.modem_events, self.states) self.frame_dispatcher = frame_dispatcher.DISPATCHER(self.config, self.modem_events, self.states) + self.frame_dispatcher.start() self.states.set("is_modem_running", True) self.modem.set_FFT_stream(self.enable_fft) return True