From 4dfc4685e9c80bc3d8f66c90834dcdd73eaaec0a Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Sun, 3 Feb 2019 00:49:35 +0000 Subject: [PATCH] Make the UDP protocol Clear (3) message two-way External servers can clear either or both of the Band Activity and Rx Frequency decodes windows. This was requested by Dave, AA6YQ, so that DX Lab Suite applications can clear old decodes on band changes to ensure that decode highlighing is consistent. --- MessageClient.cpp | 14 +++++++++++- MessageClient.hpp | 6 ++++- MessageServer.cpp | 14 +++++++++++- MessageServer.hpp | 5 ++++- NetworkMessage.hpp | 14 ++++++++++-- UDPExamples/BeaconsModel.cpp | 2 +- UDPExamples/BeaconsModel.hpp | 2 +- UDPExamples/ClientWidget.cpp | 25 +++++++++++++++++++-- UDPExamples/ClientWidget.hpp | 7 +++++- UDPExamples/DecodesModel.cpp | 2 +- UDPExamples/DecodesModel.hpp | 2 +- UDPExamples/MessageAggregatorMainWindow.cpp | 11 ++++----- widgets/mainwindow.cpp | 13 ++++++++++- 13 files changed, 98 insertions(+), 19 deletions(-) diff --git a/MessageClient.cpp b/MessageClient.cpp index 7ccfbc3ce..873ba1fd4 100644 --- a/MessageClient.cpp +++ b/MessageClient.cpp @@ -188,6 +188,18 @@ void MessageClient::impl::parse_message (QByteArray const& msg) } break; + case NetworkMessage::Clear: + { + quint8 window {0}; + in >> window; + TRACE_UDP ("Clear window:" << window); + if (check_status (in) != Fail) + { + Q_EMIT self_->clear_decodes (window); + } + } + break; + case NetworkMessage::Replay: TRACE_UDP ("Replay"); if (check_status (in) != Fail) @@ -477,7 +489,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt } } -void MessageClient::clear_decodes () +void MessageClient::decodes_cleared () { if (m_->server_port_ && !m_->server_string_.isEmpty ()) { diff --git a/MessageClient.hpp b/MessageClient.hpp index 0deb1a49d..ca5f02037 100644 --- a/MessageClient.hpp +++ b/MessageClient.hpp @@ -59,7 +59,7 @@ public: Q_SLOT void WSPR_decode (bool is_new, QTime time, qint32 snr, float delta_time, Frequency , qint32 drift, QString const& callsign, QString const& grid, qint32 power , bool off_air); - Q_SLOT void clear_decodes (); + Q_SLOT void decodes_cleared (); Q_SLOT void qso_logged (QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power, QString const& comments @@ -80,6 +80,10 @@ public: // with send_raw_datagram() above) Q_SLOT void add_blocked_destination (QHostAddress const&); + // this signal is emitted if the server has requested a decode + // window clear action + Q_SIGNAL void clear_decodes (quint8 window); + // this signal is emitted if the server sends us a reply, the only // reply supported is reply to a prior CQ or QRZ message Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode diff --git a/MessageServer.cpp b/MessageServer.cpp index 491afdbdd..dadf3d984 100644 --- a/MessageServer.cpp +++ b/MessageServer.cpp @@ -224,7 +224,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s break; case NetworkMessage::Clear: - Q_EMIT self_->clear_decodes (id); + Q_EMIT self_->decodes_cleared (id); break; case NetworkMessage::Status: @@ -455,6 +455,18 @@ void MessageServer::start (port_type port, QHostAddress const& multicast_group_a } } +void MessageServer::clear_decodes (QString const& id, quint8 window) +{ + auto iter = m_->clients_.find (id); + if (iter != std::end (m_->clients_)) + { + QByteArray message; + NetworkMessage::Builder out {&message, NetworkMessage::Clear, id, (*iter).negotiated_schema_number_}; + out << window; + m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); + } +} + void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode , QString const& message_text, bool low_confidence, quint8 modifiers) diff --git a/MessageServer.hpp b/MessageServer.hpp index 4a6022bfd..12377bef9 100644 --- a/MessageServer.hpp +++ b/MessageServer.hpp @@ -41,6 +41,9 @@ public: Q_SLOT void start (port_type port, QHostAddress const& multicast_group_address = QHostAddress {}); + // ask the client to clear one or both of the decode windows + Q_SLOT void clear_decodes (QString const& id, quint8 window = 0); + // ask the client with identification 'id' to make the same action // as a double click on the decode would // @@ -91,7 +94,7 @@ public: , QString const& name, QDateTime time_on, QString const& operator_call , QString const& my_call, QString const& my_grid , QString const& exchange_sent, QString const& exchange_rcvd); - Q_SIGNAL void clear_decodes (QString const& id); + Q_SIGNAL void decodes_cleared (QString const& id); Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF); // this signal is emitted when a network error occurs diff --git a/NetworkMessage.hpp b/NetworkMessage.hpp index a4d9ba0d5..4783bb369 100644 --- a/NetworkMessage.hpp +++ b/NetworkMessage.hpp @@ -186,16 +186,26 @@ * back a .WAV file. * * - * Clear Out 3 quint32 + * Clear Out/In 3 quint32 * Id (unique key) utf8 + * Window quint8 (In only) * * This message is send when all prior "Decode" messages in the - * "Band activity" window have been discarded and therefore are + * "Band Activity" window have been discarded and therefore are * no long available for actioning with a "Reply" message. It is * sent when the user erases the "Band activity" window and when * WSJT-X closes down normally. The server should discard all * decode messages upon receipt of this message. * + * It may also be sent to a WSJT-X instance in which case it + * clears one or both of the "Band Activity" and "Rx Frequency" + * windows. The Window argument can be one of the following + * values: + * + * 0 - clear the "Band Activity" window (default) + * 1 - clear the "Rx Frequency" window + * 2 - clear both "Band Activity" and "Rx Frequency" windows + * * * Reply In 4 quint32 * Id (target unique key) utf8 diff --git a/UDPExamples/BeaconsModel.cpp b/UDPExamples/BeaconsModel.cpp index 8955ea26a..7ac1323a1 100644 --- a/UDPExamples/BeaconsModel.cpp +++ b/UDPExamples/BeaconsModel.cpp @@ -121,7 +121,7 @@ void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air)); } -void BeaconsModel::clear_decodes (QString const& client_id) +void BeaconsModel::decodes_cleared (QString const& client_id) { for (auto row = rowCount () - 1; row >= 0; --row) { diff --git a/UDPExamples/BeaconsModel.hpp b/UDPExamples/BeaconsModel.hpp index 977a93ccc..b089349cc 100644 --- a/UDPExamples/BeaconsModel.hpp +++ b/UDPExamples/BeaconsModel.hpp @@ -32,7 +32,7 @@ public: Q_SLOT void add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time , Frequency frequency, qint32 drift, QString const& callsign, QString const& grid , qint32 power, bool off_air); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); }; #endif diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp index 7423f6d81..1715670bf 100644 --- a/UDPExamples/ClientWidget.cpp +++ b/UDPExamples/ClientWidget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "validators/MaidenheadLocatorValidator.hpp" @@ -120,6 +121,9 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod , id_ {id} , calls_of_interest_ {calls_of_interest} , decodes_proxy_model_ {id_} + , erase_action_ {new QAction {tr ("&Erase Band Activity"), this}} + , erase_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}} + , erase_both_action_ {new QAction {tr ("Erase &Both"), this}} , decodes_table_view_ {new QTableView} , beacons_table_view_ {new QTableView} , message_line_edit_ {new QLineEdit} @@ -143,6 +147,10 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod decodes_table_view_->verticalHeader ()->hide (); decodes_table_view_->hideColumn (0); decodes_table_view_->horizontalHeader ()->setStretchLastSection (true); + decodes_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu); + decodes_table_view_->insertAction (nullptr, erase_action_); + decodes_table_view_->insertAction (nullptr, erase_rx_frequency_action_); + decodes_table_view_->insertAction (nullptr, erase_both_action_); auto form_layout = new QFormLayout; form_layout->addRow (tr ("Free text:"), message_line_edit_); @@ -171,6 +179,8 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod beacons_table_view_->verticalHeader ()->hide (); beacons_table_view_->hideColumn (0); beacons_table_view_->horizontalHeader ()->setStretchLastSection (true); + beacons_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu); + beacons_table_view_->insertAction (nullptr, erase_action_); auto beacons_page = new QWidget; auto beacons_layout = new QVBoxLayout {beacons_page}; @@ -219,8 +229,19 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod setAllowedAreas (Qt::BottomDockWidgetArea); setFloating (true); + // connect context menu actions + connect (erase_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_); + }); + connect (erase_rx_frequency_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_, 1); + }); + connect (erase_both_action_, &QAction::triggered, [this] (bool /*checked*/) { + Q_EMIT do_clear_decodes (id_, 2); + }); + // connect up table view signals - connect (decodes_table_view_, &QTableView::doubleClicked, this, [this] (QModelIndex const& index) { + connect (decodes_table_view_, &QTableView::doubleClicked, [this] (QModelIndex const& index) { Q_EMIT do_reply (decodes_proxy_model_.mapToSource (index), QApplication::keyboardModifiers () >> 24); }); @@ -313,7 +334,7 @@ void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, beacons_table_view_->scrollToBottom (); } -void ClientWidget::clear_decodes (QString const& client_id) +void ClientWidget::decodes_cleared (QString const& client_id) { if (client_id == id_) { diff --git a/UDPExamples/ClientWidget.hpp b/UDPExamples/ClientWidget.hpp index 0459ad036..22488531c 100644 --- a/UDPExamples/ClientWidget.hpp +++ b/UDPExamples/ClientWidget.hpp @@ -12,6 +12,7 @@ class QAbstractItemModel; class QModelIndex; class QColor; +class QAction; using Frequency = MessageServer::Frequency; @@ -41,8 +42,9 @@ public: , float delta_time, Frequency delta_frequency, qint32 drift , QString const& callsign, QString const& grid, qint32 power , bool off_air); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); + Q_SIGNAL void do_clear_decodes (QString const& id, quint8 window = 0); Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier); Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only); Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool); @@ -74,6 +76,9 @@ private: QRegularExpression base_call_re_; int rx_df_; } decodes_proxy_model_; + QAction * erase_action_; + QAction * erase_rx_frequency_action_; + QAction * erase_both_action_; QTableView * decodes_table_view_; QTableView * beacons_table_view_; QLineEdit * message_line_edit_; diff --git a/UDPExamples/DecodesModel.cpp b/UDPExamples/DecodesModel.cpp index 88b071c11..6121ff31a 100644 --- a/UDPExamples/DecodesModel.cpp +++ b/UDPExamples/DecodesModel.cpp @@ -125,7 +125,7 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time , off_air, is_fast)); } -void DecodesModel::clear_decodes (QString const& client_id) +void DecodesModel::decodes_cleared (QString const& client_id) { for (auto row = rowCount () - 1; row >= 0; --row) { diff --git a/UDPExamples/DecodesModel.hpp b/UDPExamples/DecodesModel.hpp index fa51732bf..17c9ae125 100644 --- a/UDPExamples/DecodesModel.hpp +++ b/UDPExamples/DecodesModel.hpp @@ -34,7 +34,7 @@ public: Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode, QString const& message , bool low_confidence, bool off_air, bool is_fast); - Q_SLOT void clear_decodes (QString const& client_id); + Q_SLOT void decodes_cleared (QString const& client_id); Q_SLOT void do_reply (QModelIndex const& source, quint8 modifiers); Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp index 0c2c51c30..e6937e6cb 100644 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ b/UDPExamples/MessageAggregatorMainWindow.cpp @@ -181,8 +181,8 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () }); connect (server_, &MessageServer::client_opened, this, &MessageAggregatorMainWindow::add_client); connect (server_, &MessageServer::client_closed, this, &MessageAggregatorMainWindow::remove_client); - connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::clear_decodes); - connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::clear_decodes); + connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::decodes_cleared); + connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::decodes_cleared); connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time , qint32 snr, float delta_time , quint32 delta_frequency, QString const& mode @@ -191,8 +191,8 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message , low_confidence, off_air, dock_widgets_[id]->fast_mode ());}); connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot); - connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes); - connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes); + connect (server_, &MessageServer::decodes_cleared, decodes_model_, &DecodesModel::decodes_cleared); + connect (server_, &MessageServer::decodes_cleared, beacons_model_, &BeaconsModel::decodes_cleared); connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply); // UI behaviour @@ -248,7 +248,8 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const& connect (server_, &MessageServer::status_update, dock, &ClientWidget::update_status); connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added); connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added); - connect (server_, &MessageServer::clear_decodes, dock, &ClientWidget::clear_decodes); + connect (server_, &MessageServer::decodes_cleared, dock, &ClientWidget::decodes_cleared); + connect (dock, &ClientWidget::do_clear_decodes, server_, &MessageServer::clear_decodes); connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply); connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx); connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text); diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 5a6665716..83830fb40 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -487,6 +487,17 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, }); // Network message handlers + connect (m_messageClient, &MessageClient::clear_decodes, [this] (quint8 window) { + ++window; + if (window & 1) + { + ui->decodedTextBrowser->erase (); + } + if (window & 2) + { + ui->decodedTextBrowser2->erase (); + } + }); connect (m_messageClient, &MessageClient::reply, this, &MainWindow::replyToCQ); connect (m_messageClient, &MessageClient::replay, this, &MainWindow::replayDecodes); connect (m_messageClient, &MessageClient::location, this, &MainWindow::locationChange); @@ -3310,7 +3321,7 @@ void MainWindow::on_EraseButton_clicked () void MainWindow::band_activity_cleared () { - m_messageClient->clear_decodes (); + m_messageClient->decodes_cleared (); QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt")); if(f.exists()) f.remove(); }