diff --git a/CMakeLists.txt b/CMakeLists.txt
index 86bc970c6..e95a92551 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -222,6 +222,7 @@ set (wsjt_qt_CXXSRCS
widgets/DoubleClickablePushButton.cpp
widgets/DoubleClickableRadioButton.cpp
Network/LotWUsers.cpp
+ Network/FileDownload.cpp
models/DecodeHighlightingModel.cpp
widgets/DecodeHighlightingListView.cpp
models/FoxLog.cpp
diff --git a/Configuration.cpp b/Configuration.cpp
index c8652a599..aed2c5417 100644
--- a/Configuration.cpp
+++ b/Configuration.cpp
@@ -201,6 +201,7 @@
#include "models/DecodeHighlightingModel.hpp"
#include "logbook/logbook.h"
#include "widgets/LazyFillComboBox.hpp"
+#include "Network/FileDownload.hpp"
#include "ui_Configuration.h"
#include "moc_Configuration.cpp"
@@ -564,6 +565,8 @@ private:
Q_SLOT void on_add_macro_line_edit_editingFinished ();
Q_SLOT void delete_macro ();
void delete_selected_macros (QModelIndexList);
+ void after_CTY_downloaded();
+ void set_CTY_DAT_version(QString const& version);
Q_SLOT void on_udp_server_line_edit_textChanged (QString const&);
Q_SLOT void on_udp_server_line_edit_editingFinished ();
Q_SLOT void on_save_path_select_push_button_clicked (bool);
@@ -574,7 +577,9 @@ private:
Q_SLOT void handle_transceiver_failure (QString const& reason);
Q_SLOT void on_reset_highlighting_to_defaults_push_button_clicked (bool);
Q_SLOT void on_rescan_log_push_button_clicked (bool);
+ Q_SLOT void on_CTY_download_button_clicked (bool);
Q_SLOT void on_LotW_CSV_fetch_push_button_clicked (bool);
+
Q_SLOT void on_cbx2ToneSpacing_clicked(bool);
Q_SLOT void on_cbx4ToneSpacing_clicked(bool);
Q_SLOT void on_prompt_to_log_check_box_clicked(bool);
@@ -746,7 +751,7 @@ private:
QAudioDeviceInfo next_audio_output_device_;
AudioDevice::Channel audio_output_channel_;
AudioDevice::Channel next_audio_output_channel_;
-
+ FileDownload cty_download;
friend class Configuration;
};
@@ -859,6 +864,11 @@ bool Configuration::highlight_73 () const {return m_->highlight_73_;}
bool Configuration::highlight_DXcall () const {return m_->highlight_DXcall_;}
bool Configuration::highlight_DXgrid () const {return m_->highlight_DXgrid_;}
+void Configuration::set_CTY_DAT_version(QString const& version)
+{
+ m_->set_CTY_DAT_version(version);
+}
+
void Configuration::set_calibration (CalibrationParams params)
{
m_->calibration_ = params;
@@ -1174,8 +1184,13 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
// set up LoTW users CSV file fetching
connect (&lotw_users_, &LotWUsers::load_finished, [this] () {
- ui_->LotW_CSV_fetch_push_button->setEnabled (true);
- });
+ ui_->LotW_CSV_fetch_push_button->setEnabled (true);
+ });
+
+ connect(&lotw_users_, &LotWUsers::progress, [this] (QString const& msg) {
+ ui_->LotW_CSV_status_label->setText(msg);
+ });
+
lotw_users_.set_local_file_path (writeable_data_dir_.absoluteFilePath ("lotw-user-activity.csv"));
//
@@ -2402,9 +2417,37 @@ void Configuration::impl::on_reset_highlighting_to_defaults_push_button_clicked
void Configuration::impl::on_rescan_log_push_button_clicked (bool /*clicked*/)
{
- if (logbook_) logbook_->rescan ();
+ if (logbook_) {
+ logbook_->rescan ();
+ }
}
+void Configuration::impl::on_CTY_download_button_clicked (bool /*clicked*/)
+{
+ ui_->CTY_download_button->setEnabled (false); // disable button until download is complete
+ QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)};
+ cty_download.configure(network_manager_,
+ "http://www.country-files.com/bigcty/cty.dat",
+ dataPath.absoluteFilePath("cty.dat"),
+ "WSJT-X CTY Downloader");
+
+ // set up LoTW users CSV file fetching
+ connect (&cty_download, &FileDownload::complete, this, &Configuration::impl::after_CTY_downloaded, Qt::UniqueConnection);
+ cty_download.start_download();
+}
+void Configuration::impl::set_CTY_DAT_version(QString const& version)
+{
+ ui_->CTY_file_label->setText(QString{"CTY File Version: %1"}.arg(version));
+}
+
+void Configuration::impl::after_CTY_downloaded ()
+{
+ ui_->CTY_download_button->setEnabled (true);
+ if (logbook_) {
+ logbook_->rescan ();
+ ui_->CTY_file_label->setText(QString{"CTY File Version: %1"}.arg(logbook_->cty_version()));
+ }
+}
void Configuration::impl::on_LotW_CSV_fetch_push_button_clicked (bool /*checked*/)
{
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), true, true);
diff --git a/Configuration.hpp b/Configuration.hpp
index dbf6b9124..c3e24d935 100644
--- a/Configuration.hpp
+++ b/Configuration.hpp
@@ -243,6 +243,8 @@ public:
// Close down connection to rig.
void transceiver_offline ();
+ void set_CTY_DAT_version(QString const& version);
+
// Set transceiver frequency in Hertz.
Q_SLOT void transceiver_frequency (Frequency);
diff --git a/Configuration.ui b/Configuration.ui
index 1f91e09cd..e7502c617 100644
--- a/Configuration.ui
+++ b/Configuration.ui
@@ -6,8 +6,8 @@
0
0
- 588
- 642
+ 684
+ 662
@@ -2407,8 +2407,14 @@ Right click for insert and delete options.
Logbook of the World User Validation
-
- -
+
+
+ 4
+
+
+ 4
+
+
-
Users CSV file URL:
@@ -2418,7 +2424,7 @@ Right click for insert and delete options.
- -
+
-
-
@@ -2445,7 +2451,7 @@ Right click for insert and delete options.
- -
+
-
Age of last upload less than:
@@ -2455,27 +2461,57 @@ Right click for insert and delete options.
- -
-
-
- <html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html>
-
-
- Days since last upload
-
-
- days
-
-
- 0
-
-
- 9999
-
-
- 365
-
-
+
-
+
+
-
+
+
+ <html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html>
+
+
+ Days since last upload
+
+
+ days
+
+
+ 0
+
+
+ 9999
+
+
+ 365
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 240
+ 0
+
+
+
+
+
+
+
+
@@ -2493,6 +2529,35 @@ Right click for insert and delete options.
+ -
+
+
+ CTY File Download
+
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+ CTY File Version:
+
+
+
+ -
+
+
+ Download Latest CTY.dat
+
+
+
+
+
+
-
@@ -2501,7 +2566,7 @@ Right click for insert and delete options.
20
- 40
+ 20
@@ -3336,13 +3401,13 @@ Right click for insert and delete options.
-
-
-
-
+
+
+
+
diff --git a/Network/FileDownload.cpp b/Network/FileDownload.cpp
index 63f51d98b..7630ae59b 100644
--- a/Network/FileDownload.cpp
+++ b/Network/FileDownload.cpp
@@ -3,6 +3,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -11,7 +13,8 @@
FileDownload::FileDownload() : QObject(nullptr)
{
-
+ redirect_count_ = 0;
+ url_valid_ = false;
}
FileDownload::~FileDownload()
@@ -20,14 +23,17 @@ FileDownload::~FileDownload()
void FileDownload::errorOccurred(QNetworkReply::NetworkError code)
{
- LOG_INFO(QString{"DOWNLOAD: errorOccurred %1 -> %2"}.arg(code).arg(reply_->errorString()));
- //LOG_INFO(QString{ "DOWNLOAD: server returned %1"}.arg(reply_->))
+ LOG_INFO(QString{"FileDownload [%1]: errorOccurred %2 -> %3"}.arg(user_agent_).arg(code).arg(reply_->errorString()));
+ Q_EMIT error (reply_->errorString ());
+ delete tmpfile_;
}
-void FileDownload::configure(const QString &source_url, const QString &destination_path)
+void FileDownload::configure(QNetworkAccessManager *network_manager, const QString &source_url, const QString &destination_path, const QString &user_agent)
{
+ manager_ = network_manager;
source_url_ = source_url;
destination_filename_ = destination_path;
+ user_agent_ = user_agent;
}
void FileDownload::store()
@@ -35,17 +41,89 @@ void FileDownload::store()
if (tmpfile_->isOpen())
tmpfile_->write (reply_->read (reply_->bytesAvailable ()));
else
- LOG_INFO(QString{ "DOWNLOAD: tmpfile is not open"});
+ LOG_INFO(QString{ "FileDownload [%1]: tmpfile is not open"}.arg(user_agent_));
}
void FileDownload::replyComplete()
{
- auto is_error = reply_->error ();
- LOG_INFO(QString{"DOWNLOAD: reply complete %1"}.arg(is_error));
+ QFileInfo destination_file(destination_filename_);
+ QString const tmpfile_path = destination_file.absolutePath();
+ QDir tmpdir_(tmpfile_path);
+
+ LOG_DEBUG(QString{ "FileDownload [%1]: replyComplete"}.arg(user_agent_));
+ if (!reply_)
+ {
+ Q_EMIT load_finished ();
+ return; // we probably deleted it in an earlier call
+ }
+
+ QUrl redirect_url {reply_->attribute (QNetworkRequest::RedirectionTargetAttribute).toUrl ()};
+
+ if (reply_->error () == QNetworkReply::NoError && !redirect_url.isEmpty ())
+ {
+ if ("https" == redirect_url.scheme () && !QSslSocket::supportsSsl ())
+ {
+ Q_EMIT download_error (tr ("Network Error - SSL/TLS support not installed, cannot fetch:\n\'%1\'")
+ .arg (redirect_url.toDisplayString ()));
+ url_valid_ = false; // reset
+ Q_EMIT load_finished ();
+ }
+ else if (++redirect_count_ < 10) // maintain sanity
+ {
+ // follow redirect
+ download (reply_->url ().resolved (redirect_url));
+ }
+ else
+ {
+ Q_EMIT download_error (tr ("Network Error - Too many redirects:\n\'%1\'")
+ .arg (redirect_url.toDisplayString ()));
+ url_valid_ = false; // reset
+ Q_EMIT load_finished ();
+ }
+ }
+ else if (reply_->error () != QNetworkReply::NoError)
+ {
+ tmpfile_->close();
+ delete tmpfile_;
+ url_valid_ = false; // reset
+ // report errors that are not due to abort
+ if (QNetworkReply::OperationCanceledError != reply_->error ())
+ {
+ Q_EMIT download_error (tr ("Network Error:\n%1")
+ .arg (reply_->errorString ()));
+ }
+ Q_EMIT load_finished ();
+ }
+ else
+ {
+ if (!url_valid_)
+ {
+ // now get the body content
+ url_valid_ = true;
+ download (reply_->url ().resolved (redirect_url));
+ }
+ else // the body has completed. Save it.
+ {
+ url_valid_ = false; // reset
+ // load the database asynchronously
+ // future_load_ = std::async (std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_.fileName ());
+ LOG_INFO(QString{ "FileDownload [%1]: complete. tempfile path is %2"}.arg(user_agent_).arg(tmpfile_->fileName()));
+ // move the file to the destination
+ tmpdir_.remove(destination_filename_+".old"); // get rid of previous version
+ tmpdir_.rename(destination_filename_, destination_filename_+".old");
+ tmpdir_.rename(tmpfile_->fileName(), destination_filename_);
+ LOG_INFO(QString{ "FileDownload [%1]: moved tempfile %2 to %3"}.arg(user_agent_).arg(tmpfile_->fileName()).arg(destination_filename_));
+ tmpfile_->close();
+ delete tmpfile_;
+ emit complete(destination_filename_);
+ }
+ }
+
if (reply_ && reply_->isFinished ())
{
reply_->deleteLater ();
}
+
}
void FileDownload::downloadComplete(QNetworkReply *data)
@@ -53,75 +131,84 @@ void FileDownload::downloadComplete(QNetworkReply *data)
// make a temp file in the same place as the file we're downloading. Needs to be on the same
// filesystem as where we eventually want to 'mv' it.
- QUrl r = request_->url();
- LOG_INFO(QString{"DOWNLOAD: finished download %1 -> %2 (%3)"}.arg(source_url_).arg(destination_filename_).arg(r.url()));
-
- LOG_INFO(QString{ "DOWNLOAD: tempfile path is %1"}.arg(tmpfile_->fileName()));
-
- tmpfile_->close();
-
- LOG_INFO(QString{"DOWNLOAD: moving file to %2"}.arg(destination_filename_));
+ QUrl r = request_.url();
+ LOG_INFO(QString{"FileDownload [%1]: finished %2 of %3 -> %4 (%5)"}.arg(user_agent_).arg(data->operation()).arg(source_url_).arg(destination_filename_).arg(r.url()));
+#ifdef DEBUG_FILEDOWNLOAD
LOG_INFO("Request Headers:");
- Q_FOREACH (const QByteArray& hdr, request_->rawHeaderList()) {
- LOG_INFO(QString{ "%1 -> %2"}.arg(QString(hdr)).arg(QString(request_->rawHeader(hdr))));
- }
+ Q_FOREACH (const QByteArray& hdr, request_.rawHeaderList()) {
+ LOG_INFO(QString{ "%1 -> %2"}.arg(QString(hdr)).arg(QString(request_.rawHeader(hdr))));
+ }
LOG_INFO("Response Headers:");
Q_FOREACH (const QByteArray& hdr, reply_->rawHeaderList()) {
LOG_INFO(QString{ "%1 -> %2"}.arg(QString(hdr)).arg(QString(reply_->rawHeader(hdr))));
}
- // move the file to the destination
- tmpdir_->remove(destination_filename_+".old"); // get rid of previous version
- tmpdir_->rename(destination_filename_, destination_filename_+".old");
- tmpdir_->rename(tmpfile_->fileName(), destination_filename_);
- emit complete(destination_filename_);
+#endif
data->deleteLater();
}
-void FileDownload::download()
+void FileDownload::start_download()
{
- //QUrl url = QUrl(source_url_);
+ url_valid_ = false;
+ download(QUrl(source_url_));
+}
- manager_ = new QNetworkAccessManager(this);
+void FileDownload::download(QUrl qurl)
+{
+ request_.setUrl(qurl);
- // request_ = new QNetworkRequest("https://www.country-files.com/bigcty/cty.dat");
- request_ = new QNetworkRequest(QUrl(source_url_));
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ if (QNetworkAccessManager::Accessible != manager_->networkAccessible ())
+ {
+ // try and recover network access for QNAM
+ manager_->setNetworkAccessible (QNetworkAccessManager::Accessible);
+ }
+#endif
- LOG_INFO(QString{"DOWNLOAD: starting download %1 -> %2"}.arg(source_url_).arg(destination_filename_));
+ LOG_INFO(QString{"FileDownload [%1]: Starting download of %2 to %3"}.arg(user_agent_).arg(source_url_).arg(destination_filename_));
- request_->setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
- //request_->setHeader( QNetworkRequest::ContentTypeHeader, "some/type" );
- request_->setRawHeader("Accept", "*/*");
- request_->setRawHeader ("User-Agent", "WSJT-X CTY Downloader");
+ request_.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+ request_.setRawHeader("Accept", "*/*");
+ request_.setRawHeader ("User-Agent", user_agent_.toLocal8Bit()); // Must have a UA for some sites, like country-files
- reply_ = manager_->get(*request_);
+ if (!url_valid_)
+ {
+ reply_ = manager_->head(request_);
+ }
+ else
+ {
+ reply_ = manager_->get (request_);
+ }
- reply_->setReadBufferSize(0);
+ QObject::connect(manager_, &QNetworkAccessManager::finished, this, &FileDownload::downloadComplete, Qt::UniqueConnection);
+ QObject::connect(reply_, &QNetworkReply::downloadProgress, this, &FileDownload::downloadProgress, Qt::UniqueConnection);
+ QObject::connect(reply_, &QNetworkReply::finished, this,&FileDownload::replyComplete, Qt::UniqueConnection);
- int http_code = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ QObject::connect(reply_, &QNetworkReply::errorOccurred,this,&FileDownload::errorOccurred, Qt::UniqueConnection);
+ QObject::connect (reply_, &QNetworkReply::readyRead, this, &FileDownload::store, Qt::UniqueConnection);
- QObject::connect(manager_, &QNetworkAccessManager::finished, this, &FileDownload::downloadComplete);
- QObject::connect(reply_, &QNetworkReply::downloadProgress, this, &FileDownload::downloadProgress);
- QObject::connect(reply_, &QNetworkReply::finished, this,&FileDownload::replyComplete);
- QObject::connect(reply_, &QNetworkReply::errorOccurred,this,&FileDownload::errorOccurred);
- QObject::connect (reply_, &QNetworkReply::finished, this, &FileDownload::replyComplete);
- QObject::connect (reply_, &QNetworkReply::readyRead, this, &FileDownload::store);
-
- QFileInfo tmpfi(destination_filename_);
- QString const tmpfile_path = tmpfi.absolutePath();
- tmpdir_ = new QDir(tmpfile_path);
- tmpfile_ = new QTemporaryFile(tmpfile_path+"/big.cty.XXXXXX");
+ QFileInfo destination_file(destination_filename_);
+ QString const tmpfile_base = destination_file.fileName();
+ QString const tmpfile_path = destination_file.absolutePath();
+ tmpfile_ = new QTemporaryFile(tmpfile_path+QDir::separator()+tmpfile_base+".XXXXXX");
if (!tmpfile_->open())
{
- LOG_INFO(QString{"DOWNLOAD: Unable to open the temporary file based on %1"}.arg(tmpfile_path));
+ LOG_INFO(QString{"FileDownload [%1]: Unable to open the temporary file based on %2"}.arg(user_agent_).arg(tmpfile_path));
return;
}
- LOG_INFO(QString{"DOWNLOAD: let's go %1"}.arg(http_code));
}
void FileDownload::downloadProgress(qint64 received, qint64 total)
{
- LOG_INFO(QString{"DOWNLOAD: Progress %1 from %2, total %3, so far %4"}.arg(destination_filename_).arg(source_url_).arg(total).arg(received));
- //qDebug() << received << total;
+ LOG_DEBUG(QString{"FileDownload: [%1] Progress %2 from %3, total %4, so far %5"}.arg(user_agent_).arg(destination_filename_).arg(source_url_).arg(total).arg(received));
+ Q_EMIT progress(QString{"%4 bytes downloaded"}.arg(received));
}
+
+void FileDownload::abort ()
+{
+ if (reply_ && reply_->isRunning ())
+ {
+ reply_->abort ();
+ }
+}
\ No newline at end of file
diff --git a/Network/FileDownload.hpp b/Network/FileDownload.hpp
index 91409e8fc..b562cc925 100644
--- a/Network/FileDownload.hpp
+++ b/Network/FileDownload.hpp
@@ -3,6 +3,7 @@
#include
#include
+#include
#include
#include
#include
@@ -14,22 +15,31 @@ public:
explicit FileDownload();
~FileDownload();
- void configure(const QString& source_url, const QString& destination_filename);
+ void configure(QNetworkAccessManager *network_manager, const QString& source_url, const QString& destination_filename, const QString& user_agent);
private:
QNetworkAccessManager *manager_;
QString source_url_;
QString destination_filename_;
- QNetworkReply *reply_;
- QNetworkRequest *request_;
- QTemporaryFile *tmpfile_;
- QDir *tmpdir_;
+ QString user_agent_;
+ QPointer reply_;
+ QNetworkRequest request_;
+ QPointer tmpfile_;
+ bool url_valid_;
+ int redirect_count_;
signals:
void complete(QString filename);
+ void progress(QString filename);
+ void load_finished() const;
+ void download_error (QString const& reason) const;
+ void error(QString const& reason) const;
+
public slots:
- void download();
+ void start_download();
+ void download(QUrl url);
void store();
+ void abort();
void downloadComplete(QNetworkReply* data);
void downloadProgress(qint64 recieved, qint64 total);
void errorOccurred(QNetworkReply::NetworkError code);
diff --git a/Network/LotWUsers.cpp b/Network/LotWUsers.cpp
index 189b0ca9d..10a9a4d8b 100644
--- a/Network/LotWUsers.cpp
+++ b/Network/LotWUsers.cpp
@@ -18,7 +18,7 @@
#include
#include "qt_helpers.hpp"
#include "Logger.hpp"
-
+#include "FileDownload.hpp"
#include "pimpl_impl.hpp"
#include "moc_LotWUsers.cpp"
@@ -41,6 +41,7 @@ public:
, url_valid_ {false}
, redirect_count_ {0}
, age_constraint_ {365}
+ , connected_ {false}
{
}
@@ -50,14 +51,36 @@ public:
auto csv_file_name = csv_file_.fileName ();
auto exists = QFileInfo::exists (csv_file_name);
if (fetch && (!exists || forced_fetch))
+ {
+ current_url_.setUrl(url);
+ if (current_url_.isValid() && !QSslSocket::supportsSsl())
{
- current_url_.setUrl (url);
- if (current_url_.isValid () && !QSslSocket::supportsSsl ())
- {
- current_url_.setScheme ("http");
- }
- redirect_count_ = 0;
- download (current_url_);
+ current_url_.setScheme("http");
+ }
+ redirect_count_ = 0;
+
+ Q_EMIT self_->progress (QString("Starting download from %1").arg(url));
+
+ lotw_downloader_.configure(network_manager_,
+ url,
+ csv_file_name,
+ "WSJT-X LotW User Downloader");
+ if (!connected_)
+ {
+ connect(&lotw_downloader_, &FileDownload::complete, [this, csv_file_name] {
+ LOG_INFO(QString{"LotWUsers: Loading LotW file %1"}.arg(csv_file_name));
+ future_load_ = std::async(std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_name);
+ });
+ connect(&lotw_downloader_, &FileDownload::error, [this] (QString const& msg) {
+ LOG_INFO(QString{"LotWUsers: Error downloading LotW file: %1"}.arg(msg));
+ Q_EMIT self_->LotW_users_error (msg);
+ });
+ connect( &lotw_downloader_, &FileDownload::progress, [this] (QString const& msg) {
+ Q_EMIT self_->progress (msg);
+ });
+ connected_ = true;
+ }
+ lotw_downloader_.start_download();
}
else
{
@@ -69,143 +92,9 @@ public:
}
}
- void download (QUrl url)
- {
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- if (QNetworkAccessManager::Accessible != network_manager_->networkAccessible ())
- {
- // try and recover network access for QNAM
- network_manager_->setNetworkAccessible (QNetworkAccessManager::Accessible);
- }
-#endif
- LOG_INFO(QString("Download..."));
- QNetworkRequest request {url};
- request.setRawHeader ("User-Agent", "WSJT LotW User Downloader");
- request.setOriginatingObject (this);
-
- // this blocks for a second or two the first time it is used on
- // Windows - annoying
- if (!url_valid_)
- {
- reply_ = network_manager_->head (request);
- }
- else
- {
- reply_ = network_manager_->get (request);
- }
-
- connect (reply_.data (), &QNetworkReply::finished, this, &LotWUsers::impl::reply_finished);
- connect (reply_.data (), &QNetworkReply::readyRead, this, &LotWUsers::impl::store);
- }
-
- void reply_finished ()
- {
- LOG_INFO(QString("Finished..."));
- if (!reply_)
- {
- Q_EMIT self_->load_finished ();
- return; // we probably deleted it in an earlier call
- }
- QUrl redirect_url {reply_->attribute (QNetworkRequest::RedirectionTargetAttribute).toUrl ()};
- if (reply_->error () == QNetworkReply::NoError && !redirect_url.isEmpty ())
- {
- if ("https" == redirect_url.scheme () && !QSslSocket::supportsSsl ())
- {
- Q_EMIT self_->LotW_users_error (tr ("Network Error - SSL/TLS support not installed, cannot fetch:\n\'%1\'")
- .arg (redirect_url.toDisplayString ()));
- url_valid_ = false; // reset
- Q_EMIT self_->load_finished ();
- }
- else if (++redirect_count_ < 10) // maintain sanity
- {
- // follow redirect
- download (reply_->url ().resolved (redirect_url));
- }
- else
- {
- Q_EMIT self_->LotW_users_error (tr ("Network Error - Too many redirects:\n\'%1\'")
- .arg (redirect_url.toDisplayString ()));
- url_valid_ = false; // reset
- Q_EMIT self_->load_finished ();
- }
- }
- else if (reply_->error () != QNetworkReply::NoError)
- {
- csv_file_.cancelWriting ();
- csv_file_.commit ();
- url_valid_ = false; // reset
- // report errors that are not due to abort
- if (QNetworkReply::OperationCanceledError != reply_->error ())
- {
- Q_EMIT self_->LotW_users_error (tr ("Network Error:\n%1")
- .arg (reply_->errorString ()));
- }
- Q_EMIT self_->load_finished ();
- }
- else
- {
- if (url_valid_ && !csv_file_.commit ())
- {
- Q_EMIT self_->LotW_users_error (tr ("File System Error - Cannot commit changes to:\n\"%1\"")
- .arg (csv_file_.fileName ()));
- url_valid_ = false; // reset
- Q_EMIT self_->load_finished ();
- }
- else
- {
- if (!url_valid_)
- {
- // now get the body content
- url_valid_ = true;
- download (reply_->url ().resolved (redirect_url));
- }
- else
- {
- url_valid_ = false; // reset
- // load the database asynchronously
- future_load_ = std::async (std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_.fileName ());
- }
- }
- }
- if (reply_ && reply_->isFinished ())
- {
- reply_->deleteLater ();
- }
- }
-
- void store ()
- {
- if (url_valid_)
- {
- if (!csv_file_.isOpen ())
- {
- // create temporary file in the final location
- if (!csv_file_.open (QSaveFile::WriteOnly))
- {
- abort ();
- Q_EMIT self_->LotW_users_error (tr ("File System Error - Cannot open file:\n\"%1\"\nError(%2): %3")
- .arg (csv_file_.fileName ())
- .arg (csv_file_.error ())
- .arg (csv_file_.errorString ()));
- }
- }
- if (csv_file_.write (reply_->read (reply_->bytesAvailable ())) < 0)
- {
- abort ();
- Q_EMIT self_->LotW_users_error (tr ("File System Error - Cannot write to file:\n\"%1\"\nError(%2): %3")
- .arg (csv_file_.fileName ())
- .arg (csv_file_.error ())
- .arg (csv_file_.errorString ()));
- }
- }
- }
-
void abort ()
{
- if (reply_ && reply_->isRunning ())
- {
- reply_->abort ();
- }
+ lotw_downloader_.abort();
}
// Load the database from the given file name
@@ -225,12 +114,14 @@ public:
auto pos = l.indexOf (',');
result[l.left (pos)] = QDate::fromString (l.mid (pos + 1, l.indexOf (',', pos + 1) - pos - 1), "yyyy-MM-dd");
}
-// qDebug () << "LotW User Data Loaded";
}
else
{
throw std::runtime_error {QObject::tr ("Failed to open LotW users CSV file: '%1'").arg (f.fileName ()).toStdString ()};
}
+ LOG_INFO(QString{"LotWUsers: Loaded %1 records from %2"}.arg(result.size()).arg(lotw_csv_file));
+ Q_EMIT self_->progress (QString{"Loaded %1 records from LotW."}.arg(result.size()));
+ Q_EMIT self_->load_finished();
return result;
}
@@ -244,6 +135,8 @@ public:
std::future future_load_;
dictionary last_uploaded_;
qint64 age_constraint_; // days
+ FileDownload lotw_downloader_;
+ bool connected_;
};
#include "LotWUsers.moc"
@@ -252,6 +145,7 @@ LotWUsers::LotWUsers (QNetworkAccessManager * network_manager, QObject * parent)
: QObject {parent}
, m_ {this, network_manager}
{
+
}
LotWUsers::~LotWUsers ()
diff --git a/Network/LotWUsers.hpp b/Network/LotWUsers.hpp
index 238c57402..2d4d13075 100644
--- a/Network/LotWUsers.hpp
+++ b/Network/LotWUsers.hpp
@@ -31,6 +31,7 @@ public:
bool user (QString const& call) const;
Q_SIGNAL void LotW_users_error (QString const& reason) const;
+ Q_SIGNAL void progress (QString const& reason) const;
Q_SIGNAL void load_finished () const;
private:
diff --git a/logbook/AD1CCty.cpp b/logbook/AD1CCty.cpp
index 4112ddfb3..05a285430 100644
--- a/logbook/AD1CCty.cpp
+++ b/logbook/AD1CCty.cpp
@@ -19,6 +19,7 @@
#include "Configuration.hpp"
#include "Radio.hpp"
#include "pimpl_impl.hpp"
+#include "Logger.hpp"
#include "moc_AD1CCty.cpp"
@@ -163,6 +164,9 @@ public:
{
}
+ QString get_cty_path(const Configuration *configuration);
+ void load_cty(QFile &file);
+
entity_by_id::iterator lookup_entity (QString call, prefix const& p) const
{
call = call.toUpper ();
@@ -228,6 +232,7 @@ public:
Configuration const * configuration_;
QString path_;
+ QString cty_version_;
entities_type entities_;
prefixes_type prefixes_;
};
@@ -314,6 +319,72 @@ char const * AD1CCty::continent (Continent c)
}
}
+QString AD1CCty::impl::get_cty_path(Configuration const * configuration)
+{
+ QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)};
+ auto path = dataPath.exists (file_name)
+ ? dataPath.absoluteFilePath (file_name) // user override
+ : configuration->data_dir ().absoluteFilePath (file_name); // or original
+ return path;
+}
+
+void AD1CCty::impl::load_cty(QFile &file)
+{
+ int entity_id = 0;
+ int line_number{0};
+
+ entities_.clear();
+ prefixes_.clear();
+
+ QTextStream in{&file};
+ while (!in.atEnd())
+ {
+ auto const &entity_line = in.readLine();
+ ++line_number;
+ if (!in.atEnd())
+ {
+ auto const &entity_parts = entity_line.split(':');
+ if (entity_parts.size() >= 8)
+ {
+ auto primary_prefix = entity_parts[7].trimmed();
+ bool WAE_only{false};
+ if (primary_prefix.startsWith('*'))
+ {
+ primary_prefix = primary_prefix.mid(1);
+ WAE_only = true;
+ }
+ bool ok1, ok2, ok3, ok4, ok5;
+ entities_.emplace(++entity_id, entity_parts[0].trimmed(), WAE_only, entity_parts[1].trimmed().toInt(&ok1),
+ entity_parts[2].trimmed().toInt(&ok2), continent(entity_parts[3].trimmed()),
+ entity_parts[4].trimmed().toFloat(&ok3), entity_parts[5].trimmed().toFloat(&ok4),
+ static_cast (entity_parts[6].trimmed().toFloat(&ok5) * 60 * 60), primary_prefix);
+ if (!(ok1 && ok2 && ok3 && ok4 && ok5))
+ {
+ throw std::domain_error{"Invalid number in cty.dat line " + boost::lexical_cast(line_number)};
+ }
+ QString line;
+ QString detail;
+ do
+ {
+ in.readLineInto(&line);
+ ++line_number;
+ } while (detail += line, !detail.endsWith(';'));
+ for (auto prefix: detail.left(detail.size() - 1).split(','))
+ {
+ prefix = prefix.trimmed();
+ bool exact{false};
+ if (prefix.startsWith('='))
+ {
+ prefix = prefix.mid(1);
+ exact = true;
+ }
+ prefixes_.emplace(prefix, exact, entity_id);
+ }
+ }
+ }
+ }
+}
+
AD1CCty::AD1CCty (Configuration const * configuration)
: m_ {configuration}
{
@@ -321,69 +392,23 @@ AD1CCty::AD1CCty (Configuration const * configuration)
// TODO: G4WJS - consider doing the following asynchronously to
// speed up startup. Not urgent as it takes less than 0.5s on a Core
// i7 reading BIG CTY.DAT.
- QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
- m_->path_ = dataPath.exists (file_name)
- ? dataPath.absoluteFilePath (file_name) // user override
- : configuration->data_dir ().absoluteFilePath (file_name); // or original
+ AD1CCty::reload (configuration);
+ }
+
+void AD1CCty::reload(Configuration const * configuration)
+{
+ m_->path_ = m_->impl::get_cty_path(configuration);
QFile file {m_->path_};
+
+ LOG_INFO(QString{"Loading CTY.DAT from %1"}.arg (m_->path_));
+
if (file.open (QFile::ReadOnly))
- {
- int entity_id = 0;
- int line_number {0};
- QTextStream in {&file};
- while (!in.atEnd ())
- {
- auto const& entity_line = in.readLine ();
- ++line_number;
- if (!in.atEnd ())
- {
- auto const& entity_parts = entity_line.split (':');
- if (entity_parts.size () >= 8)
- {
- auto primary_prefix = entity_parts[7].trimmed ();
- bool WAE_only {false};
- if (primary_prefix.startsWith ('*'))
- {
- primary_prefix = primary_prefix.mid (1);
- WAE_only = true;
- }
- bool ok1, ok2, ok3, ok4, ok5;
- m_->entities_.emplace (++entity_id
- , entity_parts[0].trimmed ()
- , WAE_only
- , entity_parts[1].trimmed ().toInt (&ok1)
- , entity_parts[2].trimmed ().toInt (&ok2)
- , continent (entity_parts[3].trimmed ())
- , entity_parts[4].trimmed ().toFloat (&ok3)
- , entity_parts[5].trimmed ().toFloat (&ok4)
- , static_cast (entity_parts[6].trimmed ().toFloat (&ok5) * 60 * 60)
- , primary_prefix);
- if (!(ok1 && ok2 && ok3 && ok4 && ok5))
- {
- throw std::domain_error {"Invalid number in cty.dat line " + boost::lexical_cast (line_number)};
- }
- QString line;
- QString detail;
- do
- {
- in.readLineInto (&line);
- ++line_number;
- } while (detail += line, !detail.endsWith (';'));
- for (auto prefix : detail.left (detail.size () - 1).split (','))
- {
- prefix = prefix.trimmed ();
- bool exact {false};
- if (prefix.startsWith ('='))
- {
- prefix = prefix.mid (1);
- exact = true;
- }
- m_->prefixes_.emplace (prefix, exact, entity_id);
- }
- }
- }
- }
- }
+ {
+ m_->impl::load_cty(file);
+ m_->cty_version_ = AD1CCty::lookup("VERSION").entity_name;
+ Q_EMIT cty_loaded(m_->cty_version_);
+ LOG_INFO(QString{"Loaded CTY.DAT version %1"}.arg (m_->cty_version_));
+ }
}
AD1CCty::~AD1CCty ()
@@ -421,3 +446,7 @@ auto AD1CCty::lookup (QString const& call) const -> Record
}
return Record {};
}
+auto AD1CCty::version () const -> QString
+{
+ return m_->cty_version_;
+}
diff --git a/logbook/AD1CCty.hpp b/logbook/AD1CCty.hpp
index 4a485afa9..982ff7cec 100644
--- a/logbook/AD1CCty.hpp
+++ b/logbook/AD1CCty.hpp
@@ -42,8 +42,11 @@ public:
};
explicit AD1CCty (Configuration const *);
+ void reload(Configuration const * configuration);
~AD1CCty ();
Record lookup (QString const& call) const;
+ QString version () const;
+ Q_SIGNAL void cty_loaded (QString const& version) const;
private:
class impl;
diff --git a/logbook/WorkedBefore.cpp b/logbook/WorkedBefore.cpp
index e7214d41b..b5dedc3d2 100644
--- a/logbook/WorkedBefore.cpp
+++ b/logbook/WorkedBefore.cpp
@@ -23,6 +23,7 @@
#include
#include "Configuration.hpp"
#include "revision_utils.hpp"
+#include "Logger.hpp"
#include "qt_helpers.hpp"
#include "pimpl_impl.hpp"
@@ -225,7 +226,7 @@ namespace
{
auto const logFileName = "wsjtx_log.adi";
- // Expception class suitable for using with QtConcurrent across
+ // Exception class suitable for using with QtConcurrent across
// thread boundaries
class LoaderException final
: public QException
@@ -374,6 +375,7 @@ public:
void reload ()
{
+ prefixes_.reload (configuration_);
async_loader_ = QtConcurrent::run (loader, path_, &prefixes_);
loader_watcher_.setFuture (async_loader_);
}
@@ -402,11 +404,18 @@ WorkedBefore::WorkedBefore (Configuration const * configuration)
{
error = e.error ();
}
- Q_EMIT finished_loading (n, error);
+ QString cty_ver = m_->prefixes_.version();
+ LOG_DEBUG(QString{"WorkedBefore::reload: CTY.DAT version %1"}.arg (cty_ver));
+ Q_EMIT finished_loading (n, cty_ver, error);
});
reload ();
}
+QString WorkedBefore::cty_version () const
+{
+ return m_->prefixes_.version ();
+}
+
void WorkedBefore::reload ()
{
m_->reload ();
@@ -668,6 +677,7 @@ bool WorkedBefore::CQ_zone_worked (int CQ_zone, QString const& mode, QString con
}
}
+
bool WorkedBefore::ITU_zone_worked (int ITU_zone, QString const& mode, QString const& band) const
{
if (mode.size ())
@@ -699,3 +709,5 @@ bool WorkedBefore::ITU_zone_worked (int ITU_zone, QString const& mode, QString c
}
}
}
+
+
diff --git a/logbook/WorkedBefore.hpp b/logbook/WorkedBefore.hpp
index 1aae1aca4..0be9d783f 100644
--- a/logbook/WorkedBefore.hpp
+++ b/logbook/WorkedBefore.hpp
@@ -36,8 +36,9 @@ public:
bool continent_worked (Continent continent, QString const& mode, QString const& band) const;
bool CQ_zone_worked (int CQ_zone, QString const& mode, QString const& band) const;
bool ITU_zone_worked (int ITU_zone, QString const& mode, QString const& band) const;
+ QString cty_version () const;
- Q_SIGNAL void finished_loading (int worked_before_record_count, QString const& error) const;
+ Q_SIGNAL void finished_loading (int worked_before_record_count, QString const, QString const& error) const;
private:
class impl;
diff --git a/logbook/logbook.cpp b/logbook/logbook.cpp
index 2f2b70a5d..411d6240c 100644
--- a/logbook/logbook.cpp
+++ b/logbook/logbook.cpp
@@ -69,6 +69,11 @@ void LogBook::rescan ()
worked_before_.reload ();
}
+QString const LogBook::cty_version() const
+{
+ return worked_before_.cty_version();
+}
+
QByteArray LogBook::QSOToADIF (QString const& hisCall, QString const& hisGrid, QString const& mode,
QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn,
QDateTime const& dateTimeOff, QString const& band, QString const& comments,
diff --git a/logbook/logbook.h b/logbook/logbook.h
index de7ffae10..96a17dd6e 100644
--- a/logbook/logbook.h
+++ b/logbook/logbook.h
@@ -46,7 +46,9 @@ public:
QString const& m_myGrid, QString const& m_txPower, QString const& operator_call,
QString const& xSent, QString const& xRcvd, QString const& propmode);
- Q_SIGNAL void finished_loading (int worked_before_record_count, QString const& error) const;
+ QString const cty_version() const;
+
+ Q_SIGNAL void finished_loading (int worked_before_record_count, QString const cty_version, QString const& error) const;
CabrilloLog * contest_log ();
Multiplier const * multiplier () const;
diff --git a/translations/wsjtx_ca.ts b/translations/wsjtx_ca.ts
index db7c4fe94..e16426840 100644
--- a/translations/wsjtx_ca.ts
+++ b/translations/wsjtx_ca.ts
@@ -3684,7 +3684,7 @@ La llista es pot mantenir a la configuració (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
Log ADIF escanejat, %1 funcionava abans de la creació de registres
diff --git a/translations/wsjtx_da.ts b/translations/wsjtx_da.ts
index f9c632236..51d9d651b 100644
--- a/translations/wsjtx_da.ts
+++ b/translations/wsjtx_da.ts
@@ -3917,7 +3917,7 @@ listen. Makro listen kan også ændfres i Inderstillinger (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
Scannet ADIF log, %1 worked B4 oprettede poster
diff --git a/translations/wsjtx_en.ts b/translations/wsjtx_en.ts
index 8d5413a8a..dfec1d917 100644
--- a/translations/wsjtx_en.ts
+++ b/translations/wsjtx_en.ts
@@ -3637,7 +3637,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
diff --git a/translations/wsjtx_en_GB.ts b/translations/wsjtx_en_GB.ts
index fe223f710..4d1fd1f44 100644
--- a/translations/wsjtx_en_GB.ts
+++ b/translations/wsjtx_en_GB.ts
@@ -3637,7 +3637,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created
diff --git a/translations/wsjtx_es.ts b/translations/wsjtx_es.ts
index e2cc8caf7..3e79fdf4a 100644
--- a/translations/wsjtx_es.ts
+++ b/translations/wsjtx_es.ts
@@ -4237,7 +4237,7 @@ predefinida. La lista se puede modificar en "Ajustes" (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
Log ADIF escaneado, %1 funcionaba antes de la creación de registros
Log ADIF escaneado, %1 registros trabajados B4 creados
diff --git a/translations/wsjtx_it.ts b/translations/wsjtx_it.ts
index 590a15515..7f990d835 100644
--- a/translations/wsjtx_it.ts
+++ b/translations/wsjtx_it.ts
@@ -4112,7 +4112,7 @@ elenco. L'elenco può essere gestito in Impostazioni (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
Log ADIF scansionato,%1 ha funzionato prima della creazione dei record
diff --git a/translations/wsjtx_ja.ts b/translations/wsjtx_ja.ts
index e294cb461..ed9b26fad 100644
--- a/translations/wsjtx_ja.ts
+++ b/translations/wsjtx_ja.ts
@@ -3869,7 +3869,7 @@ ENTERを押してテキストを登録リストに追加.
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
ADIFログ検索. %1交信済み記録作成しました
diff --git a/translations/wsjtx_ru.ts b/translations/wsjtx_ru.ts
index 409a72a13..1f6bfe189 100644
--- a/translations/wsjtx_ru.ts
+++ b/translations/wsjtx_ru.ts
@@ -3690,7 +3690,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
Просканирован лог ADIF, %1 работал до создания записей
diff --git a/translations/wsjtx_zh.ts b/translations/wsjtx_zh.ts
index 93ef6c078..95f0bcf25 100644
--- a/translations/wsjtx_zh.ts
+++ b/translations/wsjtx_zh.ts
@@ -3683,7 +3683,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
扫描 ADIF 日志, %1 创建曾经通联记录
diff --git a/translations/wsjtx_zh_HK.ts b/translations/wsjtx_zh_HK.ts
index 86f599a31..0efa2fe31 100644
--- a/translations/wsjtx_zh_HK.ts
+++ b/translations/wsjtx_zh_HK.ts
@@ -3683,7 +3683,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created
掃描 ADIF 紀錄, %1 建立曾經通聯紀錄
diff --git a/translations/wsjtx_zh_TW.ts b/translations/wsjtx_zh_TW.ts
index a4b23d7d3..66f27f1e6 100644
--- a/translations/wsjtx_zh_TW.ts
+++ b/translations/wsjtx_zh_TW.ts
@@ -3714,7 +3714,7 @@ list. The list can be maintained in Settings (F2).
- Scanned ADIF log, %1 worked before records created
+ Scanned ADIF log, %1 worked-before records created. CTY: %2
掃描 ADIF 紀錄, %1 建立曾經通聯紀錄
diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp
index 44cbc28ef..7994e13e1 100644
--- a/widgets/mainwindow.cpp
+++ b/widgets/mainwindow.cpp
@@ -522,14 +522,15 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (this, &MainWindow::finished, m_logDlg.data (), &LogQSO::close);
// hook up the log book
- connect (&m_logBook, &LogBook::finished_loading, [this] (int record_count, QString const& error) {
+ connect (&m_logBook, &LogBook::finished_loading, [this] (int record_count, QString cty_version, QString const& error) {
if (error.size ())
{
MessageBox::warning_message (this, tr ("Error Scanning ADIF Log"), error);
}
else
{
- showStatusMessage (tr ("Scanned ADIF log, %1 worked before records created").arg (record_count));
+ m_config.set_CTY_DAT_version(cty_version);
+ showStatusMessage (tr ("Scanned ADIF log, %1 worked-before records created. CTY: %2. CTY: %2").arg (record_count).arg (cty_version));
}
});