Added UDP replies to halt Tx and set free text message

Also added Tx status to status UDP message.

Added   the    above   features   to   the    reference   UDP   server
message_aggregator.

Merged from the wsjtx-1.5 branch.



git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5334 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2015-05-06 20:30:29 +00:00
parent 1deffffe7f
commit 3b3ef37848
12 changed files with 170 additions and 11 deletions

View File

@ -402,6 +402,10 @@ set (all_C_and_CXXSRCS
${all_CXXSRCS} ${all_CXXSRCS}
) )
set (message_aggregator_STYLESHEETS
qss/default.qss
)
set (TOP_LEVEL_RESOURCES set (TOP_LEVEL_RESOURCES
shortcuts.txt shortcuts.txt
mouse_commands.txt mouse_commands.txt
@ -890,9 +894,13 @@ set_target_properties (wsjtx PROPERTIES
target_link_libraries (wsjtx wsjt_fort wsjt_cxx wsjt_qt ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES}) target_link_libraries (wsjtx wsjt_fort wsjt_cxx wsjt_qt ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
qt5_use_modules (wsjtx Widgets OpenGL Network Multimedia SerialPort) qt5_use_modules (wsjtx Widgets OpenGL Network Multimedia SerialPort)
add_resources (message_aggregator_RESOURCES /qss ${message_aggregator_STYLESHEETS})
configure_file (message_aggregator.qrc.in message_aggregator.qrc @ONLY)
qt5_add_resources (message_aggregator_RESOURCES_RCC ${CMAKE_BINARY_DIR}/message_aggregator.qrc)
add_executable (message_aggregator add_executable (message_aggregator
${message_aggregator_CXXSRCS} ${message_aggregator_CXXSRCS}
wsjtx.rc wsjtx.rc
${message_aggregator_RESOURCES_RCC}
) )
target_link_libraries (message_aggregator wsjt_qt) target_link_libraries (message_aggregator wsjt_qt)
qt5_use_modules (message_aggregator Widgets OpenGL Network) qt5_use_modules (message_aggregator Widgets OpenGL Network)

View File

@ -35,6 +35,7 @@
#include <exception> #include <exception>
#include <QtWidgets> #include <QtWidgets>
#include <QFile>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QStandardItem> #include <QStandardItem>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -51,6 +52,8 @@
using port_type = MessageServer::port_type; using port_type = MessageServer::port_type;
using Frequency = MessageServer::Frequency; using Frequency = MessageServer::Frequency;
QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
// //
// Decodes Model - simple data model for all decodes // Decodes Model - simple data model for all decodes
// //
@ -191,6 +194,8 @@ public:
: QDockWidget {id, parent} : QDockWidget {id, parent}
, id_ {id} , id_ {id}
, decodes_table_view_ {new QTableView} , decodes_table_view_ {new QTableView}
, message_line_edit_ {new QLineEdit}
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
, mode_label_ {new QLabel} , mode_label_ {new QLabel}
, dx_call_label_ {new QLabel} , dx_call_label_ {new QLabel}
, frequency_label_ {new QLabel} , frequency_label_ {new QLabel}
@ -207,6 +212,21 @@ public:
decodes_table_view_->hideColumn (0); decodes_table_view_->hideColumn (0);
content_layout->addWidget (decodes_table_view_); content_layout->addWidget (decodes_table_view_);
// set up controls
auto control_layout = new QHBoxLayout;
auto form_layout = new QFormLayout;
form_layout->addRow (tr ("Free text:"), message_line_edit_);
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
connect (message_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT do_free_text (id_, message_line_edit_->text ());
});
control_layout->addLayout (form_layout);
control_layout->addWidget (halt_tx_button_);
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
Q_EMIT do_halt_tx (id_);
});
content_layout->addLayout (control_layout);
// set up status area // set up status area
auto status_bar = new QStatusBar; auto status_bar = new QStatusBar;
status_bar->addPermanentWidget (mode_label_); status_bar->addPermanentWidget (mode_label_);
@ -232,7 +252,7 @@ public:
} }
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode) , QString const& report, QString const& tx_mode, bool transmitting)
{ {
if (id == id_) if (id == id_)
{ {
@ -240,6 +260,8 @@ public:
dx_call_label_->setText ("DX CALL: " + dx_call); dx_call_label_->setText ("DX CALL: " + dx_call);
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f)); frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
report_label_->setText ("SNR: " + report); report_label_->setText ("SNR: " + report);
update_dynamic_property (frequency_label_, "transmitting", transmitting);
halt_tx_button_->setEnabled (transmitting);
} }
} }
@ -256,6 +278,8 @@ public:
} }
Q_SIGNAL void do_reply (QModelIndex const&); Q_SIGNAL void do_reply (QModelIndex const&);
Q_SIGNAL void do_halt_tx (QString const& id);
Q_SIGNAL void do_free_text (QString const& id, QString const& text);
private: private:
class DecodesFilterModel final class DecodesFilterModel final
@ -280,6 +304,9 @@ private:
QString id_; QString id_;
QTableView * decodes_table_view_; QTableView * decodes_table_view_;
QLineEdit * message_line_edit_;
QAbstractButton * set_free_text_button_;
QAbstractButton * halt_tx_button_;
QLabel * mode_label_; QLabel * mode_label_;
QLabel * dx_call_label_; QLabel * dx_call_label_;
QLabel * frequency_label_; QLabel * frequency_label_;
@ -406,6 +433,8 @@ private:
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 (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_free_text, server_, &MessageServer::free_text);
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible); connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
dock_widgets_[id] = dock; dock_widgets_[id] = dock;
server_->replay (id); server_->replay (id);
@ -444,6 +473,15 @@ int main (int argc, char * argv[])
app.setApplicationName ("WSJT-X Reference UDP Message Aggregator Server"); app.setApplicationName ("WSJT-X Reference UDP Message Aggregator Server");
app.setApplicationVersion ("1.0"); app.setApplicationVersion ("1.0");
{
QFile file {":/qss/default.qss"};
if (!file.open (QFile::ReadOnly))
{
throw_qstring ("failed to open \"" + file.fileName () + "\": " + file.errorString ());
}
app.setStyleSheet (file.readAll());
}
MainWindow window; MainWindow window;
return app.exec (); return app.exec ();
} }

