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){
if(!m_->isConnected(host, port)){
//qDebug() << "connecting to" << host << port;
m_->connectToHost(host, port);
}
return m_->waitForConnected(msecs);
}
bool TCPClient::sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf){
if(!ensureConnected(host, port)){
bool TCPClient::sendNetworkMessage(QString host, port_type port, QByteArray const &message, bool crlf, int msecs){
if(!ensureConnected(host, port, msecs)){
return false;
}
//qDebug() << "connecting to" << host << port;
qint64 n = m_->send(message, crlf);
if(n <= 0){
return false;
}
//qDebug() << "sent" << n << "bytes" << message;
return m_->flush();
}

View File

@ -18,7 +18,7 @@ signals:
public slots:
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:
class impl;

View File

@ -117,7 +117,8 @@ HEADERS += qt_helpers.hpp \
Inbox.h \
messagewindow.h \
SpotClient.h \
TCPClient.h
TCPClient.h \
logbook/n3fjp.h
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
, QDateTime const& dateTimeOff, QString const& band, QString const& comments
, 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;
t = "<call:" + QString::number(hisCall.length()) + ">" + hisCall;
@ -365,7 +365,7 @@ QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QStri
">" + operator_call;
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);
}

View File

@ -13,6 +13,7 @@
#include <QString>
#include <QStringList>
#include <QMultiHash>
#include <QVariant>
#else
#include <QtGui>
#endif
@ -42,7 +43,7 @@ class ADIF
, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff
, QString const& band, QString const& comments, QString const& name
, 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

View File

@ -13,6 +13,7 @@
#include "countrydat.h"
#include "countriesworked.h"
#include "adif.h"
#include "n3fjp.h"
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);
}
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(){
if(m_additionalFieldsControls.isEmpty()){
return;
@ -207,15 +219,6 @@ void LogQSO::accept()
QString hisCall,hisGrid,mode,submode,rptSent,rptRcvd,dateOn,dateOff,timeOn,timeOff,band,operator_call;
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();
hisGrid=ui->grid->text().toUpper();
mode = ui->mode->text().toUpper();
@ -239,6 +242,8 @@ void LogQSO::accept()
auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (filename);
adifile.init(adifilePath);
auto additionalFields = collectAdditionalFields();
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)};
@ -248,17 +253,6 @@ void LogQSO::accept()
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"
static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("js8call.log")};
if(!f.open(QIODevice::Text | QIODevice::Append)) {
@ -266,20 +260,35 @@ void LogQSO::accept()
tr ("Cannot open \"%1\" for append").arg (f.fileName ()),
tr ("Error: %1").arg (f.errorString ()));
} else {
QString logEntry=m_dateTimeOn.date().toString("yyyy-MM-dd,") +
m_dateTimeOn.time().toString("hh:mm:ss,") +
m_dateTimeOff.date().toString("yyyy-MM-dd,") +
m_dateTimeOff.time().toString("hh:mm:ss,") + hisCall + "," +
hisGrid + "," + strDialFreq + "," + (mode == "MFSK" ? "JS8" : mode) +
"," + rptSent + "," + rptRcvd +
"," + comments + "," + name;
QStringList logEntryItems = {
m_dateTimeOn.date().toString("yyyy-MM-dd"),
m_dateTimeOn.time().toString("hh:mm:ss"),
m_dateTimeOff.date().toString("yyyy-MM-dd"),
m_dateTimeOff.time().toString("hh:mm:ss"),
hisCall,
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);
out << logEntry << endl;
out << logEntryItems.join(",") << endl;
f.close();
}
//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();
}

View File

@ -45,7 +45,7 @@ signals:
, QString const& rpt_sent, QString const& rpt_received
, QString const& comments
, 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:
void hideEvent (QHideEvent *);
@ -53,6 +53,7 @@ protected:
private slots:
void createAdditionalField(QString key={}, QString value={});
void resetAdditionalFields();
QMap<QString, QVariant> collectAdditionalFields();
void on_add_new_field_button_pressed();
void on_start_now_button_pressed();
void on_end_now_button_pressed();

View File

@ -29,6 +29,8 @@
#include <QActionGroup>
#include <QSplashScreen>
#include <QSound>
#include <QUdpSocket>
#include <QVariant>
#include "APRSISClient.h"
#include "revision_utils.hpp"
@ -6624,6 +6626,7 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button
}
QString comments = ui->textEditRX->textCursor().selectedText();
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_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& comments
, 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");
m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date, name, comments);
// Log to JS8Call API
if(m_config.udpEnabled()){
sendNetworkMessage("LOG.QSO", QString(ADIF), {
{"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)},
{"STATION.OP", QVariant(operator_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()){
QString data = QString(
"<CMD>"
@ -6678,6 +6695,7 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
"<fldComments>%9</fldComments>"
"<fldRstS>%10</fldRstS>"
"<fldRstR>%11</fldRstR>"
"%12"
"</CMD>");
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_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 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](){
m_n3fjpClient->sendNetworkMessage(host, port, "<CMD><CHECKLOG></CMD>");
m_n3fjpClient->sendNetworkMessage(host, port, "\r\n");
m_n3fjpClient->sendNetworkMessage(host, port, "<CMD><CHECKLOG></CMD>", 100);
m_n3fjpClient->sendNetworkMessage(host, port, "\r\n", 100);
});
}
}
// reload the logbook data
m_logBook.init();

View File

@ -352,7 +352,7 @@ private slots:
, QString const& rpt_sent, QString const& rpt_received
, QString const& comments
, 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_activated (int index);
void on_readFreq_clicked();