Added n3fjp mapping of custom fields

This commit is contained in:
Jordan Sherer 2019-06-05 11:33:21 -04:00
parent 22432c7fea
commit 72bf58ad4c
11 changed files with 182 additions and 51 deletions

View File

@ -58,24 +58,21 @@ TCPClient::TCPClient(QObject *parent) : QObject(parent)
bool TCPClient::ensureConnected(QString host, port_type port, int msecs){ bool TCPClient::ensureConnected(QString host, port_type port, int msecs){
if(!m_->isConnected(host, port)){ if(!m_->isConnected(host, port)){
//qDebug() << "connecting to" << host << port;
m_->connectToHost(host, port); m_->connectToHost(host, port);
} }
return m_->waitForConnected(msecs); return m_->waitForConnected(msecs);
} }
bool TCPClient::sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf){ bool TCPClient::sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf, int msecs){
if(!ensureConnected(host, port)){ if(!ensureConnected(host, port, msecs)){
return false; return false;
} }
//qDebug() << "connecting to" << host << port;
qint64 n = m_->send(message, crlf); qint64 n = m_->send(message, crlf);
if(n <= 0){ if(n <= 0){
return false; return false;
} }
//qDebug() << "sent" << n << "bytes" << message;
return m_->flush(); return m_->flush();
} }

View File

@ -18,7 +18,7 @@ signals:
public slots: public slots:
Q_SLOT bool ensureConnected(QString host, port_type port, int msecs = 5000); Q_SLOT bool ensureConnected(QString host, port_type port, int msecs = 5000);
Q_SLOT bool sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf=true); Q_SLOT bool sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf=true, int msecs=5000);
private: private:
class impl; class impl;

View File

@ -117,7 +117,8 @@ HEADERS += qt_helpers.hpp \
Inbox.h \ Inbox.h \
messagewindow.h \ messagewindow.h \
SpotClient.h \ SpotClient.h \
TCPClient.h TCPClient.h \
logbook/n3fjp.h
INCLUDEPATH += qmake_only INCLUDEPATH += qmake_only

View File

@ -333,7 +333,7 @@ QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QStri
, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn , QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn
, QDateTime const& dateTimeOff, QString const& band, QString const& comments , QDateTime const& dateTimeOff, QString const& band, QString const& comments
, QString const& name, QString const& strDialFreq, QString const& m_myCall , QString const& name, QString const& strDialFreq, QString const& m_myCall
, QString const& m_myGrid, QString const& operator_call, QMap<QString, QString> const &additionalFields) , QString const& m_myGrid, QString const& operator_call, QMap<QString, QVariant> const &additionalFields)
{ {
QString t; QString t;
t = "<call:" + QString::number(hisCall.length()) + ">" + hisCall; t = "<call:" + QString::number(hisCall.length()) + ">" + hisCall;
@ -365,7 +365,7 @@ QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QStri
">" + operator_call; ">" + operator_call;
foreach(auto key, additionalFields.keys()){ foreach(auto key, additionalFields.keys()){
auto value = additionalFields[key]; auto value = additionalFields[key].toString();
t += QString(" <%1:%2>%3").arg(key).arg(value.length()).arg(value); t += QString(" <%1:%2>%3").arg(key).arg(value.length()).arg(value);
} }

View File

@ -13,6 +13,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QMultiHash> #include <QMultiHash>
#include <QVariant>
#else #else
#include <QtGui> #include <QtGui>
#endif #endif
@ -42,7 +43,7 @@ class ADIF
, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff , QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff
, QString const& band, QString const& comments, QString const& name , QString const& band, QString const& comments, QString const& name
, QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid , QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid
, QString const& operator_call, const QMap<QString, QString> &additionalFields); , QString const& operator_call, const QMap<QString, QVariant> &additionalFields);
struct QSO struct QSO

View File

@ -13,6 +13,7 @@
#include "countrydat.h" #include "countrydat.h"
#include "countriesworked.h" #include "countriesworked.h"
#include "adif.h" #include "adif.h"
#include "n3fjp.h"
class QDir; class QDir;