View File

@ -123,6 +123,22 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
} }
break; break;
case NetworkMessage::HaltTx:
if (check_status (in))
{
Q_EMIT self_->halt_tx ();
}
break;
case NetworkMessage::FreeText:
if (check_status (in))
{
QByteArray message;
in >> message;
Q_EMIT self_->free_text (QString::fromUtf8 (message));
}
break;
default: default:
// Ignore // Ignore
break; break;
@ -235,13 +251,14 @@ void MessageClient::send_raw_datagram (QByteArray const& message, QHostAddress c
} }
void MessageClient::status_update (Frequency f, QString const& mode, QString const& dx_call void MessageClient::status_update (Frequency f, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode) , QString const& report, QString const& tx_mode, bool transmitting)
{ {
if (m_->server_port_ && !m_->server_.isNull ()) if (m_->server_port_ && !m_->server_.isNull ())
{ {
QByteArray message; QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Status, m_->id_}; NetworkMessage::Builder out {&message, NetworkMessage::Status, m_->id_};
out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 (); out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 ()
<< transmitting;
if (m_->check_status (out)) if (m_->check_status (out))
{ {
m_->writeDatagram (message, m_->server_, m_->server_port_); m_->writeDatagram (message, m_->server_, m_->server_port_);

View File

@ -46,7 +46,7 @@ public:
// outgoing messages // outgoing messages
Q_SLOT void status_update (Frequency, QString const& mode, QString const& dx_call, QString const& report Q_SLOT void status_update (Frequency, QString const& mode, QString const& dx_call, QString const& report
, QString const& tx_mode); , QString const& tx_mode, bool transmitting);
Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
, QString const& mode, QString const& message); , QString const& mode, QString const& message);
Q_SLOT void clear_decodes (); Q_SLOT void clear_decodes ();
@ -69,6 +69,14 @@ public:
// all decodes // all decodes
Q_SIGNAL void replay (); Q_SIGNAL void replay ();
// this signal is emitted if the server has requested transmission
// to halt immediately
Q_SIGNAL void halt_tx ();
// this signal is emitted if the server has requested a new free
// message text
Q_SIGNAL void free_text (QString const&);
// this signal is emitted when network errors occur or if a host // this signal is emitted when network errors occur or if a host
// lookup fails // lookup fails
Q_SIGNAL void error (QString const&) const; Q_SIGNAL void error (QString const&) const;

View File

@ -142,11 +142,13 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
QByteArray dx_call; QByteArray dx_call;
QByteArray report; QByteArray report;
QByteArray tx_mode; QByteArray tx_mode;
in >> f >> mode >> dx_call >> report >> tx_mode; bool transmitting;
in >> f >> mode >> dx_call >> report >> tx_mode >> transmitting;
if (check_status (in)) if (check_status (in))
{ {
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call) Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
, QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)); , QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
, transmitting);
} }
} }
break; break;
@ -313,3 +315,32 @@ void MessageServer::replay (QString const& id)
} }
} }
} }
void MessageServer::halt_tx (QString const& id)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, id};
if (m_->check_status (out))
{
m_->writeDatagram (message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
}
void MessageServer::free_text (QString const& id, QString const& text)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, id};
out << text.toUtf8 ();
if (m_->check_status (out))
{
m_->writeDatagram (message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
}

View File

@ -48,11 +48,17 @@ public:
// ask the client with identification 'id' to replay all decodes // ask the client with identification 'id' to replay all decodes
Q_SLOT void replay (QString const& id); Q_SLOT void replay (QString const& id);
// ask the client with identification 'id' to halt transmitting immediately
Q_SLOT void halt_tx (QString const& id);
// ask the client with identification 'id' to set the free text message
Q_SLOT void free_text (QString const& id, QString const& text);
// the following signals are emitted when a client broadcasts the // the following signals are emitted when a client broadcasts the
// matching message // matching message
Q_SIGNAL void client_opened (QString const& id); Q_SIGNAL void client_opened (QString const& id);
Q_SIGNAL void status_update (QString const& id, Frequency, QString const& mode, QString const& dx_call Q_SIGNAL void status_update (QString const& id, Frequency, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode); , QString const& report, QString const& tx_mode, bool transmitting);
Q_SIGNAL void client_closed (QString const& id); Q_SIGNAL void client_closed (QString const& id);
Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time Q_SIGNAL void decode (bool is_new, QString const& 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);

View File

@ -43,6 +43,12 @@
* serialization purposes (currently a quint32 size followed by size * serialization purposes (currently a quint32 size followed by size
* bytes, no terminator is present or counted). * bytes, no terminator is present or counted).
* *
* The QDataStream format document linked above is not complete for
* the QByteArray serialization format, it is similar to the QString
* serialization format in that it differentiates between empty
* strings and null strings. Empty strings have a length of zero
* whereas null strings have a length field of 0xffffffff.
*
* Schema Version 1: * Schema Version 1:
* ----------------- * -----------------
* *
@ -58,6 +64,7 @@
* DX call utf8 * DX call utf8
* Report utf8 * Report utf8
* Tx Mode utf8 * Tx Mode utf8
* Transmitting bool
* *
* Decode Out 2 quint32 * Decode Out 2 quint32
* Id (unique key) utf8 * Id (unique key) utf8
@ -99,6 +106,13 @@
* *
* Replay In 7 quint32 * Replay In 7 quint32
* Id (unique key) utf8 * Id (unique key) utf8
*
* Halt Tx In 8
* Id (unique key) utf8
*
* Free Text In 9
* Id (unique key) utf8
* Text utf8
*/ */
#include <QDataStream> #include <QDataStream>
@ -122,6 +136,8 @@ namespace NetworkMessage
QSOLogged, QSOLogged,
Close, Close,
Replay, Replay,
HaltTx,
FreeText,
maximum_message_type_ // ONLY add new message types maximum_message_type_ // ONLY add new message types
// immediately before here // immediately before here
}; };

