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.
This commit is contained in:
Bill Somerville 2019-02-03 00:49:35 +00:00
parent 846918e3aa
commit 4dfc4685e9
13 changed files with 98 additions and 19 deletions

View File

@ -188,6 +188,18 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
} }
break; 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: case NetworkMessage::Replay:
TRACE_UDP ("Replay"); TRACE_UDP ("Replay");
if (check_status (in) != Fail) 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 ()) if (m_->server_port_ && !m_->server_string_.isEmpty ())
{ {

View File

@ -59,7 +59,7 @@ public:
Q_SLOT void WSPR_decode (bool is_new, QTime time, qint32 snr, float delta_time, Frequency 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 , qint32 drift, QString const& callsign, QString const& grid, qint32 power
, bool off_air); , 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 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 , Frequency dial_frequency, QString const& mode, QString const& report_sent
, QString const& report_received, QString const& tx_power, QString const& comments , QString const& report_received, QString const& tx_power, QString const& comments
@ -80,6 +80,10 @@ public:
// with send_raw_datagram() above) // with send_raw_datagram() above)
Q_SLOT void add_blocked_destination (QHostAddress const&); 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 // this signal is emitted if the server sends us a reply, the only
// reply supported is reply to a prior CQ or QRZ message // 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 Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode

View File

@ -224,7 +224,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
break; break;
case NetworkMessage::Clear: case NetworkMessage::Clear:
Q_EMIT self_->clear_decodes (id); Q_EMIT self_->decodes_cleared (id);
break; break;
case NetworkMessage::Status: 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 void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode , quint32 delta_frequency, QString const& mode
, QString const& message_text, bool low_confidence, quint8 modifiers) , QString const& message_text, bool low_confidence, quint8 modifiers)

View File

@ -41,6 +41,9 @@ public:
Q_SLOT void start (port_type port, Q_SLOT void start (port_type port,
QHostAddress const& multicast_group_address = QHostAddress {}); 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 // ask the client with identification 'id' to make the same action
// as a double click on the decode would // 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& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid , QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd); , 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); Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF);
// this signal is emitted when a network error occurs // this signal is emitted when a network error occurs

View File

@ -186,16 +186,26 @@
* back a .WAV file. * back a .WAV file.
* *
* *
* Clear Out 3 quint32 * Clear Out/In 3 quint32
* Id (unique key) utf8 * Id (unique key) utf8
* Window quint8 (In only)
* *
* This message is send when all prior "Decode" messages in the * 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 * no long available for actioning with a "Reply" message. It is
* sent when the user erases the "Band activity" window and when * sent when the user erases the "Band activity" window and when
* WSJT-X closes down normally. The server should discard all * WSJT-X closes down normally. The server should discard all
* decode messages upon receipt of this message. * 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 * Reply In 4 quint32
* Id (target unique key) utf8 * Id (target unique key) utf8

View File

@ -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)); 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) for (auto row = rowCount () - 1; row >= 0; --row)
{ {

View File

@ -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 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 , Frequency frequency, qint32 drift, QString const& callsign, QString const& grid
, qint32 power, bool off_air); , qint32 power, bool off_air);
Q_SLOT void clear_decodes (QString const& client_id); Q_SLOT void decodes_cleared (QString const& client_id);
}; };
#endif #endif

View File

