Skip to content
Open
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
2 changes: 2 additions & 0 deletions QLog.pro
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ SOURCES += \
awards/AwardWPX.cpp \
awards/AwardWWFF.cpp \
awards/BandTableAward.cpp \
core/AdifUDPReceiver.cpp \
core/AlertEvaluator.cpp \
core/AdifRecovery.cpp \
core/AppGuard.cpp \
Expand Down Expand Up @@ -263,6 +264,7 @@ HEADERS += \
awards/AwardWPX.h \
awards/AwardWWFF.h \
awards/BandTableAward.h \
core/AdifUDPReceiver.h \
core/AlertEvaluator.h \
core/AdifRecovery.h \
core/AppGuard.h \
Expand Down
219 changes: 219 additions & 0 deletions core/AdifUDPReceiver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostAddress>
#include <QSqlTableModel>
#include <QTextStream>

#include "AdifUDPReceiver.h"
#include "core/LogParam.h"
#include "core/debug.h"
#include "logformat/AdiFormat.h"

MODULE_IDENTIFICATION("qlog.core.adifudp");

AdifUDPReceiver::AdifUDPReceiver(QObject *parent) :
QObject(parent),
socket(new QUdpSocket(this))
{
FCT_IDENTIFICATION;

connect(socket, &QUdpSocket::readyRead, this, &AdifUDPReceiver::readPendingDatagrams);
reloadSetting();
}

quint16 AdifUDPReceiver::getConfigPort()
{
FCT_IDENTIFICATION;

const quint16 ret = LogParam::getNetworkAdifListenerPort(DEFAULT_PORT);

qCDebug(runtime) << "ADIF UDP configured port" << ret;

return ret;
}

void AdifUDPReceiver::saveConfigPort(quint16 port)
{
FCT_IDENTIFICATION;

qCDebug(function_parameters) << port;

LogParam::setNetworkAdifListenerPort(port);
}

bool AdifUDPReceiver::getConfigEnabled()
{
FCT_IDENTIFICATION;

const bool ret = LogParam::getNetworkAdifListenerEnabled();

qCDebug(runtime) << "ADIF UDP listener enabled" << ret;

return ret;
}

void AdifUDPReceiver::saveConfigEnabled(bool enabled)
{
FCT_IDENTIFICATION;

qCDebug(function_parameters) << enabled;

LogParam::setNetworkAdifListenerEnabled(enabled);
}

void AdifUDPReceiver::reloadSetting()
{
FCT_IDENTIFICATION;

const bool enabled = getConfigEnabled();

qCDebug(runtime) << "Reloading ADIF UDP listener settings; enabled" << enabled;

if ( enabled )
openPort();
else
closePort();
}

void AdifUDPReceiver::openPort()
{
FCT_IDENTIFICATION;

if ( !socket )
{
qWarning() << "Cannot open ADIF UDP port - socket is not initialized";
return;
}

if ( socket->state() == QAbstractSocket::BoundState )
{
qCDebug(runtime) << "Closing previously bound ADIF UDP port";
socket->close();
}

const quint16 newPort = getConfigPort();

qCDebug(runtime) << "ADIF UDP listen port" << newPort;

if ( !socket->bind(QHostAddress::Any, newPort) )
{
qWarning() << "Cannot bind the Port for ADIF UDP";
return;
}

qCDebug(runtime) << "ADIF UDP listening on all interfaces";
}

void AdifUDPReceiver::closePort()
{
FCT_IDENTIFICATION;

if ( socket && socket->state() == QAbstractSocket::BoundState )
{
qCDebug(runtime) << "Closing ADIF UDP listener";
socket->close();
}
else
{
qCDebug(runtime) << "ADIF UDP listener is already closed";
}
}

void AdifUDPReceiver::readPendingDatagrams()
{
FCT_IDENTIFICATION;

while ( socket->hasPendingDatagrams() )
{
const QNetworkDatagram datagram = socket->receiveDatagram();
qCDebug(runtime) << "Received ADIF UDP datagram from" << datagram.senderAddress()
<< datagram.senderPort()
<< "bytes" << datagram.data().size();
processDatagram(datagram.data());
}
}

