From f304d3c6e1bd707e4d249656fb0637b1a5ea0de3 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Fri, 31 May 2019 15:15:45 -0400 Subject: [PATCH] Added N3FJP logging integration natively. Added selected text in the RX window to be placed in the comments of the log window. --- CMakeLists.txt | 1 + Configuration.cpp | 41 ++++++++++-- Configuration.hpp | 4 ++ Configuration.ui | 164 +++++++++++++++++++++++++++------------------- TCPClient.cpp | 81 +++++++++++++++++++++++ TCPClient.h | 28 ++++++++ js8call.pro | 6 +- logqso.cpp | 5 +- logqso.h | 2 +- mainwindow.cpp | 78 +++++++++++++++++----- mainwindow.h | 2 + 11 files changed, 319 insertions(+), 93 deletions(-) create mode 100644 TCPClient.cpp create mode 100644 TCPClient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c753f87..d9f50f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,7 @@ set (wsjt_qt_CXXSRCS NetworkMessage.cpp Message.cpp MessageClient.cpp + TCPClient.cpp LettersSpinBox.cpp HintedSpinBox.cpp RestrictedSpinBox.cpp diff --git a/Configuration.cpp b/Configuration.cpp index fb2e1b3..dc9f137 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -674,13 +674,12 @@ private: QString udp_server_name_; port_type udp_server_port_; -// QString n1mm_server_name () const; + QString n3fjp_server_name_; + port_type n3fjp_server_port_; + bool broadcast_to_n3fjp_; QString n1mm_server_name_; port_type n1mm_server_port_; bool broadcast_to_n1mm_; -// port_type n1mm_server_port () const; -// bool valid_n1mm_info () const; -// bool broadcast_to_n1mm() const; bool accept_udp_requests_; bool udpWindowToFront_; bool udpWindowRestore_; @@ -823,6 +822,9 @@ QString Configuration::aprs_passcode() const { return m_->aprs_passcode_; } QString Configuration::udp_server_name () const {return m_->udp_server_name_;} auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;} bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;} +QString Configuration::n3fjp_server_name () const {return m_->n3fjp_server_name_;} +auto Configuration::n3fjp_server_port () const -> port_type {return m_->n3fjp_server_port_;} +bool Configuration::broadcast_to_n3fjp () const {return m_->broadcast_to_n3fjp_;} QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;} auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;} bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;} @@ -953,6 +955,15 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s } } +bool Configuration::valid_n3fjp_info () const +{ + // do very rudimentary checking on the n3fjp server name and port number. + // + auto server_name = m_->n3fjp_server_name_; + auto port_number = m_->n3fjp_server_port_; + return(!(server_name.trimmed().isEmpty() || port_number == 0)); +} + bool Configuration::valid_n1mm_info () const { // do very rudimentary checking on the n1mm server name and port number. @@ -1200,10 +1211,13 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory, setUppercase(ui_->groups_line_edit); setUppercase(ui_->auto_whitelist_line_edit); - ui_->udp_server_port_spin_box->setMinimum (1); + ui_->udp_server_port_spin_box->setMinimum (0); ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits::max ()); - ui_->n1mm_server_port_spin_box->setMinimum (1); + ui_->n3fjp_server_port_spin_box->setMinimum (0); + ui_->n3fjp_server_port_spin_box->setMaximum (std::numeric_limits::max ()); + + ui_->n1mm_server_port_spin_box->setMinimum (0); ui_->n1mm_server_port_spin_box->setMaximum (std::numeric_limits::max ()); // @@ -1487,6 +1501,9 @@ void Configuration::impl::initialize_models () ui_->udp_server_line_edit->setText (udp_server_name_); ui_->udp_server_port_spin_box->setValue (udp_server_port_); ui_->accept_udp_requests_check_box->setChecked (accept_udp_requests_); + ui_->n3fjp_server_name_line_edit->setText (n3fjp_server_name_); + ui_->n3fjp_server_port_spin_box->setValue (n3fjp_server_port_); + ui_->enable_n3fjp_broadcast_check_box->setChecked (broadcast_to_n3fjp_); ui_->n1mm_server_name_line_edit->setText (n1mm_server_name_); ui_->n1mm_server_port_spin_box->setValue (n1mm_server_port_); ui_->enable_n1mm_broadcast_check_box->setChecked (broadcast_to_n1mm_); @@ -1783,6 +1800,9 @@ void Configuration::impl::read_settings () aprs_passcode_ = settings_->value ("aprsPasscode", "").toString(); udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString (); udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt (); + n3fjp_server_name_ = settings_->value ("N3FJPServer", "127.0.0.1").toString (); + n3fjp_server_port_ = settings_->value ("N3FJPServerPort", 1100).toUInt (); + broadcast_to_n3fjp_ = settings_->value ("BroadcastToN3FJP", false).toBool (); n1mm_server_name_ = settings_->value ("N1MMServer", "127.0.0.1").toString (); n1mm_server_port_ = settings_->value ("N1MMServerPort", 2333).toUInt (); broadcast_to_n1mm_ = settings_->value ("BroadcastToN1MM", false).toBool (); @@ -1929,6 +1949,9 @@ void Configuration::impl::write_settings () settings_->setValue ("aprsPasscode", aprs_passcode_); settings_->setValue ("UDPServer", udp_server_name_); settings_->setValue ("UDPServerPort", udp_server_port_); + settings_->setValue ("N3FJPServer", n3fjp_server_name_); + settings_->setValue ("N3FJPServerPort", n3fjp_server_port_); + settings_->setValue ("BroadcastToN3FJP", broadcast_to_n3fjp_); settings_->setValue ("N1MMServer", n1mm_server_name_); settings_->setValue ("N1MMServerPort", n1mm_server_port_); settings_->setValue ("BroadcastToN1MM", broadcast_to_n1mm_); @@ -2482,6 +2505,12 @@ void Configuration::impl::accept () } accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked (); + auto new_n3fjp_server = ui_->n3fjp_server_name_line_edit->text (); + n3fjp_server_name_ = new_n3fjp_server; + auto new_n3fjp_port = ui_->n3fjp_server_port_spin_box->value (); + n3fjp_server_port_ = new_n3fjp_port; + broadcast_to_n3fjp_ = ui_->enable_n3fjp_broadcast_check_box->isChecked (); + auto new_n1mm_server = ui_->n1mm_server_name_line_edit->text (); n1mm_server_name_ = new_n1mm_server; auto new_n1mm_port = ui_->n1mm_server_port_spin_box->value (); diff --git a/Configuration.hpp b/Configuration.hpp index 14d5b54..d2eeac1 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -176,6 +176,10 @@ public: port_type n1mm_server_port () const; bool valid_n1mm_info () const; bool broadcast_to_n1mm() const; + QString n3fjp_server_name () const; + port_type n3fjp_server_port () const; + bool valid_n3fjp_info () const; + bool broadcast_to_n3fjp() const; bool accept_udp_requests () const; bool udpWindowToFront () const; bool udpWindowRestore () const; diff --git a/Configuration.ui b/Configuration.ui index abcb94d..6a66f95 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -23,7 +23,7 @@ Select tab to change configuration parameters. - 0 + 3 @@ -62,8 +62,8 @@ 0 0 - 738 - 453 + 587 + 303 @@ -2155,7 +2155,7 @@ both here. 0 0 746 - 525 + 663 @@ -2179,7 +2179,42 @@ both here. - + + + + false + + + false + + + Some logging programs will not accept the type of reports +saved by this program. +Check this option to save the sent and received reports in the +comments field. + + + d&B reports to comments + + + + + + + true + + + false + + + Check this option to force deselect callsign after logging. + + + Deselect callsign after logging + + + + Operator Callsign (if different than Station Callsign): @@ -2202,23 +2237,7 @@ both here. - - - - true - - - false - - - Check this option to force deselect callsign after logging. - - - Deselect callsign after logging - - - - + <html><head/><body><p>The callsign of the operator, if different from the station callsign.</p></body></html> @@ -2238,41 +2257,6 @@ both here. - - - - false - - - false - - - Some logging programs will not accept the type of reports -saved by this program. -Check this option to save the sent and received reports in the -comments field. - - - d&B reports to comments - - - - - - - false - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -2409,8 +2393,8 @@ comments field. Network Services - - + + The program can send your station details and all @@ -2426,7 +2410,7 @@ for assessing propagation and system performance. - + QFormLayout::AllNonFixedFieldsGrow @@ -2504,10 +2488,58 @@ for assessing propagation and system performance. + + + + N3FJP Logger + + + + + + Enable sending logged contacts to N3FJP logging software + + + + + + + + + <html><head/><body><p>N3FJP Server:</p></body></html> + + + + + + + <html><head/><body><p>Optional host name of N3FJP software to receive logged contacts. This is usually 'localhost' or ip address 127.0.0.1</p><p>Formats:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">hostname</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">IPv4 address</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">IPv6 address</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">IPv4 multicast group address</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">IPv6 multicast group address</li></ul><p>Clearing this field will disable broadcasting of ADIF information via UDP.</p></body></html> + + + + + + + <html><head/><body><p>N3FJP Server Port:</p></body></html> + + + + + + + <html><head/><body><p>Enter the port number that should be used for N3FJP log information. For N3FJP, this value should be 1100. If this is zero, no contacts will be sent.</p></body></html> + + + + + + + + - N1MM Logger+ Broadcasts + N1MM Logger @@ -2519,7 +2551,7 @@ for assessing propagation and system performance. <html><head/><body><p>When checked, the app will broadcast a logged contact in ADIF format to the configured hostname and port. </p></body></html> - Enable logged contact ADIF broadcast + Enable sending logged contacts to N1MM logging software @@ -4258,12 +4290,12 @@ soundcard changes - - - - + + + + diff --git a/TCPClient.cpp b/TCPClient.cpp new file mode 100644 index 0000000..31b629b --- /dev/null +++ b/TCPClient.cpp @@ -0,0 +1,81 @@ +#include "TCPClient.h" + +#include +#include + +#include "pimpl_impl.hpp" + +#include "moc_TCPClient.cpp" + + +class TCPClient::impl + : public QTcpSocket +{ + Q_OBJECT + +public: + using port_type = quint16; + + impl (TCPClient * self) + : self_ {self} + { + } + + ~impl () + { + } + + bool isConnected(QString host, port_type port){ + if(host_ != host || port_ != port){ + disconnectFromHost(); + return false; + } + return state() == QTcpSocket::ConnectedState ; + } + + void connectToHost(QString host, port_type port){ + host_ = host; + port_ = port; + + QTcpSocket::connectToHost(host_, port_); + } + + qint64 send(QByteArray const &message, bool crlf){ + return write(message + (crlf ? "\r\f" : "")); + } + + TCPClient * self_; + QString host_; + port_type port_; +}; + +#include "TCPClient.moc" + +TCPClient::TCPClient(QObject *parent) : QObject(parent) + , m_ {this} +{ +} + +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)){ + 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(); +} diff --git a/TCPClient.h b/TCPClient.h new file mode 100644 index 0000000..9ee23d3 --- /dev/null +++ b/TCPClient.h @@ -0,0 +1,28 @@ +#ifndef TCPCLIENT_H +#define TCPCLIENT_H + +#include +#include + +#include "pimpl_h.hpp" + +class TCPClient : public QObject +{ + Q_OBJECT +public: + using port_type = quint16; + + explicit TCPClient(QObject *parent = nullptr); + +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); + +private: + class impl; + pimpl m_; +}; + +#endif // TCPCLIENT_H diff --git a/js8call.pro b/js8call.pro index d2cb38b..dbcb2fd 100644 --- a/js8call.pro +++ b/js8call.pro @@ -82,7 +82,8 @@ SOURCES += \ Message.cpp \ Inbox.cpp \ messagewindow.cpp \ - SpotClient.cpp + SpotClient.cpp \ + TCPClient.cpp HEADERS += qt_helpers.hpp \ pimpl_h.hpp pimpl_impl.hpp \ @@ -115,7 +116,8 @@ HEADERS += qt_helpers.hpp \ Message.h \ Inbox.h \ messagewindow.h \ - SpotClient.h + SpotClient.h \ + TCPClient.h INCLUDEPATH += qmake_only diff --git a/logqso.cpp b/logqso.cpp index 7d704f1..99d13ce 100644 --- a/logqso.cpp +++ b/logqso.cpp @@ -55,7 +55,7 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, Radio::Frequency dialFreq, QString const& myCall, QString const& myGrid, - bool toDATA, bool dBtoComments, bool bFox, QString const& opCall) + bool toDATA, bool dBtoComments, bool bFox, QString const& opCall, QString const& comments) { if(!isHidden()) return; ui->call->setText(hisCall); @@ -69,6 +69,9 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString if(rptRcvd!="") t+=" Rcvd: " + rptRcvd; ui->comments->setText(t); } + if(!comments.isEmpty()){ + ui->comments->setText(comments); + } if(toDATA) mode="DATA"; ui->mode->setText(mode); ui->sent->setText(rptSent); diff --git a/logqso.h b/logqso.h index 21dd5a7..2defb70 100644 --- a/logqso.h +++ b/logqso.h @@ -33,7 +33,7 @@ public: QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, Radio::Frequency dialFreq, QString const& myCall, QString const& myGrid, - bool toDATA, bool dBtoComments, bool bFox, QString const& opCall); + bool toDATA, bool dBtoComments, bool bFox, QString const& opCall, const QString &comments); public slots: void accept(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 1199f0f..11188b5 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -564,6 +564,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, version (), revision (), m_config.udp_server_name (), m_config.udp_server_port (), this}}, + m_n3fjpClient { new TCPClient{this}}, m_spotClient { new SpotClient{m_messageClient, this}}, m_aprsClient {new APRSISClient{"rotate.aprs2.net", 14580, this}}, psk_Reporter {new PSK_Reporter {m_messageClient, this}}, @@ -6587,11 +6588,13 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button if(m_callActivity.contains(call)){ grid = m_callActivity[call].grid; } + + 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(), m_config.log_as_DATA(), m_config.report_in_comments(), - m_config.bFox(), m_opCall); + m_config.bFox(), m_opCall, comments); } void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid @@ -6604,23 +6607,64 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString date = QSO_date_on.toString("yyyyMMdd"); m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date, name, comments); - sendNetworkMessage("LOG.QSO", QString(ADIF), { - {"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())}, - {"UTC.OFF", QVariant(QSO_date_off.toMSecsSinceEpoch())}, - {"CALL", QVariant(call)}, - {"GRID", QVariant(grid)}, - {"FREQ", QVariant(dial_freq)}, - {"MODE", QVariant(mode)}, - {"SUBMODE", QVariant(submode)}, - {"RPT.SENT", QVariant(rpt_sent)}, - {"RPT.RECV", QVariant(rpt_received)}, - {"NAME", QVariant(name)}, - {"COMMENTS", QVariant(comments)}, - {"STATION.OP", QVariant(operator_call)}, - {"STATION.CALL", QVariant(my_call)}, - {"STATION.GRID", QVariant(my_grid)} - }); + if(m_config.udpEnabled()){ + sendNetworkMessage("LOG.QSO", QString(ADIF), { + {"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())}, + {"UTC.OFF", QVariant(QSO_date_off.toMSecsSinceEpoch())}, + {"CALL", QVariant(call)}, + {"GRID", QVariant(grid)}, + {"FREQ", QVariant(dial_freq)}, + {"MODE", QVariant(mode)}, + {"SUBMODE", QVariant(submode)}, + {"RPT.SENT", QVariant(rpt_sent)}, + {"RPT.RECV", QVariant(rpt_received)}, + {"NAME", QVariant(name)}, + {"COMMENTS", QVariant(comments)}, + {"STATION.OP", QVariant(operator_call)}, + {"STATION.CALL", QVariant(my_call)}, + {"STATION.GRID", QVariant(my_grid)} + }); + } + if(m_config.broadcast_to_n3fjp() && m_config.valid_n3fjp_info()){ + QString data = QString( + "" + "" + "TRUE" + "FALSE" + "%1" + "%2" + "%3" + "%4" + "%5" + "%6" + "JS8" + "%7" + "%8" + "%9" + ""); + + data = data.arg(QSO_date_on.toString("yyyy/MM/dd")); + data = data.arg(QSO_date_on.toString("H:mm")); + data = data.arg(call); + data = data.arg(grid); + data = data.arg(m_config.bands ()->find(dial_freq).replace("m", "")); + data = data.arg(Radio::frequency_MHz_string(dial_freq)); + data = data.arg(operator_call); + data = data.arg(name); + data = data.arg(comments); + + auto host = m_config.n3fjp_server_name(); + auto port = m_config.n3fjp_server_port(); + + m_n3fjpClient->sendNetworkMessage(host, port, data.toLocal8Bit()); + + QTimer::singleShot(1000, this, [this, host, port](){ + m_n3fjpClient->sendNetworkMessage(host, port, ""); + }); + } + + // reload the logbook data m_logBook.init(); if (m_config.clear_callsign ()){ diff --git a/mainwindow.h b/mainwindow.h index 37781f1..d1a5435 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -44,6 +44,7 @@ #include "qpriorityqueue.h" #include "varicode.h" #include "MessageClient.hpp" +#include "TCPClient.h" #include "SpotClient.h" #include "APRSISClient.h" #include "keyeater.h" @@ -903,6 +904,7 @@ private: int m_firstDecode; QProgressDialog m_optimizingProgress; MessageClient * m_messageClient; + TCPClient * m_n3fjpClient; PSK_Reporter *psk_Reporter; SpotClient *m_spotClient; APRSISClient * m_aprsClient;