82
logbook/n3fjp.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef N3FJP_H
#define N3FJP_H
// Mapping between ADIF field to N3FJP field
static const QMap<QString, QString> N3FJP_ADIF_MAP = {
{"AGE", "fldAge"},
//{"", "fldARCI"},
{"BAND", "fldBand"},
{"CALL", "fldCall"},
//{"", "fldCategory"},
{"CHECK", "fldCheck"},
{"CLASS", "fldClass"},
{"COMMENT", "fldComments"},
{"COMMENT_INTL", "fldComments"},
//{"", "fldComputerName"},
{"CONTEST_ID", "fldContestID"},
{"CONT", "fldContinent"},
{"DXCC", "fldCountryDXCC"},
//{"", "fldCountryWorked"},
{"COUNTRY", "fldCountyR"},
{"COUNTRY_INTL", "fldCountyR"},
{"MY_COUNTRY", "fldCountyS"},
{"MY_COUNTRY_INTL", "fldCountyS"},
{"CQZ", "fldCQZone"},
{"QSO_DATE", "fldDateStr"},
{"FISTS", "fldFists"},
{"FISTS_CC", "fldFists"},
{"FREQ", "fldFrequency"},
//{"", "fldFuture1"},
//{"", "fldFuture2"},
{"GRIDSQUARE", "fldGridR"},
{"MY_GRIDSQUARE", "fldGridS"},
//{"", "fldIARUZone"},
//{"", "fldInitials"},
{"IOTA", "fldIOTA"},
{"ITUZ", "fldITUZone"},
//{"", "fldLightHouse"},
{"MODE", "fldMode"},
{"CONTEST_ID", "fldModeContest"},
{"NAME", "fldNameR"},
{"NAME_INTL", "fldNameR"},
{"MY_NAME", "fldNameS"},
{"MY_NAME_INTL", "fldNameS"},
{"OPERATOR", "fldOperator"},
{"*1", "fldOther1"},
{"*2", "fldOther2"},
{"*3", "fldOther3"},
{"*4", "fldOther4"},
{"*5", "fldOther5"},
{"*6", "fldOther6"},
{"*7", "fldOther7"},
{"*8", "fldOther8"},
//{"", "fldPoints"},
{"RX_PWR", "fldPower"},
{"TX_PWR", "fldPower"},
{"PRECEDENCE", "fldPrecedence"},
{"PFX", "fldPrefix"},
{"PROP_MODE", "fldPropMode"},
{"QSL_RCVD_VIA", "fldQSLConfByR"},
{"QSL_SENT_VIA", "fldQSLConfByS"},
{"QSL_RCVD", "fldQSLR"},
{"QSL_SENT", "fldQSLS"},
{"QTH", "fldQTHGroup"},
{"QTH_INTL", "fldQTHGroup"},
{"RST_RCVD", "fldRstR"},
{"RST_SENT", "fldRstS"},
{"SAT_NAME", "fldSatName"},
{"ARRL_SECT", "fldSection"},
{"SRX", "fldSerialNoR"},
{"STX", "fldSerialNoS"},
{"SRX_STRING", "fldSPC"},
{"SRX", "fldSPCNum"},
{"STATE", "fldState"},
{"STATION_CALLSIGN", "fldStation"},
{"TEN_TEN", "fldTenTen"},
{"TIME_OFF", "fldTimeOffStr"},
{"TIME_ON", "fldTimeOnStr"},
//{"", "fldTransmitterID"},
};
#endif // N3FJP_H

View File