View File

@ -187,11 +187,19 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
connect (m_logDlg.data (), &LogQSO::acceptQSO, this, &MainWindow::acceptQSO2); connect (m_logDlg.data (), &LogQSO::acceptQSO, this, &MainWindow::acceptQSO2);
connect (this, &MainWindow::finished, m_logDlg.data (), &LogQSO::close); connect (this, &MainWindow::finished, m_logDlg.data (), &LogQSO::close);
// Network message handlers // Network message handlers
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::halt_tx, ui->stopTxButton, &QAbstractButton::click);
connect (m_messageClient, &MessageClient::error, this, &MainWindow::networkError); connect (m_messageClient, &MessageClient::error, this, &MainWindow::networkError);
connect (m_messageClient, &MessageClient::free_text, [this] (QString const& text) {
if (0 == ui->tabWidget->currentIndex ()) {
ui->tx5->setCurrentText (text);
ui->txrb5->click ();
} else {
ui->freeTextMsg->setCurrentText (text);
ui->rbFreeText->click ();
}});
on_EraseButton_clicked (); on_EraseButton_clicked ();
@ -1022,7 +1030,7 @@ void MainWindow::displayDialFrequency ()
void MainWindow::statusChanged() void MainWindow::statusChanged()
{ {
m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx); m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, m_transmitting);
QFile f {m_config.temp_dir ().absoluteFilePath ("wsjtx_status.txt")}; QFile f {m_config.temp_dir ().absoluteFilePath ("wsjtx_status.txt")};
if(f.open(QFile::WriteOnly | QIODevice::Text)) { if(f.open(QFile::WriteOnly | QIODevice::Text)) {
@ -1865,6 +1873,7 @@ void MainWindow::guiUpdate()
m_transmitting = true; m_transmitting = true;
transmitDisplay (true); transmitDisplay (true);
m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, m_transmitting);
} }
if(!m_btxok && btxok0 && g_iptt==1) stopTx(); if(!m_btxok && btxok0 && g_iptt==1) stopTx();
@ -1996,6 +2005,7 @@ void MainWindow::stopTx()
tx_status_label->setText(""); tx_status_label->setText("");
ptt0Timer->start(200); //Sequencer delay ptt0Timer->start(200); //Sequencer delay
monitor (true); monitor (true);
m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, m_transmitting);
} }
void MainWindow::stopTx2() void MainWindow::stopTx2()