void AdifUDPReceiver::processDatagram(const QByteArray &data)
{
FCT_IDENTIFICATION;

qCDebug(function_parameters) << "bytes" << data.size();

QString adif = QString::fromUtf8(data);
if ( adif.isEmpty() )
{
qCDebug(runtime) << "ADIF UDP datagram is not valid UTF-8, trying Latin-1";
adif = QString::fromLatin1(data);
}

const int start = adif.indexOf(QLatin1Char('<'));
if ( start < 0 )
{
qWarning() << "Ignoring ADIF UDP datagram without ADIF tags";
return;
}

if ( start > 0 )
qCDebug(runtime) << "Skipping ADIF UDP datagram prefix bytes" << start;

emitRecords(adif.mid(start));
}

void AdifUDPReceiver::emitRecords(const QString &adif)
{
FCT_IDENTIFICATION;

qCDebug(function_parameters) << "characters" << adif.size();

QString input(adif);
QTextStream in(&input);
AdiFormat adi(in);

QSqlTableModel model;
model.setTable("contacts");
model.removeColumn(model.fieldIndex("id"));

bool imported = false;
int recordCount = 0;
while ( true )
{
QSqlRecord record = model.record(0);
if ( !adi.importNext(record) )
break;

if ( record.value("callsign").toString().isEmpty() )
{
qWarning() << "Ignoring ADIF UDP record without callsign" << record;
continue;
}

const int qslSentIndex = record.indexOf("qsl_sent");
if ( qslSentIndex >= 0 )
record.remove(qslSentIndex);

const int lotwQslSentIndex = record.indexOf("lotw_qsl_sent");
if ( lotwQslSentIndex >= 0 )
record.remove(lotwQslSentIndex);

const int eqslQslSentIndex = record.indexOf("eqsl_qsl_sent");
if ( eqslQslSentIndex >= 0 )
record.remove(eqslQslSentIndex);

qCDebug(runtime) << "Emitting ADIF UDP contact"
<< record.value("callsign").toString()
<< record.value("start_time")
<< record.value("freq")
<< record.value("band")
<< record.value("mode");
qCDebug(function_parameters) << record;

emit addContact(record);
imported = true;
recordCount++;
}

if ( !imported )
qWarning() << "ADIF UDP datagram did not contain an importable QSO record";
else
qCDebug(runtime) << "Imported ADIF UDP records" << recordCount;
}
40 changes: 40 additions & 0 deletions core/AdifUDPReceiver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef QLOG_CORE_ADIFUDPRECEIVER_H
#define QLOG_CORE_ADIFUDPRECEIVER_H

#include <QObject>
#include <QSqlRecord>

class QUdpSocket;

class AdifUDPReceiver : public QObject
{
Q_OBJECT
public:
explicit AdifUDPReceiver(QObject *parent = nullptr);

static quint16 getConfigPort();
static void saveConfigPort(quint16 port);
static bool getConfigEnabled();
static void saveConfigEnabled(bool enabled);

signals:
void addContact(QSqlRecord record);

public slots:
void reloadSetting();

private slots:
void readPendingDatagrams();

private:
void openPort();
void closePort();
void processDatagram(const QByteArray &data);
void emitRecords(const QString &adif);

QUdpSocket *socket;

static const int DEFAULT_PORT = 2333;
};

#endif // QLOG_CORE_ADIFUDPRECEIVER_H
20 changes: 20 additions & 0 deletions core/LogParam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,26 @@ void LogParam::setNetworkWsjtxListenerMulticastTTL(int ttl)
setParam("network/listener/wsjtx/multicast/ttl", ttl);
}

bool LogParam::getNetworkAdifListenerEnabled()
{
return getParam("network/listener/adif/enabled", false).toBool();
}

void LogParam::setNetworkAdifListenerEnabled(bool state)
{
setParam("network/listener/adif/enabled", state);
}

int LogParam::getNetworkAdifListenerPort(int defaultPort)
{
return getParam("network/listener/adif/port", defaultPort).toInt();
}

void LogParam::setNetworkAdifListenerPort(int port)
{
setParam("network/listener/adif/port", port);
}

