Refactoring of tx commands - WIP

This commit is contained in:
Pedro 2023-11-23 16:59:53 +01:00
parent 5c34ef40da
commit 5bb25c3d45
7 changed files with 71 additions and 38 deletions

25
modem/command.py Normal file
View file

@ -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(
)

3
modem/command_cq.py Normal file
View file

@ -0,0 +1,3 @@
from command import TxCommand
class CQCommand(TxCommand):

11
modem/command_ping.py Normal file
View file

@ -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)

View file

@ -1,23 +1,23 @@
from modem_frametypes import FRAME_TYPE as FR_TYPE from modem_frametypes import FRAME_TYPE as FR_TYPE
import helpers import helpers
import codec2
class DataFrameFactory: class DataFrameFactory:
def __init__(self, modem_config, modem_state, **data): def __init__(self, modem):
self.modem_config = modem_config self.modem_config = modem.config
self.modem_state = modem_state self.modem_state = modem.state
self.data = data
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): def build(self):
build_method = getattr(self, self.type.name) build_method = getattr(self, self.type.name)
return build_method() return build_method()
def build_ping(self): def build_ping(self, dxcallsign):
ping_frame = bytearray(self.length_sig0_frame) ping_frame = bytearray(self.length_sig0_frame)
ping_frame[:1] = bytes(self.type.value) 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[4:7] = helpers.get_crc_24(self.myfullcall)
ping_frame[7:13] = helpers.callsign_to_bytes(self.myfullcall) ping_frame[7:13] = helpers.callsign_to_bytes(self.myfullcall)
return ping_frame return ping_frame
@ -25,20 +25,20 @@ class DataFrameFactory:
def build_cq(self): def build_cq(self):
cq_frame = bytearray(self.length_sig0_frame) cq_frame = bytearray(self.length_sig0_frame)
cq_frame[:1] = bytes([FR_TYPE.CQ.value]) 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) cq_frame[7:11] = helpers.encode_grid(self.mygrid)
return cq_frame return cq_frame
def build_fec_is_writing(self): def build_fec_is_writing(self):
fec_frame = bytearray(14) fec_frame = bytearray(14)
fec_frame[:1] = bytes([FR_TYPE.IS_WRITING.value]) 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 return fec_frame
def build_qrv(self): def build_qrv(self):
qrv_frame = bytearray(self.length_sig0_frame) qrv_frame = bytearray(self.length_sig0_frame)
qrv_frame[:1] = bytes([FR_TYPE.QRV.value]) 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[7:11] = helpers.encode_grid(self.mygrid)
qrv_frame[11:12] = helpers.snr_to_bytes(snr) qrv_frame[11:12] = helpers.snr_to_bytes(snr)
return qrv_frame return qrv_frame
@ -46,16 +46,16 @@ class DataFrameFactory:
def build_beacon(self): def build_beacon(self):
beacon_frame = bytearray(self.length_sig0_frame) beacon_frame = bytearray(self.length_sig0_frame)
beacon_frame[:1] = bytes([FR_TYPE.BEACON.value]) 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) beacon_frame[7:11] = helpers.encode_grid(self.mygrid)
return beacon_frame return beacon_frame
def build_fec_wakeup(self): def build_fec_wakeup(self):
mode_int_wakeup = codec2.freedv_get_mode_value_by_name("sig0") 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 = bytearray(payload_per_wakeup_frame)
fec_wakeup_frame[:1] = bytes([FR_TYPE.FEC_WAKEUP.value]) 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[7:8] = bytes([mode_int])
fec_wakeup_frame[8:9] = bytes([1]) # n payload bursts fec_wakeup_frame[8:9] = bytes([1]) # n payload bursts
return fec_wakeup_frame return fec_wakeup_frame

View file

@ -27,7 +27,6 @@ class DISPATCHER():
self._initialize_handlers(config, event_queue, states) self._initialize_handlers(config, event_queue, states)
self._initialize_dispatchers() self._initialize_dispatchers()
self._initialize_queues() self._initialize_queues()
self._start_worker_threads()
def _initialize_handlers(self, config, event_queue, states): def _initialize_handlers(self, config, event_queue, states):
"""Initializes various data handlers.""" """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_transmit, name="Transmit Worker", daemon=True).start()
threading.Thread(target=self.worker_receive, name="Receive 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: def worker_transmit(self) -> None:
"""Dispatch incoming UI instructions for transmitting operations""" """Dispatch incoming UI instructions for transmitting operations"""
while True: while True:
data = self.data_queue_transmit.get() 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( self.log.debug(
"[Modem] TX DISPATCHER - waiting with processing command ", "[Modem] TX DISPATCHER - got a transmit command",
is_arq_state=self.states.is_arq_state, 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 # Dispatch commands known to command_dispatcher
if data[0] in self.command_dispatcher: if data[0] in self.command_dispatcher:

View file

@ -12,6 +12,9 @@ import state_manager
import ujson as json import ujson as json
import websocket_manager as wsm import websocket_manager as wsm
import api_validations as validations 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__) app = Flask(__name__)
CORS(app) CORS(app)
@ -72,6 +75,12 @@ def validate(req, param, validator, isRequired = True):
if not validator(req[param]): if not validator(req[param]):
api_abort(f"Value of '{param}' is invalid.", 400) 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 ## REST API
@app.route('/', methods=['GET']) @app.route('/', methods=['GET'])
def index(): def index():
@ -138,8 +147,8 @@ def post_ping():
if not app.state_manager.is_modem_running: if not app.state_manager.is_modem_running:
api_abort('Modem not running', 503) api_abort('Modem not running', 503)
validate(request.json, 'dxcall', validations.validate_freedata_callsign) validate(request.json, 'dxcall', validations.validate_freedata_callsign)
server_commands.ping_ping(request.json['dxcall']) enqueue_tx_command(PingCommand, request.json)
return api_response(request.json) return 'ok'
@app.route('/modem/send_test_frame', methods=['POST']) @app.route('/modem/send_test_frame', methods=['POST'])
def post_send_test_frame(): def post_send_test_frame():

View file

@ -72,6 +72,7 @@ class SM:
self.modem = modem.RF(self.config, self.modem_events, self.modem_fft, self.modem_service, self.states) 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.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 = frame_dispatcher.DISPATCHER(self.config, self.modem_events, self.states)
self.frame_dispatcher.start()
self.states.set("is_modem_running", True) self.states.set("is_modem_running", True)
self.modem.set_FFT_stream(self.enable_fft) self.modem.set_FFT_stream(self.enable_fft)
return True return True