@ -92,6 +92,18 @@ void LogQSO::createAdditionalField(QString key, QString value){
m_additionalFieldsControls.append(l); m_additionalFieldsControls.append(l);
} }
QMap<QString, QVariant> LogQSO::collectAdditionalFields(){
QMap<QString, QVariant> additionalFields;
foreach(auto field, m_additionalFieldsControls){
auto key = field->property("fieldKey").toString();
if(key.isEmpty()){
continue;
}
additionalFields[key] = QVariant(field->text());
}
return additionalFields;
}
void LogQSO::resetAdditionalFields(){ void LogQSO::resetAdditionalFields(){
if(m_additionalFieldsControls.isEmpty()){ if(m_additionalFieldsControls.isEmpty()){
return; return;
@ -207,15 +219,6 @@ void LogQSO::accept()
QString hisCall,hisGrid,mode,submode,rptSent,rptRcvd,dateOn,dateOff,timeOn,timeOff,band,operator_call; QString hisCall,hisGrid,mode,submode,rptSent,rptRcvd,dateOn,dateOff,timeOn,timeOff,band,operator_call;
QString comments,name; QString comments,name;
QMap<QString, QString> additionalFields;
foreach(auto field, m_additionalFieldsControls){
auto key = field->property("fieldKey").toString();
if(key.isEmpty()){
continue;
}
additionalFields[key] = field->text();
}
hisCall=ui->call->text().toUpper(); hisCall=ui->call->text().toUpper();
hisGrid=ui->grid->text().toUpper(); hisGrid=ui->grid->text().toUpper();
mode = ui->mode->text().toUpper(); mode = ui->mode->text().toUpper();
@ -239,6 +242,8 @@ void LogQSO::accept()
auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (filename); auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (filename);
adifile.init(adifilePath); adifile.init(adifilePath);
auto additionalFields = collectAdditionalFields();
QByteArray ADIF {adifile.QSOToADIF (hisCall, hisGrid, mode, submode, rptSent, rptRcvd, m_dateTimeOn, m_dateTimeOff, band QByteArray ADIF {adifile.QSOToADIF (hisCall, hisGrid, mode, submode, rptSent, rptRcvd, m_dateTimeOn, m_dateTimeOff, band
, comments, name, strDialFreq, m_myCall, m_myGrid, operator_call, additionalFields)}; , comments, name, strDialFreq, m_myCall, m_myGrid, operator_call, additionalFields)};
@ -248,17 +253,6 @@ void LogQSO::accept()
tr ("Cannot open \"%1\"").arg (adifilePath)); tr ("Cannot open \"%1\"").arg (adifilePath));
} }
// Log to N1MM Logger
if (m_config->broadcast_to_n1mm() && m_config->valid_n1mm_info()) {
const QHostAddress n1mmhost = QHostAddress(m_config->n1mm_server_name());
QUdpSocket _sock;
auto rzult = _sock.writeDatagram (ADIF + " <eor>", n1mmhost, quint16(m_config->n1mm_server_port()));
if (rzult == -1) {
MessageBox::warning_message (this, tr ("Error sending log to N1MM"),
tr ("Write returned \"%1\"").arg (rzult));
}
}
//Log this QSO to file "js8call.log" //Log this QSO to file "js8call.log"
static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("js8call.log")}; static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("js8call.log")};
if(!f.open(QIODevice::Text | QIODevice::Append)) { if(!f.open(QIODevice::Text | QIODevice::Append)) {
@ -266,20 +260,35 @@ void LogQSO::accept()
tr ("Cannot open \"%1\" for append").arg (f.fileName ()), tr ("Cannot open \"%1\" for append").arg (f.fileName ()),
tr ("Error: %1").arg (f.errorString ())); tr ("Error: %1").arg (f.errorString ()));
} else { } else {
QString logEntry=m_dateTimeOn.date().toString("yyyy-MM-dd,") + QStringList logEntryItems = {
m_dateTimeOn.time().toString("hh:mm:ss,") + m_dateTimeOn.date().toString("yyyy-MM-dd"),
m_dateTimeOff.date().toString("yyyy-MM-dd,") + m_dateTimeOn.time().toString("hh:mm:ss"),
m_dateTimeOff.time().toString("hh:mm:ss,") + hisCall + "," + m_dateTimeOff.date().toString("yyyy-MM-dd"),
hisGrid + "," + strDialFreq + "," + (mode == "MFSK" ? "JS8" : mode) + m_dateTimeOff.time().toString("hh:mm:ss"),
"," + rptSent + "," + rptRcvd + hisCall,
"," + comments + "," + name; hisGrid,
strDialFreq,
(mode == "MFSK" ? "JS8" : mode),
rptSent,
rptRcvd,
comments,
name
};
if(!additionalFields.isEmpty()){
foreach(auto value, additionalFields.values()){
logEntryItems.append(value.toString());
}
}
QTextStream out(&f); QTextStream out(&f);
out << logEntry << endl; out << logEntryItems.join(",") << endl;
f.close(); f.close();
} }
//Clean up and finish logging //Clean up and finish logging
Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, submode, rptSent, rptRcvd, comments, name,m_dateTimeOn, operator_call, m_myCall, m_myGrid, ADIF); Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, submode, rptSent, rptRcvd, comments, name,m_dateTimeOn, operator_call, m_myCall, m_myGrid, ADIF, additionalFields);
QDialog::accept(); QDialog::accept();
} }

