Merge branch 'develop' into dev-connected-mode

pull/865/head
DJ2LS 2025-03-18 08:27:18 +01:00 committed by GitHub
commit 7a885d65e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 114 additions and 39 deletions

View File

@ -153,6 +153,36 @@ export function eventDispatcher(data) {
loadAllData();
return;
case "message-logging":
if (data.endpoint === "wavelog" || data.endpoint === "udp") {
if (data.status === true) {
const message = `
<div>
<strong>${i18next.t('popups.adiflogheader')}:</strong>
<span class="badge bg-success">${i18next.t('popups.adiflogtext1')}${data.message}${i18next.t('popups.adiflogtext2')}</span>
<div class="mt-2">
<span class="badge bg-secondary">${data.endpoint} ${i18next.t('popups.adiflogheader')}</span>
</div>
</div>
`;
displayToast("success", "bi-check-circle", message, 5000);
} else {
const message = `
<div>
<strong>${i18next.t('popups.adiflogheader')}:</strong>
<span class="badge bg-danger">${data.message}</span>
<div class="mt-2">
<span class="badge bg-secondary">${data.endpoint} ${i18next.t('popups.adiflogerror')}</span>
</div>
</div>
`;
displayToast("warning", "bi-exclamation-circle", message, 5000);
}
}
return;
case "frame-handler":
switch (data.received) {
case "CQ":

View File

@ -33,7 +33,11 @@
"bytesperminute": "Bytes per minute",
"transmissionfailedwith": "Transmission FAILED with",
"confirmingtransmissionwith": "Confirming transmission with",
"receivedbytes": "Received Bytes"
"receivedbytes": "Received Bytes",
"adiflogheader": "ADIF",
"adiflogtext1": "QSO with",
"adiflogtext2": "added to the log.",
"adiflogerror": "ADIF export error"
},
"navbar": {
"frequency_help": "What's the frequency, Kenneth?",

View File

@ -33,7 +33,11 @@
"bytesperminute": "Byte per minutt",
"transmissionfailedwith": "Overføring MISLYKKET med",
"confirmingtransmissionwith": "Bekrefter overføring med",
"receivedbytes": "Mottatte bytes"
"receivedbytes": "Mottatte bytes",
"adiflogheader": "ADIF",
"adiflogtext1": "QSO med",
"adiflogtext2": "lagt til i loggen.",
"adiflogerror": "ADIF export feilet"
},
"navbar": {
"frequency_help": "Hva er frekvensen, Kenneth?",

View File

@ -1,42 +1,50 @@
"""
This module provides a utility function to send ADIF-formatted QSO data over UDP.
It reads configuration settings from a provided dictionary to determine if ADIF UDP logging is enabled,
and if so, retrieves the destination host and port. Using a UDP socket, it encodes the ADIF data as UTF-8 and
sends it to the specified server.
"""
import socket
import structlog
import threading
def send_adif_qso_data(config, adif_data):
def send_adif_qso_data(config, event_manager, adif_data):
"""
Sends ADIF QSO data to the specified server via UDP.
Sends ADIF QSO data to the specified server via UDP in a non-blocking manner.
Parameters:
server_ip (str): IP address of the server.
server_port (int): Port of the server.
config (dict): Configuration settings.
event_manager: An event manager to log success/failure.
adif_data (str): ADIF-formatted QSO data.
"""
log = structlog.get_logger()
# If False then exit the function
# Check if ADIF UDP logging is enabled
adif = config['QSO_LOGGING'].get('enable_adif_udp', 'False')
if not adif:
return # exit as we don't want to log ADIF UDP
return # Exit if ADIF UDP logging is disabled
adif_log_host = config['QSO_LOGGING'].get('adif_udp_host', '127.0.0.1')
adif_log_port = int(config['QSO_LOGGING'].get('adif_udp_port', '2237'))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
def send_thread():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Set a timeout of 3 seconds to avoid blocking indefinitely
sock.settimeout(3)
# Send the ADIF data to the server
sock.sendto(adif_data.encode('utf-8'), (adif_log_host, adif_log_port))
log.info(f"[CHAT] ADIF QSO data sent to: {adif_log_host}:{adif_log_port} {adif_data.encode('utf-8')}")
except Exception as e:
log.info(f"[CHAT] Error sending ADIF data: {e}")
finally:
sock.close()
callsign_start = adif_data.find(f">") + 1
callsign_end = adif_data.find(f"<QSO_DATE", callsign_start)
call_value = adif_data[callsign_start:callsign_end]
try:
sock.sendto(adif_data.encode('utf-8'), (adif_log_host, adif_log_port))
log.info(f"[CHAT] ADIF QSO data sent to: {adif_log_host}:{adif_log_port}")
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}")
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}")
event_manager.freedata_logging(type="udp", status=True, message=f" {call_value} ")
finally:
sock.close()
# Run the sending function in a separate thread
thread = threading.Thread(target=send_thread, daemon=True)
thread.start()