@ -2,6 +2,7 @@
#include <QRegExp> #include <QRegExp>
#include <QColor> #include <QColor>
#include <QAction>
#include "validators/MaidenheadLocatorValidator.hpp" #include "validators/MaidenheadLocatorValidator.hpp"
@ -120,6 +121,9 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
, id_ {id} , id_ {id}
, calls_of_interest_ {calls_of_interest} , calls_of_interest_ {calls_of_interest}
, decodes_proxy_model_ {id_} , 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} , decodes_table_view_ {new QTableView}
, beacons_table_view_ {new QTableView} , beacons_table_view_ {new QTableView}
, message_line_edit_ {new QLineEdit} , message_line_edit_ {new QLineEdit}
@ -143,6 +147,10 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
decodes_table_view_->verticalHeader ()->hide (); decodes_table_view_->verticalHeader ()->hide ();
decodes_table_view_->hideColumn (0); decodes_table_view_->hideColumn (0);
decodes_table_view_->horizontalHeader ()->setStretchLastSection (true); 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; auto form_layout = new QFormLayout;
form_layout->addRow (tr ("Free text:"), message_line_edit_); 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_->verticalHeader ()->hide ();
beacons_table_view_->hideColumn (0); beacons_table_view_->hideColumn (0);
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true); 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_page = new QWidget;
auto beacons_layout = new QVBoxLayout {beacons_page}; auto beacons_layout = new QVBoxLayout {beacons_page};
@ -219,8 +229,19 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
setAllowedAreas (Qt::BottomDockWidgetArea); setAllowedAreas (Qt::BottomDockWidgetArea);
setFloating (true); 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 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); 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 (); beacons_table_view_->scrollToBottom ();
} }
void ClientWidget::clear_decodes (QString const& client_id) void ClientWidget::decodes_cleared (QString const& client_id)
{ {
if (client_id == id_) if (client_id == id_)
{ {

View File

@ -12,6 +12,7 @@
class QAbstractItemModel; class QAbstractItemModel;
class QModelIndex; class QModelIndex;
class QColor; class QColor;
class QAction;
using Frequency = MessageServer::Frequency; using Frequency = MessageServer::Frequency;
@ -41,8 +42,9 @@ public:
, float delta_time, Frequency delta_frequency, qint32 drift , float delta_time, Frequency delta_frequency, qint32 drift
, QString const& callsign, QString const& grid, qint32 power , QString const& callsign, QString const& grid, qint32 power
, bool off_air); , 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_reply (QModelIndex const&, quint8 modifier);
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only); 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); Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
@ -74,6 +76,9 @@ private:
QRegularExpression base_call_re_; QRegularExpression base_call_re_;
int rx_df_; int rx_df_;
} decodes_proxy_model_; } decodes_proxy_model_;
QAction * erase_action_;
QAction * erase_rx_frequency_action_;
QAction * erase_both_action_;
QTableView * decodes_table_view_; QTableView * decodes_table_view_;
QTableView * beacons_table_view_; QTableView * beacons_table_view_;
QLineEdit * message_line_edit_; QLineEdit * message_line_edit_;

View File

@ -125,7 +125,7 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time
, off_air, is_fast)); , 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) for (auto row = rowCount () - 1; row >= 0; --row)
{ {

View File

@ -34,7 +34,7 @@ public:
Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time 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 , quint32 delta_frequency, QString const& mode, QString const& message
, bool low_confidence, bool off_air, bool is_fast); , 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_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 Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency

View File

@ -181,8 +181,8 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow ()
}); });
connect (server_, &MessageServer::client_opened, this, &MessageAggregatorMainWindow::add_client); connect (server_, &MessageServer::client_opened, this, &MessageAggregatorMainWindow::add_client);
connect (server_, &MessageServer::client_closed, this, &MessageAggregatorMainWindow::remove_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, decodes_model_, &DecodesModel::decodes_cleared);
connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::clear_decodes); connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::decodes_cleared);
connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time
, qint32 snr, float delta_time , qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode , 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 decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message
, low_confidence, off_air, dock_widgets_[id]->fast_mode ());}); , low_confidence, off_air, dock_widgets_[id]->fast_mode ());});
connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot); connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot);
connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes); connect (server_, &MessageServer::decodes_cleared, decodes_model_, &DecodesModel::decodes_cleared);
connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes); connect (server_, &MessageServer::decodes_cleared, beacons_model_, &BeaconsModel::decodes_cleared);
connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply); connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply);
// UI behaviour // 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::status_update, dock, &ClientWidget::update_status);
connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added); connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added);
connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_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_reply, decodes_model_, &DecodesModel::do_reply);
connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx); connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx);
connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text); connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text);

View File

@ -487,6 +487,17 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
}); });
// Network message handlers // 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::reply, this, &MainWindow::replyToCQ);
connect (m_messageClient, &MessageClient::replay, this, &MainWindow::replayDecodes); connect (m_messageClient, &MessageClient::replay, this, &MainWindow::replayDecodes);
connect (m_messageClient, &MessageClient::location, this, &MainWindow::locationChange); connect (m_messageClient, &MessageClient::location, this, &MainWindow::locationChange);
@ -3310,7 +3321,7 @@ void MainWindow::on_EraseButton_clicked ()
void MainWindow::band_activity_cleared () void MainWindow::band_activity_cleared ()
{ {
m_messageClient->clear_decodes (); m_messageClient->decodes_cleared ();
QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt")); QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt"));
if(f.exists()) f.remove(); if(f.exists()) f.remove();
} }