View File

@ -0,0 +1,5 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>@message_aggregator_RESOURCES@
</qresource>
</RCC>

5
qss/default.qss Normal file
View File

@ -0,0 +1,5 @@
/* default stylesheet for the message aggregator application */
[transmitting="true"] {
background-color: yellow
}

View File

@ -2,6 +2,9 @@
#include <QString> #include <QString>
#include <QFont> #include <QFont>
#include <QWidget>
#include <QStyle>
#include <QVariant>
QString font_as_stylesheet (QFont const& font) QString font_as_stylesheet (QFont const& font)
{ {
@ -24,3 +27,11 @@ QString font_as_stylesheet (QFont const& font)
.arg (font.styleName ()) .arg (font.styleName ())
.arg (font_weight); .arg (font_weight);
} }
void update_dynamic_property (QWidget * widget, char const * property, QVariant const& value)
{
widget->setProperty (property, value);
widget->style ()->unpolish (widget);
widget->style ()->polish (widget);
widget->update ();
}

View File

@ -9,10 +9,10 @@
#include <QMetaType> #include <QMetaType>
#include <QMetaEnum> #include <QMetaEnum>
#include <QString> #include <QString>
#include <QByteArray>
#include <QDebug> #include <QDebug>
#include <QHostAddress> #include <QHostAddress>
#include <QHash>
class QVariant;
#define ENUM_QDATASTREAM_OPS_DECL(CLASS, ENUM) \ #define ENUM_QDATASTREAM_OPS_DECL(CLASS, ENUM) \
QDataStream& operator << (QDataStream&, CLASS::ENUM); \ QDataStream& operator << (QDataStream&, CLASS::ENUM); \
@ -72,6 +72,10 @@ void throw_qstring (QString const& qs)
QString font_as_stylesheet (QFont const&); QString font_as_stylesheet (QFont const&);
// do what is necessary to change a dynamic property and trigger any
// conditional style sheet updates
void update_dynamic_property (QWidget *, char const * property, QVariant const& value);
// Register some useful Qt types with QMetaType // Register some useful Qt types with QMetaType
Q_DECLARE_METATYPE (QHostAddress); Q_DECLARE_METATYPE (QHostAddress);