View File

@ -85,9 +85,9 @@ async def post_freedata_message_adif_log(message_id: str, request:Request):
if not adif_output:
return
# Send the ADIF data via UDP
adif_udp_logger.send_adif_qso_data(request.app.config_manager.read(), adif_output)
wavelog_api_logger.send_wavelog_qso_data(request.app.config_manager.read(), adif_output)
# Send the ADIF data via UDP and/or wavelog
adif_udp_logger.send_adif_qso_data(request.app.config_manager.read(), request.app.event_manager, adif_output)
wavelog_api_logger.send_wavelog_qso_data(request.app.config_manager.read(), request.app.event_manager, adif_output)
return api_response(adif_output)
@router.patch("/messages/{message_id}", summary="Update Message by ID", tags=["FreeDATA"], responses={

View File

@ -238,4 +238,20 @@ class EventManager:
Args:
message_id (any, optional): The ID of the changed message. Defaults to None.
"""
self.broadcast({"message-db": "changed", "message_id": message_id})
self.broadcast({"message-db": "changed", "message_id": message_id})
def freedata_logging(self, type, status, message):
"""Broadcasts a FreeDATA logging event.
This method broadcasts an event related to FreeDATA logging,
indicating the type of logging endpoint and its status. It is
used to inform other parts of the application about logging
activities.
Args:
type (str): The type of logging endpoint (e.g., "file", "websocket").
status (any): The status of the logging operation.
message (str): The message to be displayed
"""
self.broadcast({"type": "message-logging", "endpoint": type, "status": status, "message": message})

View File

@ -1,8 +1,9 @@
import requests
import re
import threading
import structlog
def send_wavelog_qso_data(config, wavelog_data):
def send_wavelog_qso_data(config, event_manager, wavelog_data):
"""
Sends wavelog QSO data to the specified server via API call.
@ -41,9 +42,21 @@ def send_wavelog_qso_data(config, wavelog_data):
"string": wavelog_data
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status() # Raise an error for bad status codes
log.info(f"[CHAT] Wavelog API: {wavelog_data}")
except requests.exceptions.RequestException as e:
log.warning(f"[WAVELOG ADIF API EXCEPTION]: {e}")
def send_api():
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status() # Raise an error for bad status codes
log.info(f"[CHAT] Wavelog API: {wavelog_data}")
callsign_start = wavelog_data.find(f">") + 1
callsign_end = wavelog_data.find(f"<QSO_DATE", callsign_start)
call_value = wavelog_data[callsign_start:callsign_end]
event_manager.freedata_logging(type="wavelog", status=True, message=f" {call_value} ")
except Exception as e:
event_manager.freedata_logging(type="wavelog", status=False, message=f"{e}")
# Run the API call in a background thread to avoid blocking the main thread
thread = threading.Thread(target=send_api, daemon=True)
thread.start()