QStringList LogParam::getEnabledMemberlists()
{
return getParamStringList("memberlist/enabledlists");
Expand Down
4 changes: 4 additions & 0 deletions core/LogParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ class LogParam : public QObject
static void setNetworkWsjtxListenerMulticastAddr(const QString &addr);
static int getNetworkWsjtxListenerMulticastTTL();
static void setNetworkWsjtxListenerMulticastTTL(int ttl);
static bool getNetworkAdifListenerEnabled();
static void setNetworkAdifListenerEnabled(bool state);
static int getNetworkAdifListenerPort(int defaultPort);
static void setNetworkAdifListenerPort(int port);

/********************
* Club Member Lists
Expand Down
11 changes: 10 additions & 1 deletion ui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rotator/Rotator.h"
#include "cwkey/CWKeyer.h"
#include "core/WsjtxUDPReceiver.h"
#include "core/AdifUDPReceiver.h"
#include "core/debug.h"
#include "ui/NewContactWidget.h"
#include "ui/QSOFilterDialog.h"
Expand Down Expand Up @@ -69,7 +70,9 @@ MainWindow::MainWindow(QWidget* parent) :
ui(new Ui::MainWindow),
stats(new StatisticsWidget),
clublogRT(new ClubLogUploader(this)),
adifRecoveryManager(new AdifRecoveryManager(this))
adifRecoveryManager(new AdifRecoveryManager(this)),
wsjtx(nullptr),
adifUDP(nullptr)
{
FCT_IDENTIFICATION;

Expand Down Expand Up @@ -299,6 +302,9 @@ MainWindow::MainWindow(QWidget* parent) :
FldigiTCPServer* fldigi = new FldigiTCPServer(this);
connect(fldigi, &FldigiTCPServer::addContact, ui->newContactWidget, &NewContactWidget::saveExternalContact);

adifUDP = new AdifUDPReceiver(this);
connect(adifUDP, &AdifUDPReceiver::addContact, ui->newContactWidget, &NewContactWidget::saveExternalContact);

connect(adifRecoveryManager, &AdifRecoveryManager::contactsRecovered, ui->logbookWidget, &LogbookWidget::updateTable);
connect(adifRecoveryManager, &AdifRecoveryManager::problem, this, [this](const QString &message)
{
Expand All @@ -325,6 +331,7 @@ MainWindow::MainWindow(QWidget* parent) :
connect(ui->wsjtxWidget, &WsjtxWidget::modeChanged, ui->newContactWidget, &NewContactWidget::changeModefromRig);

connect(this, &MainWindow::settingsChanged, wsjtx, &WsjtxUDPReceiver::reloadSetting);
connect(this, &MainWindow::settingsChanged, adifUDP, &AdifUDPReceiver::reloadSetting);
connect(this, &MainWindow::settingsChanged, adifRecoveryManager, &AdifRecoveryManager::reloadSettings);
connect(this, &MainWindow::settingsChanged, ui->rotatorWidget, &RotatorWidget::reloadSettings);
connect(this, &MainWindow::settingsChanged, ui->rigWidget, &RigWidget::reloadSettings);
Expand Down Expand Up @@ -2251,6 +2258,8 @@ MainWindow::~MainWindow()
clublogRT->deleteLater();
if ( wsjtx )
wsjtx->deleteLater();
if ( adifUDP )
adifUDP->deleteLater();

seqGroup->deleteLater();
dupeGroup->deleteLater();
Expand Down
2 changes: 2 additions & 0 deletions ui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class MainWindow;

class QLabel;
class WsjtxUDPReceiver;
class AdifUDPReceiver;
class AdifRecoveryManager;

class MainWindow : public QMainWindow {
Expand Down Expand Up @@ -121,6 +122,7 @@ private slots:
ClubLogUploader* clublogRT;
AdifRecoveryManager* adifRecoveryManager;
WsjtxUDPReceiver* wsjtx;
AdifUDPReceiver* adifUDP;
QActionGroup *seqGroup;
QActionGroup *dupeGroup;
QActionGroup *linkExchangeGroup;
Expand Down
Loading