View File

@ -45,7 +45,7 @@ signals:
, QString const& rpt_sent, QString const& rpt_received , QString const& rpt_sent, QString const& rpt_received
, QString const& comments , QString const& comments
, QString const& name, QDateTime const& QSO_date_on, QString const& operator_call , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call
, QString const& my_call, QString const& my_grid, QByteArray const& ADIF); , QString const& my_call, QString const& my_grid, QByteArray const& ADIF, QMap<QString, QVariant> const &additionalFields);
protected: protected:
void hideEvent (QHideEvent *); void hideEvent (QHideEvent *);
@ -53,6 +53,7 @@ protected:
private slots: private slots:
void createAdditionalField(QString key={}, QString value={}); void createAdditionalField(QString key={}, QString value={});
void resetAdditionalFields(); void resetAdditionalFields();
QMap<QString, QVariant> collectAdditionalFields();
void on_add_new_field_button_pressed(); void on_add_new_field_button_pressed();
void on_start_now_button_pressed(); void on_start_now_button_pressed();
void on_end_now_button_pressed(); void on_end_now_button_pressed();

View File

@ -29,6 +29,8 @@
#include <QActionGroup> #include <QActionGroup>
#include <QSplashScreen> #include <QSplashScreen>
#include <QSound> #include <QSound>
#include <QUdpSocket>
#include <QVariant>
#include "APRSISClient.h" #include "APRSISClient.h"
#include "revision_utils.hpp" #include "revision_utils.hpp"
@ -6624,6 +6626,7 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button
} }
QString comments = ui->textEditRX->textCursor().selectedText(); QString comments = ui->textEditRX->textCursor().selectedText();
m_logDlg->initLogQSO (call.trimmed(), grid.trimmed(), m_modeTx == "FT8" ? "JS8" : m_modeTx, m_rptSent, m_rptRcvd, m_logDlg->initLogQSO (call.trimmed(), grid.trimmed(), m_modeTx == "FT8" ? "JS8" : m_modeTx, m_rptSent, m_rptRcvd,
m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal + ui->TxFreqSpinBox->value(), m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal + ui->TxFreqSpinBox->value(),
m_config.my_callsign(), m_config.my_grid(), m_config.my_callsign(), m_config.my_grid(),
@ -6636,11 +6639,12 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
, QString const& rpt_sent, QString const& rpt_received , QString const& rpt_sent, QString const& rpt_received
, QString const& comments , QString const& comments
, QString const& name, QDateTime const& QSO_date_on, QString const& operator_call , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call
, QString const& my_call, QString const& my_grid, QByteArray const& ADIF) , QString const& my_call, QString const& my_grid, QByteArray const& ADIF, QMap<QString, QVariant> const &additionalFields)
{ {
QString date = QSO_date_on.toString("yyyyMMdd"); QString date = QSO_date_on.toString("yyyyMMdd");
m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date, name, comments); m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date, name, comments);
// Log to JS8Call API
if(m_config.udpEnabled()){ if(m_config.udpEnabled()){
sendNetworkMessage("LOG.QSO", QString(ADIF), { sendNetworkMessage("LOG.QSO", QString(ADIF), {
{"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())}, {"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())},
@ -6656,10 +6660,23 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
{"COMMENTS", QVariant(comments)}, {"COMMENTS", QVariant(comments)},
{"STATION.OP", QVariant(operator_call)}, {"STATION.OP", QVariant(operator_call)},
{"STATION.CALL", QVariant(my_call)}, {"STATION.CALL", QVariant(my_call)},
{"STATION.GRID", QVariant(my_grid)} {"STATION.GRID", QVariant(my_grid)},
{"EXTRA", additionalFields}
}); });
} }
// Log to N1MM Logger
if (m_config.broadcast_to_n1mm() && m_config.valid_n1mm_info()) {
const QHostAddress n1mmhost = QHostAddress(m_config.n1mm_server_name());
QUdpSocket _sock;
auto rzult = _sock.writeDatagram (ADIF + " <eor>", n1mmhost, quint16(m_config.n1mm_server_port()));
if (rzult == -1) {
MessageBox::warning_message (this, tr ("Error sending log to N1MM"),
tr ("Write returned \"%1\"").arg (rzult));
}
}
// Log to N3FJP Logger
if(m_config.broadcast_to_n3fjp() && m_config.valid_n3fjp_info()){ if(m_config.broadcast_to_n3fjp() && m_config.valid_n3fjp_info()){
QString data = QString( QString data = QString(
"<CMD>" "<CMD>"
@ -6678,6 +6695,7 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
"<fldComments>%9</fldComments>" "<fldComments>%9</fldComments>"
"<fldRstS>%10</fldRstS>" "<fldRstS>%10</fldRstS>"
"<fldRstR>%11</fldRstR>" "<fldRstR>%11</fldRstR>"
"%12"
"</CMD>"); "</CMD>");
data = data.arg(QSO_date_on.toString("yyyy/MM/dd")); data = data.arg(QSO_date_on.toString("yyyy/MM/dd"));
@ -6692,16 +6710,37 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
data = data.arg(rpt_sent); data = data.arg(rpt_sent);
data = data.arg(rpt_received); data = data.arg(rpt_received);
int other = 0;
QStringList additional;
if(!additionalFields.isEmpty()){
foreach(auto key, additionalFields.keys()){
QString n3key;
if(N3FJP_ADIF_MAP.contains(key)){
n3key = N3FJP_ADIF_MAP.value(key);
} else {
other++;
n3key = N3FJP_ADIF_MAP.value(QString("*%1").arg(other));
}
if(n3key.isEmpty()){
break;
}
auto value = additionalFields[key].toString();
additional.append(QString("<%1>%2</%1>").arg(n3key).arg(value));
}
}
data = data.arg(additional.join(""));
auto host = m_config.n3fjp_server_name(); auto host = m_config.n3fjp_server_name();
auto port = m_config.n3fjp_server_port(); auto port = m_config.n3fjp_server_port();
m_n3fjpClient->sendNetworkMessage(host, port, data.toLocal8Bit()); if(m_n3fjpClient->sendNetworkMessage(host, port, data.toLocal8Bit(), 100)){
QTimer::singleShot(300, this, [this, host, port](){ QTimer::singleShot(300, this, [this, host, port](){
m_n3fjpClient->sendNetworkMessage(host, port, "<CMD><CHECKLOG></CMD>"); m_n3fjpClient->sendNetworkMessage(host, port, "<CMD><CHECKLOG></CMD>", 100);
m_n3fjpClient->sendNetworkMessage(host, port, "\r\n"); m_n3fjpClient->sendNetworkMessage(host, port, "\r\n", 100);
}); });
} }
}
// reload the logbook data // reload the logbook data
m_logBook.init(); m_logBook.init();

View File

@ -352,7 +352,7 @@ private slots:
, QString const& rpt_sent, QString const& rpt_received , QString const& rpt_sent, QString const& rpt_received
, QString const& comments , QString const& comments
, QString const& name, QDateTime const& QSO_date_on, QString const& operator_call , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call
, QString const& my_call, QString const& my_grid, QByteArray const& ADIF); , QString const& my_call, QString const& my_grid, QByteArray const& ADIF, const QMap<QString, QVariant> &additionalFields);
void on_bandComboBox_currentIndexChanged (int index); void on_bandComboBox_currentIndexChanged (int index);
void on_bandComboBox_activated (int index); void on_bandComboBox_activated (int index);
void on_readFreq_clicked(); void on_readFreq_clicked();