Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions freedata_server/adif_udp_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ def send_thread():
ctx.event_manager.freedata_logging(type="udp", status=True, message=f" {call_value} ")

except socket.timeout:
log.info(
f"[CHAT] Timeout occurred sending ADIF data to {adif_log_host}:{adif_log_port}"
)
log.info(f"[CHAT] Timeout occurred sending ADIF data to {adif_log_host}:{adif_log_port}")
ctx.event_manager.freedata_logging(type="udp", status=True, message=f" {call_value} ")
except Exception as e:
log.info(f"[CHAT] Error sending ADIF data: {e}")
Expand Down
6 changes: 1 addition & 5 deletions freedata_server/api/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,7 @@ async def get_audio_devices(ctx: AppContext = Depends(get_ctx)):
responses={
200: {
"description": "List of available serial devices (COM ports).",
"content": {
"application/json": {
"example": [{"description": "n/a [26a9]", "port": "/dev/ttyS4"}]
}
},
"content": {"application/json": {"example": [{"description": "n/a [26a9]", "port": "/dev/ttyS4"}]}},
},
404: {
"description": "The requested resource was not found.",
Expand Down
68 changes: 17 additions & 51 deletions freedata_server/api/freedata.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends

import command_norm
import command_message_send
import adif_udp_logger
import wavelog_api_logger
from context import AppContext, get_ctx
import asyncio

from freedata_server.api.common import api_response, api_abort
Expand All @@ -17,6 +12,7 @@
from freedata_server import command_message_send
from freedata_server import adif_udp_logger
from freedata_server import wavelog_api_logger
from freedata_server import command_norm
from freedata_server.context import AppContext, get_ctx
from freedata_server.norm.norm_transmission_iss import NormTransmissionISS

Expand All @@ -39,6 +35,7 @@ def _mgr_beacon(ctx: AppContext):
def _mgr_stations(ctx: AppContext):
return DatabaseManagerStations(ctx)


def _mgr_broadcasts(ctx: AppContext):
return DatabaseManagerBroadcasts(ctx)

Expand Down Expand Up @@ -66,11 +63,7 @@ async def get_freedata_message(message_id: str, ctx: AppContext = Depends(get_ct
responses={
200: {
"description": "Message transmitted successfully.",
"content": {
"application/json": {
"example": {"destination": "XX1XXX-6", "body": "Hello FreeDATA"}
}
},
"content": {"application/json": {"example": {"destination": "XX1XXX-6", "body": "Hello FreeDATA"}}},
},
404: {
"description": "The requested resource was not found.",
Expand Down Expand Up @@ -136,9 +129,7 @@ async def post_freedata_message_adif_log(message_id: str, ctx: AppContext = Depe
},
},
)
async def patch_freedata_message(
message_id: str, payload: dict, ctx: AppContext = Depends(get_ctx)
):
async def patch_freedata_message(message_id: str, payload: dict, ctx: AppContext = Depends(get_ctx)):
if payload.get("action") == "retransmit":
_mgr_msgs(ctx).update_message(message_id, {"status": "queued"})
_mgr_msgs(ctx).increment_message_attempts(message_id)
Expand Down Expand Up @@ -224,11 +215,7 @@ async def get_freedata_messages(ctx: AppContext = Depends(get_ctx)):
},
404: {
"description": "Message not found.",
"content": {
"application/json": {
"example": {"message": "Message not found", "status": "failure"}
}
},
"content": {"application/json": {"example": {"message": "Message not found", "status": "failure"}}},
},
},
)
Expand Down Expand Up @@ -543,63 +530,45 @@ async def set_station_info(callsign: str, payload: dict, ctx: AppContext = Depen


@router.get("/broadcasts", summary="Get All Broadcast Messages", tags=["FreeDATA"], responses={})
async def get_freedata_broadcasts(
ctx: AppContext = Depends(get_ctx)
):
#filters = {k: v for k, v in ctx.config_manager.read().get('FILTERS', {}).items()}
async def get_freedata_broadcasts(ctx: AppContext = Depends(get_ctx)):
# filters = {k: v for k, v in ctx.config_manager.read().get('FILTERS', {}).items()}
# use query params if needed
# filters = dict(ctx.request.query_params)
result = _mgr_broadcasts(ctx).get_all_broadcasts_json()
return api_response(result)


@router.get("/broadcasts/{domain}/", summary="Get Broadcats per Domain", tags=["FreeDATA"], responses={})
async def get_freedata_broadcasts_per_domain(
domain: str,
ctx: AppContext = Depends(get_ctx)
):
async def get_freedata_broadcasts_per_domain(domain: str, ctx: AppContext = Depends(get_ctx)):
result = _mgr_broadcasts(ctx).get_broadcasts_per_domain_json(domain)
return api_response(result)


@router.get("/broadcasts/domains", summary="Get All Broadcast Messages", tags=["FreeDATA"], responses={})
async def get_freedata_broadcasts(
ctx: AppContext = Depends(get_ctx)
):
#filters = {k: v for k, v in ctx.config_manager.read().get('FILTERS', {}).items()}
async def get_freedata_broadcasts(ctx: AppContext = Depends(get_ctx)): # noqa: F811 # same as line 534, but different path
# filters = {k: v for k, v in ctx.config_manager.read().get('FILTERS', {}).items()}
# use query params if needed
# filters = dict(ctx.request.query_params)
result = _mgr_broadcasts(ctx).get_broadcast_domains_json()
return api_response(result)


@router.delete("/broadcasts/{id}", summary="Delete Message or Broadcast by ID", tags=["FreeDATA"], responses={})
async def delete_freedata_broadcast_domain(
id: str,
ctx: AppContext = Depends(get_ctx)
):
async def delete_freedata_broadcast_domain(id: str, ctx: AppContext = Depends(get_ctx)):
ok = _mgr_broadcasts(ctx).delete_broadcast_message_or_domain(id)
if not ok:
api_abort("Message not found", 404)
return api_response({"message": f"{id} deleted", "status": "success"})



@router.patch("/broadcasts/{id}", summary="Retransmit Broadcast by ID", tags=["FreeDATA"], responses={})
async def patch_freedata_broadcast_domain(
id: str,
payload: dict,
ctx: AppContext = Depends(get_ctx)
):
async def patch_freedata_broadcast_domain(id: str, payload: dict, ctx: AppContext = Depends(get_ctx)):
if payload.get("action") == "retransmit":
_mgr_broadcasts(ctx).increment_attempts(id)
msg = _mgr_broadcasts(ctx).get_broadcast_per_id(id, get_object=True)
if msg:
loop = asyncio.get_running_loop()
loop.run_in_executor(
None,
NormTransmissionISS(ctx).retransmit_data,
msg
)
loop.run_in_executor(None, NormTransmissionISS(ctx).retransmit_data, msg)
return api_response({"message_id": id, "status": "retransmit started"})
else:
api_abort("Message not found", 404)
Expand All @@ -608,9 +577,6 @@ async def patch_freedata_broadcast_domain(


@router.post("/broadcasts", summary="Transmit Broadcast", tags=["FreeDATA"], responses={})
async def post_freedata_broadcast(
payload: dict,
ctx: AppContext = Depends(get_ctx)
):
async def post_freedata_broadcast(payload: dict, ctx: AppContext = Depends(get_ctx)):
await enqueue_tx_command(ctx, command_norm.Norm, payload)
return api_response({"message": f"broadcast transmitted", "status": "success"})
return api_response({"message": "broadcast transmitted", "status": "success"})
4 changes: 1 addition & 3 deletions freedata_server/api/modem.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,7 @@ async def post_cq(ctx: AppContext = Depends(get_ctx)):
"description": "Invalid input parameters.",
"content": {
"application/json": {
"example": {
"error": "Incorrect value for 'enabled' or 'away_from_key'. Should be bool."
}
"example": {"error": "Incorrect value for 'enabled' or 'away_from_key'. Should be bool."}
}
},
},
Expand Down
24 changes: 7 additions & 17 deletions freedata_server/api/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ async def websocket_events(websocket: WebSocket, ctx: AppContext = Depends(get_c
WebSocket endpoint for event streams.
"""
await websocket.accept()
await ctx.websocket_manager.handle_connection(
websocket, ctx.websocket_manager.events_client_list, ctx.modem_events
)
await ctx.websocket_manager.handle_connection(websocket, ctx.websocket_manager.events_client_list, ctx.modem_events)


@router.websocket("/fft")
Expand All @@ -21,9 +19,7 @@ async def websocket_fft(websocket: WebSocket, ctx: AppContext = Depends(get_ctx)
WebSocket endpoint for FFT data streams.
"""
await websocket.accept()
await ctx.websocket_manager.handle_connection(
websocket, ctx.websocket_manager.fft_client_list, ctx.modem_fft
)
await ctx.websocket_manager.handle_connection(websocket, ctx.websocket_manager.fft_client_list, ctx.modem_fft)


@router.websocket("/states")
Expand All @@ -32,23 +28,17 @@ async def websocket_states(websocket: WebSocket, ctx: AppContext = Depends(get_c
WebSocket endpoint for state updates.
"""
await websocket.accept()
await ctx.websocket_manager.handle_connection(
websocket, ctx.websocket_manager.states_client_list, ctx.state_queue
)
await ctx.websocket_manager.handle_connection(websocket, ctx.websocket_manager.states_client_list, ctx.state_queue)


@router.websocket("/audio_rx")
async def websocket_audio_rx(
websocket: WebSocket,
ctx: AppContext = Depends(get_ctx)
):
async def websocket_audio_rx(websocket: WebSocket, ctx: AppContext = Depends(get_ctx)):
"""
WebSocket endpoint for state updates.
"""
await websocket.accept()
await ctx.websocket_manager.handle_connection(
websocket,
ctx.websocket_manager.audio_rx_client_list,
ctx.state_queue
websocket, ctx.websocket_manager.audio_rx_client_list, ctx.state_queue
)
#while True:
# while True:
# await websocket.send_bytes(b"\x00" * 1024)
46 changes: 11 additions & 35 deletions freedata_server/arq_data_type_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ def dispatch(self, type_byte: int, data: bytearray, statistics: dict):

self.ctx.state_manager.setARQ(False)

if (
session_type
and session_type in self.handlers
and "handle" in self.handlers[session_type]
):
if session_type and session_type in self.handlers and "handle" in self.handlers[session_type]:
return self.handlers[session_type]["handle"](data, statistics)
else:
self.log(f"Unknown handling endpoint for type: {type_byte}", isWarning=True)
Expand Down Expand Up @@ -271,9 +267,7 @@ def prepare_raw_lzma(self, data):
The LZMA-compressed data as a bytearray.
"""
compressed_data = lzma.compress(data)
self.log(
f"Preparing LZMA compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes"
)
self.log(f"Preparing LZMA compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
return compressed_data

def handle_raw_lzma(self, data, statistics):
Expand All @@ -290,9 +284,7 @@ def handle_raw_lzma(self, data, statistics):
The decompressed data as a bytearray.
"""
decompressed_data = lzma.decompress(data)
self.log(
f"Handling LZMA compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes"
)
self.log(f"Handling LZMA compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
return decompressed_data

def failed_raw_lzma(self, data, statistics):
Expand Down Expand Up @@ -338,9 +330,7 @@ def prepare_raw_gzip(self, data):
The GZIP-compressed data as a bytearray.
"""
compressed_data = gzip.compress(data)
self.log(
f"Preparing GZIP compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes"
)
self.log(f"Preparing GZIP compressed data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
return compressed_data

def handle_raw_gzip(self, data, statistics):
Expand All @@ -357,9 +347,7 @@ def handle_raw_gzip(self, data, statistics):
The decompressed data as a bytearray.
"""
decompressed_data = gzip.decompress(data)
self.log(
f"Handling GZIP compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes"
)
self.log(f"Handling GZIP compressed data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
return decompressed_data

def failed_raw_gzip(self, data, statistics):
Expand Down Expand Up @@ -408,9 +396,7 @@ def prepare_p2pmsg_zlib(self, data):
compressor = zlib.compressobj(level=6, wbits=-zlib.MAX_WBITS, strategy=zlib.Z_FILTERED)
compressed_data = compressor.compress(data) + compressor.flush()

self.log(
f"Preparing ZLIB compressed P2PMSG data: {len(data)} Bytes >>> {len(compressed_data)} Bytes"
)
self.log(f"Preparing ZLIB compressed P2PMSG data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
return compressed_data

def handle_p2pmsg_zlib(self, data, statistics):
Expand All @@ -431,9 +417,7 @@ def handle_p2pmsg_zlib(self, data, statistics):
decompressed_data = decompressor.decompress(data)
decompressed_data += decompressor.flush()

self.log(
f"Handling ZLIB compressed P2PMSG data: {len(decompressed_data)} Bytes from {len(data)} Bytes"
)
self.log(f"Handling ZLIB compressed P2PMSG data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
message_received(self.ctx, decompressed_data, statistics)
return decompressed_data

Expand Down Expand Up @@ -500,9 +484,7 @@ def prepare_p2p_connection(self, data):
compressor = zlib.compressobj(level=6, wbits=-zlib.MAX_WBITS, strategy=zlib.Z_FILTERED)
compressed_data = compressor.compress(data) + compressor.flush()

self.log(
f"Preparing zlib compressed P2P_CONNECTION data: {len(data)} Bytes >>> {len(compressed_data)} Bytes"
)
self.log(f"Preparing zlib compressed P2P_CONNECTION data: {len(data)} Bytes >>> {len(compressed_data)} Bytes")
print(self.ctx.state_manager.p2p_connection_sessions)
return compressed_data

Expand All @@ -511,13 +493,9 @@ def handle_p2p_connection(self, data, statistics):
decompressed_data = decompressor.decompress(data)
decompressed_data += decompressor.flush()

self.log(
f"Handling gzip compressed P2P_CONNECTION data: {len(decompressed_data)} Bytes from {len(data)} Bytes"
)
self.log(f"Handling gzip compressed P2P_CONNECTION data: {len(decompressed_data)} Bytes from {len(data)} Bytes")
for session_id in self.ctx.state_manager.p2p_connection_sessions:
self.ctx.state_manager.p2p_connection_sessions[session_id].received_arq(
decompressed_data
)
self.ctx.state_manager.p2p_connection_sessions[session_id].received_arq(decompressed_data)

def failed_p2p_connection(self, data, statistics):
decompressor = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
Expand All @@ -535,6 +513,4 @@ def transmitted_p2p_connection(self, data, statistics):
decompressed_data = decompressor.decompress(data)
decompressed_data += decompressor.flush()
for session_id in self.ctx.state_manager.p2p_connection_sessions:
self.ctx.state_manager.p2p_connection_sessions[session_id].transmitted_arq(
decompressed_data
)
self.ctx.state_manager.p2p_connection_sessions[session_id].transmitted_arq(decompressed_data)
9 changes: 2 additions & 7 deletions freedata_server/arq_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ def __init__(self, ctx, dxcall: str):
# we will use the schedule manager, for checking, how old is the state change for deciding, how we continue with the message
self.last_state_change_timestamp = time.time()


# histogram lists for storing statistics
self.snr_histogram = []
self.bpm_histogram = []
Expand Down Expand Up @@ -219,9 +218,7 @@ def on_frame_received(self, frame):
)
return

self.log(
f"Ignoring unknown transition from state {self.state.name} with frame {frame['frame_type']}"
)
self.log(f"Ignoring unknown transition from state {self.state.name} with frame {frame['frame_type']}")

def is_session_outdated(self):
"""Checks if the session is outdated.
Expand Down Expand Up @@ -365,9 +362,7 @@ def get_appropriate_speed_level(self, snr, maximum_bandwidth=None):

# Adjust maximum_bandwidth if set to 0 (use maximum available bandwidth from speed levels)
if maximum_bandwidth == 0:
maximum_bandwidth = max(
details["bandwidth"] for details in self.SPEED_LEVEL_DICT.values()
)
maximum_bandwidth = max(details["bandwidth"] for details in self.SPEED_LEVEL_DICT.values())

# Iterate through speed levels in reverse order to find the highest appropriate one
for level in sorted(self.SPEED_LEVEL_DICT.keys(), reverse=True):
Expand Down
Loading