mirror of https://github.com/DJ2LS/FreeDATA.git
more docstrings
parent
e6b3e83790
commit
f40c021a43
|
|
@ -3,13 +3,35 @@ import json
|
|||
import structlog
|
||||
|
||||
class EventManager:
|
||||
"""Manages and broadcasts events within the FreeDATA server.
|
||||
|
||||
This class handles the broadcasting of various events, including PTT
|
||||
changes, scatter changes, buffer overflows, custom events, ARQ session
|
||||
updates, and freedata_server status changes, to multiple queues. It
|
||||
provides a centralized mechanism for distributing event information
|
||||
throughout the application.
|
||||
"""
|
||||
|
||||
def __init__(self, queues):
|
||||
"""Initializes the EventManager with a list of queues.
|
||||
|
||||
Args:
|
||||
queues (list): A list of queues to which events will be broadcast.
|
||||
"""
|
||||
self.queues = queues
|
||||
self.logger = structlog.get_logger('Event Manager')
|
||||
self.lastpttstate = False
|
||||
|
||||
def broadcast(self, data):
|
||||
"""Broadcasts an event to all registered queues.
|
||||
|
||||
This method broadcasts the given event data to all queues registered
|
||||
with the EventManager. It clears a queue if its size exceeds 10 to
|
||||
prevent excessive queue buildup.
|
||||
|
||||
Args:
|
||||
data: The event data to broadcast.
|
||||
"""
|
||||
for q in self.queues:
|
||||
self.logger.debug(f"Event: ", ev=data)
|
||||
if q.qsize() > 10:
|
||||
|
|
@ -17,21 +39,72 @@ class EventManager:
|
|||
q.put(data)
|
||||
|
||||
def send_ptt_change(self, on:bool = False):
|
||||
"""Sends a PTT change event.
|
||||
|
||||
This method broadcasts a "ptt" event indicating whether the Push-to-Talk
|
||||
(PTT) is activated or deactivated. It avoids sending duplicate events
|
||||
by checking the last PTT state.
|
||||
|
||||
Args:
|
||||
on (bool, optional): True if PTT is activated, False otherwise. Defaults to False.
|
||||
"""
|
||||
if (on == self.lastpttstate):
|
||||
return
|
||||
self.lastpttstate= on
|
||||
self.broadcast({"ptt": bool(on)})
|
||||
|
||||
def send_scatter_change(self, data):
|
||||
"""Sends a scatter change event.
|
||||
|
||||
This method broadcasts a "scatter" event containing the provided
|
||||
scatter data as a JSON string.
|
||||
|
||||
Args:
|
||||
data: The scatter data to send.
|
||||
"""
|
||||
self.broadcast({"scatter": json.dumps(data)})
|
||||
|
||||
def send_buffer_overflow(self, data):
|
||||
"""Sends a buffer overflow event.
|
||||
|
||||
This method broadcasts a "buffer-overflow" event, indicating that a
|
||||
buffer overflow has occurred. The event data includes the provided
|
||||
data converted to a string.
|
||||
|
||||
Args:
|
||||
data: The buffer overflow data to send.
|
||||
"""
|
||||
self.broadcast({"buffer-overflow": str(data)})
|
||||
|
||||
def send_custom_event(self, **event_data):
|
||||
"""Sends a custom event.
|
||||
|
||||
This method broadcasts a custom event with the provided keyword
|
||||
arguments as the event data. This allows for flexible creation and
|
||||
distribution of application-specific events.
|
||||
|
||||
Args:
|
||||
**event_data: Keyword arguments representing the event data.
|
||||
"""
|
||||
self.broadcast(event_data)
|
||||
|
||||
def send_arq_session_new(self, outbound: bool, session_id, dxcall, total_bytes, state):
|
||||
"""Sends an event for a new ARQ session.
|
||||
|
||||
This method broadcasts an event indicating the start of a new ARQ
|
||||
(Automatic Repeat reQuest) session. The event includes information
|
||||
about the session's direction (inbound or outbound), session ID,
|
||||
destination callsign, total bytes to be transferred, and initial
|
||||
state.
|
||||
|
||||
Args:
|
||||
outbound (bool): True if the session is outbound (sending data),
|
||||
False if it's inbound (receiving data).
|
||||
session_id: The unique ID of the ARQ session.
|
||||
dxcall (str): The callsign of the remote station.
|
||||
total_bytes (int): The total number of bytes to be transferred.
|
||||
state (str): The initial state of the ARQ session.
|
||||
"""
|
||||
direction = 'outbound' if outbound else 'inbound'
|
||||
event = {
|
||||
"type": "arq",
|
||||
|
|
@ -45,6 +118,23 @@ class EventManager:
|
|||
self.broadcast(event)
|
||||
|
||||
def send_arq_session_progress(self, outbound: bool, session_id, dxcall, received_bytes, total_bytes, state, speed_level, statistics=None):
|
||||
"""Sends an ARQ session progress update event.
|
||||
|
||||
This method broadcasts an event indicating the progress of an ARQ
|
||||
session. The event includes the session ID, destination callsign,
|
||||
received and total bytes, current state, speed level, and any
|
||||
relevant statistics.
|
||||
|
||||
Args:
|
||||
outbound (bool): True if the session is outbound, False otherwise.
|
||||
session_id: The ID of the ARQ session.
|
||||
dxcall (str): The callsign of the remote station.
|
||||
received_bytes (int): The number of bytes received so far.
|
||||
total_bytes (int): The total number of bytes to be transferred.
|
||||
state (str): The current state of the ARQ session.
|
||||
speed_level: The current speed level of the ARQ session.
|
||||
statistics (dict, optional): A dictionary containing session statistics. Defaults to None.
|
||||
"""
|
||||
if statistics is None:
|
||||
statistics = {}
|
||||
|
||||
|
|
@ -64,6 +154,23 @@ class EventManager:
|
|||
self.broadcast(event)
|
||||
|
||||
def send_arq_session_finished(self, outbound: bool, session_id, dxcall, success: bool, state: bool, data=False, statistics=None):
|
||||
"""Sends an ARQ session finished event.
|
||||
|
||||
This method broadcasts an event indicating the completion of an ARQ
|
||||
session. The event includes information about the session's direction,
|
||||
ID, destination callsign, success status, final state, data
|
||||
(if any), and statistics. It base64-encodes any data included in
|
||||
the event.
|
||||
|
||||
Args:
|
||||
outbound (bool): True if the session was outbound, False otherwise.
|
||||
session_id: The ID of the ARQ session.
|
||||
dxcall (str): The callsign of the remote station.
|
||||
success (bool): True if the session completed successfully, False otherwise.
|
||||
state (str): The final state of the ARQ session.
|
||||
data (any, optional): The data transferred during the session. Defaults to False.
|
||||
statistics (dict, optional): A dictionary of session statistics. Defaults to None.
|
||||
"""
|
||||
if statistics is None:
|
||||
statistics = {}
|
||||
if data:
|
||||
|
|
@ -86,20 +193,49 @@ class EventManager:
|
|||
self.broadcast(event)
|
||||
|
||||
def modem_started(self):
|
||||
"""Sends a freedata_server started event.
|
||||
|
||||
This method broadcasts an event indicating that the FreeDATA freedata_server
|
||||
has started successfully.
|
||||
"""
|
||||
event = {"freedata_server": "started"}
|
||||
self.broadcast(event)
|
||||
|
||||
def modem_restarted(self):
|
||||
"""Sends a freedata_server restarted event.
|
||||
|
||||
This method broadcasts an event indicating that the FreeDATA freedata_server
|
||||
has been restarted.
|
||||
"""
|
||||
event = {"freedata_server": "restarted"}
|
||||
self.broadcast(event)
|
||||
|
||||
def modem_stopped(self):
|
||||
"""Sends a freedata_server stopped event.
|
||||
|
||||
This method broadcasts an event indicating that the FreeDATA freedata_server
|
||||
has stopped.
|
||||
"""
|
||||
event = {"freedata_server": "stopped"}
|
||||
self.broadcast(event)
|
||||
|
||||
def modem_failed(self):
|
||||
"""Sends a freedata_server failed event.
|
||||
|
||||
This method broadcasts an event indicating that the FreeDATA freedata_server
|
||||
has failed to start or has encountered an error.
|
||||
"""
|
||||
event = {"freedata_server": "failed"}
|
||||
self.broadcast(event)
|
||||
|
||||
def freedata_message_db_change(self, message_id=None):
|
||||
"""Sends a FreeDATA message database change event.
|
||||
|
||||
This method broadcasts an event indicating that the FreeDATA message
|
||||
database has been changed. The event includes the ID of the message
|
||||
that triggered the change, if available.
|
||||
|
||||
Args:
|
||||
message_id (any, optional): The ID of the changed message. Defaults to None.
|
||||
"""
|
||||
self.broadcast({"message-db": "changed", "message_id": message_id})
|
||||
|
|
@ -15,7 +15,24 @@ from constants import EXPLORER_API_URL
|
|||
log = structlog.get_logger("explorer")
|
||||
|
||||
class Explorer:
|
||||
"""Pushes station and last heard data to the FreeDATA explorer.
|
||||
|
||||
This class collects station information, including callsign, gridsquare,
|
||||
frequency, signal strength, version, bandwidth, beacon status, and
|
||||
last heard stations, and pushes this data to the FreeDATA explorer API.
|
||||
"""
|
||||
def __init__(self, modem_version, config_manager, states):
|
||||
"""Initializes the Explorer.
|
||||
|
||||
This method initializes the Explorer with the modem version,
|
||||
configuration manager, state manager, and the URL of the FreeDATA
|
||||
explorer API.
|
||||
|
||||
Args:
|
||||
modem_version (str): The version of the FreeDATA modem.
|
||||
config_manager (ConfigManager): The configuration manager object.
|
||||
states (StateManager): The state manager object.
|
||||
"""
|
||||
self.modem_version = modem_version
|
||||
self.config_manager = config_manager
|
||||
self.config = self.config_manager.read()
|
||||
|
|
@ -23,6 +40,13 @@ class Explorer:
|
|||
self.explorer_url = EXPLORER_API_URL
|
||||
|
||||
def push(self):
|
||||
"""Pushes station and last heard data to the explorer.
|
||||
|
||||
This method collects station information from the configuration and
|
||||
state manager, formats it as JSON, and sends it to the FreeDATA
|
||||
explorer API. It includes error handling and logging for successful
|
||||
pushes, failed pushes, and connection issues.
|
||||
"""
|
||||
self.config = self.config_manager.read()
|
||||
|
||||
frequency = 0 if self.states.radio_frequency is None else self.states.radio_frequency
|
||||
|
|
|
|||
|
|
@ -18,7 +18,14 @@ from frame_handler_beacon import BeaconFrameHandler
|
|||
|
||||
|
||||
|
||||
class DISPATCHER():
|
||||
class DISPATCHER:
|
||||
"""Dispatches received frames to appropriate handlers.
|
||||
|
||||
This class manages the dispatching of received frames to the correct
|
||||
handler based on the frame type. It initializes frame handlers, starts
|
||||
worker threads for receiving and processing frames, and provides a
|
||||
mechanism for stopping the dispatcher.
|
||||
"""
|
||||
|
||||
FRAME_HANDLER = {
|
||||
FR_TYPE.ARQ_SESSION_OPEN_ACK.value: {"class": ARQFrameHandler, "name": "ARQ OPEN ACK"},
|
||||
|
|
@ -52,6 +59,18 @@ class DISPATCHER():
|
|||
}
|
||||
|
||||
def __init__(self, config, event_manager, states, modem):
|
||||
"""Initializes the frame dispatcher.
|
||||
|
||||
This method sets up the frame dispatcher with the provided
|
||||
configuration, event manager, state manager, and modem. It
|
||||
initializes frame handlers and starts the receive worker thread.
|
||||
|
||||
Args:
|
||||
config (dict): The configuration dictionary.
|
||||
event_manager (EventManager): The event manager object.
|
||||
states (StateManager): The state manager object.
|
||||
modem: The modem object.
|
||||
"""
|
||||
self.log = structlog.get_logger("frame_dispatcher")
|
||||
|
||||
self.log.info("loading frame dispatcher.....\n")
|
||||
|
|
@ -99,6 +118,21 @@ class DISPATCHER():
|
|||
continue
|
||||
|
||||
def process_data(self, bytes_out, freedv, bytes_per_frame: int, snr, frequency_offset, mode_name) -> None:
|
||||
"""Processes received data frames.
|
||||
|
||||
This method deconstructs the received data into a frame dictionary,
|
||||
identifies the frame type, and dispatches the frame to the
|
||||
appropriate handler based on its type. It logs warnings for
|
||||
unknown frame types.
|
||||
|
||||
Args:
|
||||
bytes_out (bytes): The raw frame data.
|
||||
freedv: The FreeDV instance.
|
||||
bytes_per_frame (int): The number of bytes per frame.
|
||||
snr (float): The signal-to-noise ratio of the received frame.
|
||||
frequency_offset (float): The frequency offset of the received frame.
|
||||
mode_name (str): The name of the FreeDV mode.
|
||||
"""
|
||||
# get frame as dictionary
|
||||
deconstructed_frame = self.frame_factory.deconstruct(bytes_out, mode_name=mode_name)
|
||||
frametype = deconstructed_frame["frame_type_int"]
|
||||
|
|
@ -106,17 +140,31 @@ class DISPATCHER():
|
|||
self.log.warning(
|
||||
"[DISPATCHER] ARQ - other frame type", frametype=FR_TYPE(frametype).name)
|
||||
return
|
||||
|
||||
|
||||
# instantiate handler
|
||||
handler_class = self.FRAME_HANDLER[frametype]['class']
|
||||
handler: FrameHandler = handler_class(self.FRAME_HANDLER[frametype]['name'],
|
||||
self.config,
|
||||
self.states,
|
||||
self.event_manager,
|
||||
self.modem)
|
||||
self.config,
|
||||
self.states,
|
||||
self.event_manager,
|
||||
self.modem)
|
||||
handler.handle(deconstructed_frame, snr, frequency_offset, freedv, bytes_per_frame)
|
||||
|
||||
def get_id_from_frame(self, data):
|
||||
"""Extracts the session ID from an ARQ_SESSION_OPEN frame.
|
||||
|
||||
This method checks if the provided data represents an
|
||||
ARQ_SESSION_OPEN frame and, if so, extracts and returns the session
|
||||
ID. Otherwise, it returns None. This method is currently not used
|
||||
in the code.
|
||||
|
||||
Args:
|
||||
data (bytes): The frame data.
|
||||
|
||||
Returns:
|
||||
bytes or None: The session ID if the frame is an ARQ_SESSION_OPEN
|
||||
frame, None otherwise.
|
||||
"""
|
||||
if data[:1] == FR_TYPE.ARQ_SESSION_OPEN:
|
||||
return data[13:14]
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -13,10 +13,25 @@ TESTMODE = False
|
|||
|
||||
|
||||
class FrameHandler():
|
||||
"""Base class for handling received frames.
|
||||
|
||||
This class provides common functionality for processing received frames,
|
||||
including checking if the frame is addressed to the current station,
|
||||
adding activity to the activity list, managing heard stations, emitting
|
||||
events, and transmitting responses. Subclasses implement the
|
||||
`follow_protocol` method to handle specific frame types and protocols.
|
||||
"""
|
||||
def __init__(self, name: str, config, states: StateManager, event_manager: EventManager,
|
||||
modem) -> None:
|
||||
|
||||
"""Initializes a new FrameHandler instance.
|
||||
|
||||
Args:
|
||||
name (str): The name of the frame handler.
|
||||
config (dict): The configuration dictionary.
|
||||
states (StateManager): The state manager object.
|
||||
event_manager (EventManager): The event manager object.
|
||||
modem: The modem object.
|
||||
"""
|
||||
self.name = name
|
||||
self.config = config
|
||||
self.states = states
|
||||
|
|
@ -33,54 +48,85 @@ class FrameHandler():
|
|||
}
|
||||
|
||||
def is_frame_for_me(self):
|
||||
call_with_ssid = self.config['STATION']['mycall'] + "-" + str(self.config['STATION']['myssid'])
|
||||
ft = self.details['frame']['frame_type']
|
||||
valid = False
|
||||
|
||||
# Check for callsign checksum
|
||||
if ft in ['ARQ_SESSION_OPEN', 'ARQ_SESSION_OPEN_ACK', 'PING', 'PING_ACK', 'P2P_CONNECTION_CONNECT']:
|
||||
valid, mycallsign = helpers.check_callsign(
|
||||
call_with_ssid,
|
||||
self.details["frame"]["destination_crc"],
|
||||
self.config['STATION']['ssid_list'])
|
||||
"""Checks if the received frame is addressed to this station.
|
||||
|
||||
# Check for session id on IRS side
|
||||
elif ft in ['ARQ_SESSION_INFO', 'ARQ_BURST_FRAME', 'ARQ_STOP']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.arq_irs_sessions:
|
||||
valid = True
|
||||
This method checks if the received frame is intended for this
|
||||
station by verifying the destination callsign CRC and SSID against
|
||||
the station's configured callsign and SSID list. It also checks for
|
||||
session IDs in the case of ARQ and P2P frames.
|
||||
|
||||
# Check for session id on ISS side
|
||||
elif ft in ['ARQ_SESSION_INFO_ACK', 'ARQ_BURST_ACK', 'ARQ_STOP_ACK']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.arq_iss_sessions:
|
||||
valid = True
|
||||
|
||||
# check for p2p connection
|
||||
elif ft in ['P2P_CONNECTION_CONNECT']:
|
||||
valid, mycallsign = helpers.check_callsign(
|
||||
call_with_ssid,
|
||||
self.details["frame"]["destination_crc"],
|
||||
self.config['STATION']['ssid_list'])
|
||||
|
||||
#check for p2p connection
|
||||
elif ft in ['P2P_CONNECTION_CONNECT_ACK', 'P2P_CONNECTION_PAYLOAD', 'P2P_CONNECTION_PAYLOAD_ACK', 'P2P_CONNECTION_DISCONNECT', 'P2P_CONNECTION_DISCONNECT_ACK']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.p2p_connection_sessions:
|
||||
valid = True
|
||||
|
||||
else:
|
||||
Returns:
|
||||
bool: True if the frame is for this station, False otherwise.
|
||||
"""
|
||||
call_with_ssid = self.config['STATION']['mycall'] + "-" + str(self.config['STATION']['myssid'])
|
||||
ft = self.details['frame']['frame_type']
|
||||
valid = False
|
||||
|
||||
if not valid:
|
||||
self.logger.info(f"[Frame handler] {ft} received but not for us.")
|
||||
# Check for callsign checksum
|
||||
if ft in ['ARQ_SESSION_OPEN', 'ARQ_SESSION_OPEN_ACK', 'PING', 'PING_ACK', 'P2P_CONNECTION_CONNECT']:
|
||||
valid, mycallsign = helpers.check_callsign(
|
||||
call_with_ssid,
|
||||
self.details["frame"]["destination_crc"],
|
||||
self.config['STATION']['ssid_list'])
|
||||
|
||||
# Check for session id on IRS side
|
||||
elif ft in ['ARQ_SESSION_INFO', 'ARQ_BURST_FRAME', 'ARQ_STOP']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.arq_irs_sessions:
|
||||
valid = True
|
||||
|
||||
# Check for session id on ISS side
|
||||
elif ft in ['ARQ_SESSION_INFO_ACK', 'ARQ_BURST_ACK', 'ARQ_STOP_ACK']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.arq_iss_sessions:
|
||||
valid = True
|
||||
|
||||
# check for p2p connection
|
||||
elif ft in ['P2P_CONNECTION_CONNECT']:
|
||||
valid, mycallsign = helpers.check_callsign(
|
||||
call_with_ssid,
|
||||
self.details["frame"]["destination_crc"],
|
||||
self.config['STATION']['ssid_list'])
|
||||
|
||||
# check for p2p connection
|
||||
elif ft in ['P2P_CONNECTION_CONNECT_ACK', 'P2P_CONNECTION_PAYLOAD', 'P2P_CONNECTION_PAYLOAD_ACK',
|
||||
'P2P_CONNECTION_DISCONNECT', 'P2P_CONNECTION_DISCONNECT_ACK']:
|
||||
session_id = self.details['frame']['session_id']
|
||||
if session_id in self.states.p2p_connection_sessions:
|
||||
valid = True
|
||||
|
||||
else:
|
||||
valid = False
|
||||
|
||||
if not valid:
|
||||
self.logger.info(f"[Frame handler] {ft} received but not for us.")
|
||||
|
||||
return valid
|
||||
|
||||
return valid
|
||||
|
||||
def should_respond(self):
|
||||
"""Checks if the frame handler should respond to the received frame.
|
||||
|
||||
This method simply calls is_frame_for_me() to determine if a
|
||||
response is necessary. It can be overridden by subclasses to
|
||||
implement more complex response logic.
|
||||
|
||||
Returns:
|
||||
bool: True if the handler should respond, False otherwise.
|
||||
"""
|
||||
return self.is_frame_for_me()
|
||||
|
||||
def is_origin_on_blacklist(self):
|
||||
"""Checks if the origin callsign is on the blacklist.
|
||||
|
||||
This method checks if the origin callsign of the received frame is
|
||||
present in the callsign blacklist defined in the configuration.
|
||||
It handles callsigns with SSIDs by removing the suffix and performs
|
||||
a case-insensitive comparison.
|
||||
|
||||
Returns:
|
||||
bool: True if the origin callsign is blacklisted, False otherwise.
|
||||
"""
|
||||
origin_callsign = self.details["frame"]["origin"]
|
||||
|
||||
# Remove the suffix after the hyphen if it exists
|
||||
|
|
@ -96,6 +142,13 @@ class FrameHandler():
|
|||
|
||||
|
||||
def add_to_activity_list(self):
|
||||
"""Adds the received frame to the activity list.
|
||||
|
||||
This method extracts relevant information from the received frame,
|
||||
such as origin, destination, gridsquare, SNR, frequency offset,
|
||||
activity type, session ID, and away-from-key status, and adds it
|
||||
as a new activity to the state manager's activity list.
|
||||
"""
|
||||
frame = self.details['frame']
|
||||
|
||||
activity = {
|
||||
|
|
@ -123,6 +176,14 @@ class FrameHandler():
|
|||
self.states.add_activity(activity)
|
||||
|
||||
def add_to_heard_stations(self):
|
||||
"""Adds the received frame's origin station to the heard stations list.
|
||||
|
||||
This method extracts information from the received frame, including
|
||||
callsign, gridsquare, signal strength, frequency offset, and
|
||||
away-from-key status, and adds it to the heard stations list in the
|
||||
state manager. It also calculates the distance between the current
|
||||
station and the received station if gridsquares are available.
|
||||
"""
|
||||
frame = self.details['frame']
|
||||
|
||||
if 'origin' not in frame:
|
||||
|
|
@ -157,6 +218,16 @@ class FrameHandler():
|
|||
away_from_key=away_from_key
|
||||
)
|
||||
def make_event(self):
|
||||
"""Creates a frame received event dictionary.
|
||||
|
||||
This method constructs a dictionary containing information about the
|
||||
received frame, including timestamps, callsigns, gridsquares, signal
|
||||
strength, and distance. This dictionary is used for emitting events
|
||||
related to frame reception.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the event data.
|
||||
"""
|
||||
|
||||
event = {
|
||||
"type": "frame-handler",
|
||||
|
|
@ -185,26 +256,75 @@ class FrameHandler():
|
|||
return event
|
||||
|
||||
def emit_event(self):
|
||||
"""Emits a frame received event.
|
||||
|
||||
This method creates an event dictionary containing information about
|
||||
the received frame, such as the frame type, timestamp, callsigns,
|
||||
gridsquare, SNR, distance, and away-from-key status. It then
|
||||
broadcasts this event through the event manager.
|
||||
"""
|
||||
event_data = self.make_event()
|
||||
print(event_data)
|
||||
self.event_manager.broadcast(event_data)
|
||||
|
||||
def get_tx_mode(self):
|
||||
"""Returns the transmission mode for acknowledgements.
|
||||
|
||||
This method returns the FreeDV mode used for transmitting
|
||||
acknowledgement frames. Currently, it always returns the signalling
|
||||
mode.
|
||||
|
||||
Returns:
|
||||
FREEDV_MODE: The FreeDV mode for transmissions.
|
||||
"""
|
||||
return FREEDV_MODE.signalling
|
||||
|
||||
def transmit(self, frame):
|
||||
"""Transmits a frame using the modem.
|
||||
|
||||
This method transmits the given frame using the modem. In test mode,
|
||||
it broadcasts the frame through the event manager instead of using
|
||||
the modem.
|
||||
|
||||
Args:
|
||||
frame: The frame to transmit.
|
||||
"""
|
||||
if not TESTMODE:
|
||||
self.modem.transmit(self.get_tx_mode(), 1, 0, frame)
|
||||
else:
|
||||
self.event_manager.broadcast(frame)
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Handles protocol-specific actions for the received frame.
|
||||
|
||||
This method is intended to be overridden by subclasses to implement
|
||||
specific protocol handling logic for different frame types. The base
|
||||
implementation does nothing.
|
||||
"""
|
||||
pass
|
||||
|
||||
def log(self):
|
||||
"""Logs the frame type being handled."""
|
||||
self.logger.info(f"[Frame Handler] Handling frame {self.details['frame']['frame_type']}")
|
||||
|
||||
def handle(self, frame, snr, frequency_offset, freedv_inst, bytes_per_frame):
|
||||
"""Handles a received frame.
|
||||
|
||||
This method processes the received frame, updates internal state,
|
||||
performs blacklist checks, adds the frame to activity lists and heard
|
||||
stations, emits an event, and calls the follow_protocol method for
|
||||
subclass-specific handling.
|
||||
|
||||
Args:
|
||||
frame (dict): The received frame data.
|
||||
snr (float): The signal-to-noise ratio of the received frame.
|
||||
frequency_offset (float): The frequency offset of the received frame.
|
||||
freedv_inst: The FreeDV instance.
|
||||
bytes_per_frame (int): The number of bytes per frame.
|
||||
|
||||
Returns:
|
||||
bool: True if the frame was processed successfully, False if it was blocked due to blacklisting.
|
||||
"""
|
||||
self.details['frame'] = frame
|
||||
self.details['snr'] = snr
|
||||
self.details['frequency_offset'] = frequency_offset
|
||||
|
|
@ -253,3 +373,4 @@ class FrameHandler():
|
|||
self.add_to_activity_list()
|
||||
self.emit_event()
|
||||
self.follow_protocol()
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -9,8 +9,22 @@ from arq_session_irs import IRS_State
|
|||
|
||||
|
||||
class ARQFrameHandler(frame_handler.FrameHandler):
|
||||
"""Handles ARQ frames and manages ARQ sessions.
|
||||
|
||||
This class processes incoming ARQ frames, manages ARQ sessions for both
|
||||
ISS (Information Sending Station) and IRS (Information Receiving Station),
|
||||
and dispatches frames to the appropriate session based on their type
|
||||
and session ID.
|
||||
"""
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Processes the received ARQ frame based on its type.
|
||||
|
||||
This method handles different ARQ frame types, including session
|
||||
open, information, burst data, stop, and various acknowledgements.
|
||||
It manages session creation, retrieval, and updates based on the
|
||||
frame type and session ID.
|
||||
"""
|
||||
|
||||
if not self.should_respond():
|
||||
return
|
||||
|
|
|
|||
|
|
@ -6,8 +6,20 @@ from message_system_db_messages import DatabaseManagerMessages
|
|||
|
||||
from message_system_db_manager import DatabaseManager
|
||||
class BeaconFrameHandler(frame_handler.FrameHandler):
|
||||
"""Handles received beacon frames.
|
||||
|
||||
This class processes received beacon frames, stores them in the database,
|
||||
and checks for queued messages to be sent based on configuration and
|
||||
signal strength.
|
||||
"""
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Processes the received beacon frame.
|
||||
|
||||
This method adds the beacon information to the database and checks
|
||||
for queued messages to send if auto-repeat is enabled and the
|
||||
signal strength is above a certain threshold.
|
||||
"""
|
||||
DatabaseManagerBeacon(self.event_manager).add_beacon(datetime.datetime.now(),
|
||||
self.details['frame']["origin"],
|
||||
self.details["snr"],
|
||||
|
|
|
|||
|
|
@ -8,12 +8,25 @@ from message_system_db_messages import DatabaseManagerMessages
|
|||
import numpy as np
|
||||
|
||||
class CQFrameHandler(frame_handler.FrameHandler):
|
||||
"""Handles received CQ frames.
|
||||
|
||||
This class processes received CQ (Calling Any Station) frames and sends
|
||||
a QRV (Ready to Receive) frame as an acknowledgement if the station is
|
||||
not currently busy with ARQ. It also checks for queued messages to be
|
||||
sent based on the configuration.
|
||||
"""
|
||||
|
||||
#def should_respond(self):
|
||||
# self.logger.debug(f"Respond to CQ: {self.config['MODEM']['respond_to_cq']}")
|
||||
# return bool(self.config['MODEM']['respond_to_cq'] and not self.states.getARQ())
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Processes the received CQ frame.
|
||||
|
||||
This method checks if the modem is currently busy with ARQ. If not,
|
||||
it sends a QRV frame as an acknowledgement and checks for queued
|
||||
messages to send.
|
||||
"""
|
||||
|
||||
if self.states.getARQ():
|
||||
return
|
||||
|
|
|
|||
|
|
@ -6,8 +6,21 @@ from modem_frametypes import FRAME_TYPE as FR
|
|||
from p2p_connection import P2PConnection
|
||||
|
||||
class P2PConnectionFrameHandler(frame_handler.FrameHandler):
|
||||
"""Handles P2P connection frames.
|
||||
|
||||
This class processes P2P connection frames, manages P2P connections,
|
||||
and dispatches frames to the appropriate connection based on their
|
||||
type and session ID.
|
||||
"""
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Processes received P2P connection frames.
|
||||
|
||||
This method handles different P2P frame types, including connection
|
||||
requests, acknowledgements, payload data, disconnections, and payload
|
||||
acknowledgements. It manages connection creation, retrieval, and
|
||||
updates based on the frame type and session ID.
|
||||
"""
|
||||
|
||||
if not self.should_respond():
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ from message_system_db_messages import DatabaseManagerMessages
|
|||
|
||||
|
||||
class PingFrameHandler(frame_handler.FrameHandler):
|
||||
"""Handles received PING frames.
|
||||
|
||||
This class processes received PING frames, sends acknowledgements, and
|
||||
checks for queued messages to be sent based on configuration.
|
||||
"""
|
||||
|
||||
#def is_frame_for_me(self):
|
||||
# call_with_ssid = self.config['STATION']['mycall'] + "-" + str(self.config['STATION']['myssid'])
|
||||
|
|
@ -19,6 +24,12 @@ class PingFrameHandler(frame_handler.FrameHandler):
|
|||
# return valid
|
||||
|
||||
def follow_protocol(self):
|
||||
"""Processes the received PING frame.
|
||||
|
||||
This method checks if the frame is for the current station and if
|
||||
the modem is not busy with ARQ. If both conditions are met, it sends
|
||||
a PING acknowledgement and checks for queued messages to send.
|
||||
"""
|
||||
if not bool(self.is_frame_for_me() and not self.states.getARQ()):
|
||||
return
|
||||
self.logger.debug(
|
||||
|
|
@ -31,6 +42,11 @@ class PingFrameHandler(frame_handler.FrameHandler):
|
|||
self.check_for_queued_message()
|
||||
|
||||
def send_ack(self):
|
||||
"""Sends a PING acknowledgement frame.
|
||||
|
||||
This method builds a PING acknowledgement frame using the received
|
||||
frame's origin CRC and SNR, and transmits it using the modem.
|
||||
"""
|
||||
factory = data_frame_factory.DataFrameFactory(self.config)
|
||||
ping_ack_frame = factory.build_ping_ack(
|
||||
self.details['frame']['origin_crc'],
|
||||
|
|
@ -39,6 +55,13 @@ class PingFrameHandler(frame_handler.FrameHandler):
|
|||
self.transmit(ping_ack_frame)
|
||||
|
||||
def check_for_queued_message(self):
|
||||
"""Checks for queued messages to send.
|
||||
|
||||
This method checks if auto-repeat is enabled in the configuration
|
||||
and if the received signal strength is above a certain threshold.
|
||||
If both conditions are met, it sets any messages addressed to the
|
||||
originating station to 'queued' status in the message database.
|
||||
"""
|
||||
|
||||
# only check for queued messages, if we have enabled this and if we have a minimum snr received
|
||||
if self.config["MESSAGES"]["enable_auto_repeat"] and self.details["snr"] >= -2:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,24 @@ from socket_interface import SocketInterfaceHandler
|
|||
import queue
|
||||
|
||||
class SM:
|
||||
"""Manages the FreeDATA server services.
|
||||
|
||||
This class controls the starting, stopping, and restarting of the modem,
|
||||
radio manager, and socket interface. It handles commands from the modem
|
||||
service queue and performs actions based on the received commands.
|
||||
"""
|
||||
def __init__(self, app):
|
||||
"""Initializes the service manager.
|
||||
|
||||
This method sets up the service manager with references to the main
|
||||
application object and its components, including the config manager,
|
||||
modem, radio manager, state manager, event manager, and schedule
|
||||
manager. It also initializes the socket interface manager if enabled
|
||||
in the configuration and starts the runner thread.
|
||||
|
||||
Args:
|
||||
app: The main application object.
|
||||
"""
|
||||
self.log = structlog.get_logger("service manager")
|
||||
self.app = app
|
||||
self.modem = False
|
||||
|
|
@ -32,6 +49,13 @@ class SM:
|
|||
|
||||
|
||||
def runner(self):
|
||||
"""Main loop for handling service commands.
|
||||
|
||||
This method continuously monitors the modem service queue for
|
||||
commands and executes the corresponding actions. It handles starting,
|
||||
stopping, and restarting the modem, radio manager, and socket
|
||||
interface.
|
||||
"""
|
||||
while not self.shutdown_flag.is_set():
|
||||
try:
|
||||
cmd = self.modem_service.get()
|
||||
|
|
@ -84,6 +108,15 @@ class SM:
|
|||
self.modem_service.queue.clear()
|
||||
|
||||
def start_modem(self):
|
||||
"""Starts the FreeDATA modem.
|
||||
|
||||
This method initializes and starts the RF modem, frame dispatcher,
|
||||
and schedule manager. It performs checks for valid callsign and
|
||||
audio device functionality before starting the modem.
|
||||
|
||||
Returns:
|
||||
bool: True if the modem started successfully, False otherwise.
|
||||
"""
|
||||
|
||||
if self.config['STATION']['mycall'] in ['XX1XXX']:
|
||||
self.log.warning("wrong callsign in config! interrupting startup")
|
||||
|
|
@ -118,6 +151,14 @@ class SM:
|
|||
return True
|
||||
|
||||
def stop_modem(self):
|
||||
"""Stops the FreeDATA modem and related services.
|
||||
|
||||
This method stops the RF modem, frame dispatcher, and schedule
|
||||
manager. It also updates the modem running state and emits a
|
||||
'modem_stopped' event. It handles potential AttributeErrors that
|
||||
may occur during the stopping process if components are not
|
||||
initialized.
|
||||
"""
|
||||
self.log.warning("stopping modem....")
|
||||
try:
|
||||
if self.modem and hasattr(self.app, 'modem_service'):
|
||||
|
|
@ -141,6 +182,18 @@ class SM:
|
|||
self.event_manager.modem_stopped()
|
||||
|
||||
def test_audio(self):
|
||||
"""Tests the configured audio devices.
|
||||
|
||||
This method tests the input and output audio devices specified in
|
||||
the configuration. It logs the test results and returns a list
|
||||
indicating whether each device passed the test.
|
||||
|
||||
Returns:
|
||||
list: A list of booleans, where the first element represents the
|
||||
input device test result and the second element represents the
|
||||
output device test result. Returns [False, False] if an error
|
||||
occurs during testing.
|
||||
"""
|
||||
try:
|
||||
audio_test = audio.test_audio_devices(self.config['AUDIO']['input_device'],
|
||||
self.config['AUDIO']['output_device'])
|
||||
|
|
@ -152,14 +205,31 @@ class SM:
|
|||
return [False, False]
|
||||
|
||||
def start_radio_manager(self):
|
||||
"""Starts the radio manager.
|
||||
|
||||
This method initializes and starts the RadioManager, which handles
|
||||
communication with the radio.
|
||||
"""
|
||||
self.app.radio_manager = radio_manager.RadioManager(self.config, self.state_manager, self.event_manager)
|
||||
|
||||
def stop_radio_manager(self):
|
||||
"""Stops the radio manager.
|
||||
|
||||
This method stops the RadioManager and releases the associated
|
||||
resources. It handles potential AttributeErrors if the radio manager
|
||||
has not been initialized.
|
||||
"""
|
||||
if hasattr(self.app, 'radio_manager'):
|
||||
self.app.radio_manager.stop()
|
||||
del self.app.radio_manager
|
||||
|
||||
def shutdown(self):
|
||||
"""Shuts down the service manager.
|
||||
|
||||
This method stops the modem, sets the shutdown flag, and waits for
|
||||
the runner thread to finish. This ensures a clean shutdown of all
|
||||
managed services.
|
||||
"""
|
||||
self.log.warning("[SHUTDOWN] stopping service manager....")
|
||||
self.modem_service.put("stop")
|
||||
threading.Event().wait(2) # we need some time before processing with the shutdown_event_flag
|
||||
|
|
|
|||
|
|
@ -205,43 +205,134 @@ class StateManager:
|
|||
|
||||
# .wait() blocks until the event is set
|
||||
def isTransmitting(self):
|
||||
"""Checks if the server is currently transmitting.
|
||||
|
||||
This method returns True if the transmitting_event is not set,
|
||||
indicating that a transmission is in progress. Otherwise, it
|
||||
returns False.
|
||||
|
||||
Returns:
|
||||
bool: True if transmitting, False otherwise.
|
||||
"""
|
||||
return not self.transmitting_event.is_set()
|
||||
|
||||
# .wait() blocks until the event is set
|
||||
def setTransmitting(self, transmitting: bool):
|
||||
"""Sets the transmitting status of the server.
|
||||
|
||||
This method controls the transmitting_event, which is used for
|
||||
synchronization and blocking other operations during transmissions.
|
||||
If transmitting is True, the event is cleared (set to non-signaled
|
||||
state), causing any threads waiting on it to block. If transmitting
|
||||
is False, the event is set (signaled state), allowing waiting
|
||||
threads to proceed.
|
||||
|
||||
Args:
|
||||
transmitting (bool): True if the server is transmitting, False otherwise.
|
||||
"""
|
||||
if transmitting:
|
||||
self.transmitting_event.clear()
|
||||
else:
|
||||
self.transmitting_event.set()
|
||||
|
||||
def setARQ(self, busy):
|
||||
"""Sets the ARQ status.
|
||||
|
||||
This method sets the is_modem_busy event based on the provided
|
||||
busy flag. If busy is True, the event is cleared, indicating that
|
||||
the modem is busy with ARQ. If busy is False, the event is set,
|
||||
indicating that the modem is available.
|
||||
|
||||
Args:
|
||||
busy (bool): True if ARQ is busy, False otherwise.
|
||||
"""
|
||||
if busy:
|
||||
self.is_modem_busy.clear()
|
||||
else:
|
||||
self.is_modem_busy.set()
|
||||
|
||||
def getARQ(self):
|
||||
"""Gets the ARQ status.
|
||||
|
||||
This method returns True if the is_modem_busy event is not set,
|
||||
indicating that ARQ is currently not busy. Otherwise, it returns
|
||||
False.
|
||||
|
||||
Returns:
|
||||
bool: True if ARQ is not busy, False otherwise.
|
||||
"""
|
||||
return not self.is_modem_busy.is_set()
|
||||
|
||||
def waitForTransmission(self):
|
||||
"""Waits for any ongoing transmissions to complete.
|
||||
|
||||
This method blocks the calling thread until the transmitting_event
|
||||
is set, indicating that the server is no longer transmitting.
|
||||
"""
|
||||
self.transmitting_event.wait()
|
||||
|
||||
def waitForChannelBusy(self):
|
||||
"""Waits for the channel busy event.
|
||||
|
||||
This method waits for the channel_busy_event to be set, with a
|
||||
timeout of 2 seconds. This is used to pause operations when the
|
||||
channel is detected as busy.
|
||||
"""
|
||||
self.channel_busy_event.wait(2)
|
||||
|
||||
def register_arq_iss_session(self, session):
|
||||
"""Registers an ARQ ISS session.
|
||||
|
||||
This method registers an ARQ Information Sending Station (ISS) session
|
||||
by storing it in the arq_iss_sessions dictionary. It returns True
|
||||
if the session is successfully registered, False if a session with
|
||||
the same ID already exists.
|
||||
|
||||
Args:
|
||||
session (ARQSessionISS): The ARQ ISS session to register.
|
||||
|
||||
Returns:
|
||||
bool: True if the session was registered, False otherwise.
|
||||
"""
|
||||
if session.id in self.arq_iss_sessions:
|
||||
return False
|
||||
self.arq_iss_sessions[session.id] = session
|
||||
return True
|
||||
|
||||
def register_arq_irs_session(self, session):
|
||||
"""Registers an ARQ IRS session.
|
||||
|
||||
This method registers an ARQ Information Receiving Station (IRS)
|
||||
session, storing it in the arq_irs_sessions dictionary. It
|
||||
returns True if the session is registered successfully, or False
|
||||
if a session with the same ID already exists.
|
||||
|
||||
Args:
|
||||
session (ARQSessionIRS): The ARQ IRS session to register.
|
||||
|
||||
Returns:
|
||||
bool: True if the session was registered, False otherwise.
|
||||
"""
|
||||
if session.id in self.arq_irs_sessions:
|
||||
return False
|
||||
self.arq_irs_sessions[session.id] = session
|
||||
return True
|
||||
|
||||
def check_if_running_arq_session(self, irs=False):
|
||||
"""Checks if there is a running ARQ session.
|
||||
|
||||
This method iterates through either the ISS or IRS ARQ sessions,
|
||||
depending on the 'irs' flag. It cleans up outdated sessions and
|
||||
checks if any remaining sessions are not in a final state (ENDED,
|
||||
ABORTED, or FAILED).
|
||||
|
||||
Args:
|
||||
irs (bool, optional): If True, checks IRS sessions; otherwise,
|
||||
checks ISS sessions. Defaults to False (ISS sessions).
|
||||
|
||||
Returns:
|
||||
bool: True if there is a running ARQ session, False otherwise.
|
||||
"""
|
||||
sessions = self.arq_irs_sessions if irs else self.arq_iss_sessions
|
||||
|
||||
for session_id in sessions:
|
||||
|
|
@ -252,15 +343,26 @@ class StateManager:
|
|||
self.remove_arq_irs_session(session_id)
|
||||
else:
|
||||
self.remove_arq_iss_session(session_id)
|
||||
|
||||
|
||||
# check again if session id exists in session because of cleanup
|
||||
if session_id in sessions and sessions[session_id].state.name not in ['ENDED', 'ABORTED', 'FAILED']:
|
||||
print(f"[State Manager] running session...[{session_id}]")
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
||||
def get_arq_iss_session(self, id):
|
||||
"""Retrieves an ARQ ISS session by ID.
|
||||
|
||||
This method returns the ARQ Information Sending Station (ISS) session
|
||||
associated with the given ID. If no session with the given ID is
|
||||
found, it returns None.
|
||||
|
||||
Args:
|
||||
id: The ID of the ARQ ISS session.
|
||||
|
||||
Returns:
|
||||
ARQSessionISS or None: The ARQSessionISS object if found, None otherwise.
|
||||
"""
|
||||
if id not in self.arq_iss_sessions:
|
||||
#raise RuntimeError(f"ARQ ISS Session '{id}' not found!")
|
||||
# DJ2LS: WIP We need to find a better way of handling this
|
||||
|
|
@ -268,6 +370,18 @@ class StateManager:
|
|||
return self.arq_iss_sessions[id]
|
||||
|
||||
def get_arq_irs_session(self, id):
|
||||
"""Retrieves an ARQ IRS session by ID.
|
||||
|
||||
This method returns the ARQ Information Receiving Station (IRS) session
|
||||
associated with the given ID. It returns None if no session with the
|
||||
given ID is found.
|
||||
|
||||
Args:
|
||||
id: The ID of the ARQ IRS session.
|
||||
|
||||
Returns:
|
||||
ARQSessionIRS or None: The ARQSessionIRS object if found, None otherwise.
|
||||
"""
|
||||
if id not in self.arq_irs_sessions:
|
||||
#raise RuntimeError(f"ARQ IRS Session '{id}' not found!")
|
||||
# DJ2LS: WIP We need to find a better way of handling this
|
||||
|
|
@ -275,14 +389,39 @@ class StateManager:
|
|||
return self.arq_irs_sessions[id]
|
||||
|
||||
def remove_arq_iss_session(self, id):
|
||||
"""Removes an ARQ ISS session.
|
||||
|
||||
This method removes the ARQ Information Sending Station (ISS) session
|
||||
associated with the given ID from the arq_iss_sessions dictionary.
|
||||
|
||||
Args:
|
||||
id: The ID of the ARQ ISS session to remove.
|
||||
"""
|
||||
if id in self.arq_iss_sessions:
|
||||
del self.arq_iss_sessions[id]
|
||||
|
||||
def remove_arq_irs_session(self, id):
|
||||
"""Removes an ARQ IRS session.
|
||||
|
||||
This method removes the ARQ Information Receiving Station (IRS) session
|
||||
associated with the given ID from the arq_irs_sessions dictionary.
|
||||
|
||||
Args:
|
||||
id: The ID of the ARQ IRS session to remove.
|
||||
"""
|
||||
if id in self.arq_irs_sessions:
|
||||
del self.arq_irs_sessions[id]
|
||||
|
||||
def add_activity(self, activity_data):
|
||||
"""Adds a new activity to the activities list.
|
||||
|
||||
This method generates a unique ID for the activity, adds a timestamp
|
||||
and frequency if not provided, and stores the activity data in the
|
||||
activities list. It then triggers a state update.
|
||||
|
||||
Args:
|
||||
activity_data (dict): A dictionary containing the activity data.
|
||||
"""
|
||||
# Generate a random 8-byte string as hex
|
||||
activity_id = np.random.bytes(8).hex()
|
||||
|
||||
|
|
@ -297,12 +436,31 @@ class StateManager:
|
|||
self.sendStateUpdate(self.newstate)
|
||||
|
||||
def calculate_channel_busy_state(self):
|
||||
"""Calculates and sets the overall channel busy state.
|
||||
|
||||
This method determines the channel busy state based on the status
|
||||
of channel_busy_condition_traffic and
|
||||
channel_busy_condition_codec2. If both events are set (not busy),
|
||||
it sets the channel_busy_event, indicating the channel is available.
|
||||
Otherwise, it resets the channel_busy_event.
|
||||
"""
|
||||
if self.channel_busy_condition_traffic.is_set() and self.channel_busy_condition_codec2.is_set():
|
||||
self.channel_busy_event.set()
|
||||
else:
|
||||
self.channel_busy_event = threading.Event()
|
||||
|
||||
def set_channel_busy_condition_traffic(self, busy):
|
||||
"""Sets the channel busy condition based on data traffic.
|
||||
|
||||
This method sets or clears the channel_busy_condition_traffic event
|
||||
based on the provided busy flag. If busy is False, the event is set,
|
||||
indicating no traffic. If busy is True, the event is cleared,
|
||||
indicating traffic. It then recalculates the overall channel busy
|
||||
state.
|
||||
|
||||
Args:
|
||||
busy (bool): True if there is data traffic, False otherwise.
|
||||
"""
|
||||
if not busy:
|
||||
self.channel_busy_condition_traffic.set()
|
||||
else:
|
||||
|
|
@ -310,6 +468,17 @@ class StateManager:
|
|||
self.calculate_channel_busy_state()
|
||||
|
||||
def set_channel_busy_condition_codec2(self, traffic):
|
||||
"""Sets the channel busy condition based on Codec2 traffic.
|
||||
|
||||
This method sets or clears the channel_busy_condition_codec2 event
|
||||
based on the provided traffic flag. If traffic is False, the event
|
||||
is set, indicating no Codec2 traffic. If traffic is True, the event
|
||||
is cleared, indicating ongoing Codec2 traffic. It then recalculates
|
||||
the overall channel busy state.
|
||||
|
||||
Args:
|
||||
traffic (bool): True if there is Codec2 traffic, False otherwise.
|
||||
"""
|
||||
if not traffic:
|
||||
self.channel_busy_condition_codec2.set()
|
||||
else:
|
||||
|
|
@ -317,9 +486,27 @@ class StateManager:
|
|||
self.calculate_channel_busy_state()
|
||||
|
||||
def is_receiving_codec2_signal(self):
|
||||
"""Checks if the server is receiving a Codec2 signal.
|
||||
|
||||
This method returns True if the channel_busy_condition_codec2 event
|
||||
is not set, indicating that a Codec2 signal is being received.
|
||||
Otherwise, it returns False.
|
||||
|
||||
Returns:
|
||||
bool: True if a Codec2 signal is being received, False otherwise.
|
||||
"""
|
||||
return not self.channel_busy_condition_codec2.is_set()
|
||||
|
||||
def get_radio_status(self):
|
||||
"""Returns the current radio status.
|
||||
|
||||
This method returns a dictionary containing the current status
|
||||
information of the radio, including its status, frequency, mode,
|
||||
RF level, S-meter strength, SWR, and tuner status.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the radio status information.
|
||||
"""
|
||||
return {
|
||||
"radio_status": self.radio_status,
|
||||
"radio_frequency": self.radio_frequency,
|
||||
|
|
@ -331,6 +518,19 @@ class StateManager:
|
|||
}
|
||||
|
||||
def register_p2p_connection_session(self, session):
|
||||
"""Registers a P2P connection session.
|
||||
|
||||
This method registers a peer-to-peer (P2P) connection session by
|
||||
storing it in the p2p_connection_sessions dictionary. It returns
|
||||
True if the session is successfully registered, or False if a
|
||||
session with the same ID already exists.
|
||||
|
||||
Args:
|
||||
session (P2PConnection): The P2P connection session object.
|
||||
|
||||
Returns:
|
||||
bool: True if the session was registered, False otherwise.
|
||||
"""
|
||||
if session.session_id in self.p2p_connection_sessions:
|
||||
print("session already registered...")
|
||||
return False
|
||||
|
|
|
|||
Loading…
Reference in New Issue