diff --git a/UDPExamples/BeaconsModel.cpp b/UDPExamples/BeaconsModel.cpp deleted file mode 100644 index 1302dc0..0000000 --- a/UDPExamples/BeaconsModel.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "BeaconsModel.hpp" - -#include -#include - -namespace -{ - char const * const headings[] = { - QT_TRANSLATE_NOOP ("BeaconsModel", "Client"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Time"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Snr"), - QT_TRANSLATE_NOOP ("BeaconsModel", "DT"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Frequency"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Drift"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Grid"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Power"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Live"), - QT_TRANSLATE_NOOP ("BeaconsModel", "Callsign"), - }; - - QString live_string (bool off_air) - { - return off_air ? QT_TRANSLATE_NOOP ("BeaconsModel", "no") : QT_TRANSLATE_NOOP ("BeaconsModel", "yes"); - } - - QFont text_font {"Courier", 10}; - - QList make_row (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) - { - auto time_item = new QStandardItem {time.toString ("hh:mm")}; - time_item->setData (time); - time_item->setTextAlignment (Qt::AlignRight); - - auto snr_item = new QStandardItem {QString::number (snr)}; - snr_item->setData (snr); - snr_item->setTextAlignment (Qt::AlignRight); - - auto dt = new QStandardItem {QString::number (delta_time)}; - dt->setData (delta_time); - dt->setTextAlignment (Qt::AlignRight); - - auto freq = new QStandardItem {Radio::pretty_frequency_MHz_string (frequency)}; - freq->setData (frequency); - freq->setTextAlignment (Qt::AlignRight); - - auto dri = new QStandardItem {QString::number (drift)}; - dri->setData (drift); - dri->setTextAlignment (Qt::AlignRight); - - auto gd = new QStandardItem {grid}; - gd->setTextAlignment (Qt::AlignRight); - - auto pwr = new QStandardItem {QString::number (power)}; - pwr->setData (power); - pwr->setTextAlignment (Qt::AlignRight); - - auto live = new QStandardItem {live_string (off_air)}; - live->setTextAlignment (Qt::AlignHCenter); - - QList row { - new QStandardItem {client_id}, time_item, snr_item, dt, freq, dri, gd, pwr, live, new QStandardItem {callsign}}; - Q_FOREACH (auto& item, row) - { - item->setEditable (false); - item->setFont (text_font); - item->setTextAlignment (item->textAlignment () | Qt::AlignVCenter); - } - return row; - } -} - -BeaconsModel::BeaconsModel (QObject * parent) - : QStandardItemModel {0, sizeof (headings) / sizeof (headings[0]), parent} -{ - int column {0}; - for (auto const& heading : headings) - { - setHeaderData (column++, Qt::Horizontal, tr (heading)); - } -} - -void BeaconsModel::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) -{ - if (!is_new) - { - int target_row {-1}; - for (auto row = 0; row < rowCount (); ++row) - { - if (data (index (row, 0)).toString () == client_id) - { - auto row_time = item (row, 1)->data ().toTime (); - if (row_time == time - && item (row, 2)->data ().toInt () == snr - && item (row, 3)->data ().toFloat () == delta_time - && item (row, 4)->data ().value () == frequency - && data (index (row, 5)).toInt () == drift - && data (index (row, 7)).toString () == grid - && data (index (row, 8)).toInt () == power - && data (index (row, 6)).toString () == live_string (off_air) - && data (index (row, 9)).toString () == callsign) - { - return; - } - if (time <= row_time) - { - target_row = row; // last row with same time - } - } - } - if (target_row >= 0) - { - insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air)); - return; - } - } - - appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air)); -} - -void BeaconsModel::clear_decodes (QString const& client_id) -{ - for (auto row = rowCount () - 1; row >= 0; --row) - { - if (data (index (row, 0)).toString () == client_id) - { - removeRow (row); - } - } -} - -#include "moc_BeaconsModel.cpp" diff --git a/UDPExamples/BeaconsModel.hpp b/UDPExamples/BeaconsModel.hpp deleted file mode 100644 index 977a93c..0000000 --- a/UDPExamples/BeaconsModel.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef WSJTX_UDP_BEACONS_MODEL_HPP__ -#define WSJTX_UDP_BEACONS_MODEL_HPP__ - -#include - -#include "MessageServer.hpp" - -using Frequency = MessageServer::Frequency; - -class QString; -class QTime; - -// -// Beacons Model - simple data model for all beacon spots -// -// The model is a basic table with uniform row format. Rows consist of -// QStandardItem instances containing the string representation of the -// column data and if the underlying field is not a string then the -// UserRole+1 role contains the underlying data item. -// -// Two slots are provided to add a new decode and remove all spots for -// a client. -// -class BeaconsModel - : public QStandardItemModel -{ - Q_OBJECT; - -public: - explicit BeaconsModel (QObject * parent = nullptr); - - 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); -}; - -#endif diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp deleted file mode 100644 index 06b09ba..0000000 --- a/UDPExamples/ClientWidget.cpp +++ /dev/null @@ -1,310 +0,0 @@ -#include "ClientWidget.hpp" - -#include -#include - -#include "MaidenheadLocatorValidator.hpp" - -namespace -{ - //QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"}; - QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>]*"}; - QRegularExpression cq_re {"(CQ|CQDX|QRZ)[^A-Z0-9/]+"}; - - 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 (); - } -} - -ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id) - : client_id_ {client_id} - , rx_df_ (-1) -{ -} - -QVariant ClientWidget::IdFilterModel::data (QModelIndex const& proxy_index, int role) const -{ - if (role == Qt::BackgroundRole) - { - switch (proxy_index.column ()) - { - case 8: // message - { - auto message = QSortFilterProxyModel::data (proxy_index).toString (); - if (base_call_re_.pattern ().size () - && message.contains (base_call_re_)) - { - return QColor {255,200,200}; - } - if (message.contains (cq_re)) - { - return QColor {200, 255, 200}; - } - } - break; - - case 4: // DF - if (qAbs (QSortFilterProxyModel::data (proxy_index).toInt () - rx_df_) <= 10) - { - return QColor {255, 200, 200}; - } - break; - - default: - break; - } - } - return QSortFilterProxyModel::data (proxy_index, role); -} - -bool ClientWidget::IdFilterModel::filterAcceptsRow (int source_row - , QModelIndex const& source_parent) const -{ - auto source_index_col0 = sourceModel ()->index (source_row, 0, source_parent); - return sourceModel ()->data (source_index_col0).toString () == client_id_; -} - -void ClientWidget::IdFilterModel::de_call (QString const& call) -{ - if (call != call_) - { - beginResetModel (); - if (call.size ()) - { - base_call_re_.setPattern ("[^A-Z0-9]*" + Radio::base_callsign (call) + "[^A-Z0-9]*"); - } - else - { - base_call_re_.setPattern (QString {}); - } - call_ = call; - endResetModel (); - } -} - -void ClientWidget::IdFilterModel::rx_df (int df) -{ - if (df != rx_df_) - { - beginResetModel (); - rx_df_ = df; - endResetModel (); - } -} - -namespace -{ - QString make_title (QString const& id, QString const& version, QString const& revision) - { - QString title {id}; - if (version.size ()) - { - title += QString {" v%1"}.arg (version); - } - if (revision.size ()) - { - title += QString {" (%1)"}.arg (revision); - } - return title; - } -} - -ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model - , QString const& id, QString const& version, QString const& revision - , QListWidget const * calls_of_interest, QWidget * parent) - : QDockWidget {make_title (id, version, revision), parent} - , id_ {id} - , calls_of_interest_ {calls_of_interest} - , decodes_proxy_model_ {id_} - , decodes_table_view_ {new QTableView} - , beacons_table_view_ {new QTableView} - , message_line_edit_ {new QLineEdit} - , grid_line_edit_ {new QLineEdit} - , decodes_stack_ {new QStackedLayout} - , auto_off_button_ {new QPushButton {tr ("&Auto Off")}} - , halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}} - , de_label_ {new QLabel} - , mode_label_ {new QLabel} - , fast_mode_ {false} - , frequency_label_ {new QLabel} - , dx_label_ {new QLabel} - , rx_df_label_ {new QLabel} - , tx_df_label_ {new QLabel} - , report_label_ {new QLabel} - , columns_resized_ {false} -{ - // set up widgets - decodes_proxy_model_.setSourceModel (decodes_model); - decodes_table_view_->setModel (&decodes_proxy_model_); - decodes_table_view_->verticalHeader ()->hide (); - decodes_table_view_->hideColumn (0); - decodes_table_view_->horizontalHeader ()->setStretchLastSection (true); - - auto form_layout = new QFormLayout; - form_layout->addRow (tr ("Free text:"), message_line_edit_); - form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_); - message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this}); - grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this}); - connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) { - Q_EMIT do_free_text (id_, text, false); - }); - connect (message_line_edit_, &QLineEdit::editingFinished, [this] () { - Q_EMIT do_free_text (id_, message_line_edit_->text (), true); - }); - connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () { - Q_EMIT location (id_, grid_line_edit_->text ()); - }); - - auto decodes_page = new QWidget; - auto decodes_layout = new QVBoxLayout {decodes_page}; - decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2}); - decodes_layout->addWidget (decodes_table_view_); - decodes_layout->addLayout (form_layout); - - auto beacons_proxy_model = new IdFilterModel {id_}; - beacons_proxy_model->setSourceModel (beacons_model); - beacons_table_view_->setModel (beacons_proxy_model); - beacons_table_view_->verticalHeader ()->hide (); - beacons_table_view_->hideColumn (0); - beacons_table_view_->horizontalHeader ()->setStretchLastSection (true); - - auto beacons_page = new QWidget; - auto beacons_layout = new QVBoxLayout {beacons_page}; - beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2}); - beacons_layout->addWidget (beacons_table_view_); - - decodes_stack_->addWidget (decodes_page); - decodes_stack_->addWidget (beacons_page); - - // stack alternative views - auto content_layout = new QVBoxLayout; - content_layout->setContentsMargins (QMargins {2, 2, 2, 2}); - content_layout->addLayout (decodes_stack_); - - // set up controls - auto control_button_box = new QDialogButtonBox; - control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole); - control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole); - connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) { - Q_EMIT do_halt_tx (id_, true); - }); - connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) { - Q_EMIT do_halt_tx (id_, false); - }); - content_layout->addWidget (control_button_box); - - // set up status area - auto status_bar = new QStatusBar; - status_bar->addPermanentWidget (de_label_); - status_bar->addPermanentWidget (mode_label_); - status_bar->addPermanentWidget (frequency_label_); - status_bar->addPermanentWidget (dx_label_); - status_bar->addPermanentWidget (rx_df_label_); - status_bar->addPermanentWidget (tx_df_label_); - status_bar->addPermanentWidget (report_label_); - content_layout->addWidget (status_bar); - connect (this, &ClientWidget::topLevelChanged, status_bar, &QStatusBar::setSizeGripEnabled); - - // set up central widget - auto content_widget = new QFrame; - content_widget->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken); - content_widget->setLayout (content_layout); - setWidget (content_widget); - // setMinimumSize (QSize {550, 0}); - setFeatures (DockWidgetMovable | DockWidgetFloatable); - setAllowedAreas (Qt::BottomDockWidgetArea); - setFloating (true); - - // connect up table view signals - connect (decodes_table_view_, &QTableView::doubleClicked, this, [this] (QModelIndex const& index) { - Q_EMIT do_reply (decodes_proxy_model_.mapToSource (index), QApplication::keyboardModifiers () >> 24); - }); - - // tell new client about calls of interest - for (int row = 0; row < calls_of_interest_->count (); ++row) - { - Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text (), QColor {Qt::blue}, QColor {Qt::yellow}); - } -} - -ClientWidget::~ClientWidget () -{ - for (int row = 0; row < calls_of_interest_->count (); ++row) - { - // tell client to forget calls of interest - Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text ()); - } -} - -void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call - , QString const& report, QString const& tx_mode, bool tx_enabled - , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df - , QString const& de_call, QString const& de_grid, QString const& dx_grid - , bool watchdog_timeout, QString const& sub_mode, bool fast_mode) -{ - if (id == id_) - { - fast_mode_ = fast_mode; - decodes_proxy_model_.de_call (de_call); - decodes_proxy_model_.rx_df (rx_df); - de_label_->setText (de_call.size () >= 0 ? QString {"DE: %1%2"}.arg (de_call) - .arg (de_grid.size () ? '(' + de_grid + ')' : QString {}) : QString {}); - mode_label_->setText (QString {"Mode: %1%2%3%4"} - .arg (mode) - .arg (sub_mode) - .arg (fast_mode && !mode.contains (QRegularExpression {R"(ISCAT|MSK144)"}) ? "fast" : "") - .arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')')); - frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f)); - dx_label_->setText (dx_call.size () >= 0 ? QString {"DX: %1%2"}.arg (dx_call) - .arg (dx_grid.size () ? '(' + dx_grid + ')' : QString {}) : QString {}); - rx_df_label_->setText (rx_df >= 0 ? QString {"Rx: %1"}.arg (rx_df) : ""); - tx_df_label_->setText (tx_df >= 0 ? QString {"Tx: %1"}.arg (tx_df) : ""); - report_label_->setText ("SNR: " + report); - update_dynamic_property (frequency_label_, "transmitting", transmitting); - auto_off_button_->setEnabled (tx_enabled); - halt_tx_button_->setEnabled (transmitting); - update_dynamic_property (mode_label_, "decoding", decoding); - update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout); - } -} - -void ClientWidget::decode_added (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*/) -{ - if (client_id == id_ && !columns_resized_) - { - decodes_stack_->setCurrentIndex (0); - decodes_table_view_->resizeColumnsToContents (); - columns_resized_ = true; - } - decodes_table_view_->scrollToBottom (); -} - -void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/ - , float /*delta_time*/, Frequency /*delta_frequency*/, qint32 /*drift*/ - , QString const& /*callsign*/, QString const& /*grid*/, qint32 /*power*/ - , bool /*off_air*/) -{ - if (client_id == id_ && !columns_resized_) - { - decodes_stack_->setCurrentIndex (1); - beacons_table_view_->resizeColumnsToContents (); - columns_resized_ = true; - } - beacons_table_view_->scrollToBottom (); -} - -void ClientWidget::clear_decodes (QString const& client_id) -{ - if (client_id == id_) - { - columns_resized_ = false; - } -} - -#include "moc_ClientWidget.cpp" diff --git a/UDPExamples/ClientWidget.hpp b/UDPExamples/ClientWidget.hpp deleted file mode 100644 index 29059bf..0000000 --- a/UDPExamples/ClientWidget.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__ -#define WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__ - -#include -#include -#include -#include -#include - -#include "MessageServer.hpp" - -class QAbstractItemModel; -class QModelIndex; -class QColor; - -using Frequency = MessageServer::Frequency; - -class ClientWidget - : public QDockWidget -{ - Q_OBJECT; - -public: - explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model - , QString const& id, QString const& version, QString const& revision - , QListWidget const * calls_of_interest, QWidget * parent = nullptr); - ~ClientWidget (); - - bool fast_mode () const {return fast_mode_;} - - Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call - , QString const& report, QString const& tx_mode, bool tx_enabled - , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df - , QString const& de_call, QString const& de_grid, QString const& dx_grid - , bool watchdog_timeout, QString const& sub_mode, bool fast_mode); - Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr - , float delta_time, quint32 delta_frequency, QString const& mode - , QString const& message, bool low_confidence, bool off_air); - Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime, qint32 snr - , 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_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); - Q_SIGNAL void location (QString const& id, QString const& text); - Q_SIGNAL void highlight_callsign (QString const& id, QString const& call - , QColor const& bg = QColor {}, QColor const& fg = QColor {} - , bool last_only = false); - -private: - QString id_; - QListWidget const * calls_of_interest_; - class IdFilterModel final - : public QSortFilterProxyModel - { - public: - IdFilterModel (QString const& client_id); - - void de_call (QString const&); - void rx_df (int); - - QVariant data (QModelIndex const& proxy_index, int role = Qt::DisplayRole) const override; - - protected: - bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override; - - private: - QString client_id_; - QString call_; - QRegularExpression base_call_re_; - int rx_df_; - } decodes_proxy_model_; - QTableView * decodes_table_view_; - QTableView * beacons_table_view_; - QLineEdit * message_line_edit_; - QLineEdit * grid_line_edit_; - QStackedLayout * decodes_stack_; - QAbstractButton * auto_off_button_; - QAbstractButton * halt_tx_button_; - QLabel * de_label_; - QLabel * mode_label_; - bool fast_mode_; - QLabel * frequency_label_; - QLabel * dx_label_; - QLabel * rx_df_label_; - QLabel * tx_df_label_; - QLabel * report_label_; - bool columns_resized_; -}; - -#endif diff --git a/UDPExamples/DecodesModel.cpp b/UDPExamples/DecodesModel.cpp deleted file mode 100644 index 5847e49..0000000 --- a/UDPExamples/DecodesModel.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "DecodesModel.hpp" - -#include -#include -#include -#include -#include -#include - -namespace -{ - char const * const headings[] = { - QT_TRANSLATE_NOOP ("DecodesModel", "Client"), - QT_TRANSLATE_NOOP ("DecodesModel", "Time"), - QT_TRANSLATE_NOOP ("DecodesModel", "Snr"), - QT_TRANSLATE_NOOP ("DecodesModel", "DT"), - QT_TRANSLATE_NOOP ("DecodesModel", "DF"), - QT_TRANSLATE_NOOP ("DecodesModel", "Md"), - QT_TRANSLATE_NOOP ("DecodesModel", "Confidence"), - QT_TRANSLATE_NOOP ("DecodesModel", "Live"), - QT_TRANSLATE_NOOP ("DecodesModel", "Message"), - }; - - QString confidence_string (bool low_confidence) - { - return low_confidence ? QT_TRANSLATE_NOOP ("DecodesModel", "low") : QT_TRANSLATE_NOOP ("DecodesModel", "high"); - } - - QString live_string (bool off_air) - { - return off_air ? QT_TRANSLATE_NOOP ("DecodesModel", "no") : QT_TRANSLATE_NOOP ("DecodesModel", "yes"); - } - - QFont text_font {"Courier", 10}; - - QList make_row (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) - { - auto time_item = new QStandardItem {time.toString (is_fast || "~" == mode ? "hh:mm:ss" : "hh:mm")}; - time_item->setData (time); - time_item->setTextAlignment (Qt::AlignRight); - - auto snr_item = new QStandardItem {QString::number (snr)}; - snr_item->setData (snr); - snr_item->setTextAlignment (Qt::AlignRight); - - auto dt = new QStandardItem {QString::number (delta_time)}; - dt->setData (delta_time); - dt->setTextAlignment (Qt::AlignRight); - - auto df = new QStandardItem {QString::number (delta_frequency)}; - df->setData (delta_frequency); - df->setTextAlignment (Qt::AlignRight); - - auto md = new QStandardItem {mode}; - md->setTextAlignment (Qt::AlignHCenter); - - auto confidence = new QStandardItem {confidence_string (low_confidence)}; - confidence->setTextAlignment (Qt::AlignHCenter); - - auto live = new QStandardItem {live_string (off_air)}; - live->setTextAlignment (Qt::AlignHCenter); - - QList row { - new QStandardItem {client_id}, time_item, snr_item, dt, df, md, confidence, live, new QStandardItem {message}}; - Q_FOREACH (auto& item, row) - { - item->setEditable (false); - item->setFont (text_font); - item->setTextAlignment (item->textAlignment () | Qt::AlignVCenter); - } - return row; - } -} - -DecodesModel::DecodesModel (QObject * parent) - : QStandardItemModel {0, sizeof (headings) / sizeof (headings[0]), parent} -{ - int column {0}; - for (auto const& heading : headings) - { - setHeaderData (column++, Qt::Horizontal, tr (heading)); - } -} - -void DecodesModel::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) -{ - if (!is_new) - { - int target_row {-1}; - for (auto row = 0; row < rowCount (); ++row) - { - if (data (index (row, 0)).toString () == client_id) - { - auto row_time = item (row, 1)->data ().toTime (); - if (row_time == time - && item (row, 2)->data ().toInt () == snr - && item (row, 3)->data ().toFloat () == delta_time - && item (row, 4)->data ().toUInt () == delta_frequency - && data (index (row, 5)).toString () == mode - && data (index (row, 7)).toString () == confidence_string (low_confidence) - && data (index (row, 6)).toString () == live_string (off_air) - && data (index (row, 8)).toString () == message) - { - return; - } - if (time <= row_time) - { - target_row = row; // last row with same time - } - } - } - if (target_row >= 0) - { - insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, delta_frequency, mode - , message, low_confidence, off_air, is_fast)); - return; - } - } - - appendRow (make_row (client_id, time, snr, delta_time, delta_frequency, mode, message, low_confidence - , off_air, is_fast)); -} - -void DecodesModel::clear_decodes (QString const& client_id) -{ - for (auto row = rowCount () - 1; row >= 0; --row) - { - if (data (index (row, 0)).toString () == client_id) - { - removeRow (row); - } - } -} - -void DecodesModel::do_reply (QModelIndex const& source, quint8 modifiers) -{ - auto row = source.row (); - Q_EMIT reply (data (index (row, 0)).toString () - , item (row, 1)->data ().toTime () - , item (row, 2)->data ().toInt () - , item (row, 3)->data ().toFloat () - , item (row, 4)->data ().toInt () - , data (index (row, 5)).toString () - , data (index (row, 8)).toString () - , confidence_string (true) == data (index (row, 7)).toString () - , modifiers); -} - -#include "moc_DecodesModel.cpp" diff --git a/UDPExamples/DecodesModel.hpp b/UDPExamples/DecodesModel.hpp deleted file mode 100644 index fa51732..0000000 --- a/UDPExamples/DecodesModel.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef WSJTX_UDP_DECODES_MODEL_HPP__ -#define WSJTX_UDP_DECODES_MODEL_HPP__ - -#include - -#include "MessageServer.hpp" - -using Frequency = MessageServer::Frequency; - -class QTime; -class QString; -class QModelIndex; - -// -// Decodes Model - simple data model for all decodes -// -// The model is a basic table with uniform row format. Rows consist of -// QStandardItem instances containing the string representation of the -// column data and if the underlying field is not a string then the -// UserRole+1 role contains the underlying data item. -// -// Three slots are provided to add a new decode, remove all decodes -// for a client and, to build a reply to CQ message for a given row -// which is emitted as a signal respectively. -// -class DecodesModel - : public QStandardItemModel -{ - Q_OBJECT; - -public: - explicit DecodesModel (QObject * parent = nullptr); - - 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 do_reply (QModelIndex const& source, quint8 modifiers); - - Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency - , QString const& mode, QString const& message, bool low_confidence, quint8 modifiers); -}; - -#endif diff --git a/UDPExamples/MessageAggregator.cpp b/UDPExamples/MessageAggregator.cpp deleted file mode 100644 index 81515ac..0000000 --- a/UDPExamples/MessageAggregator.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// MessageAggregator - an example application that utilizes the WSJT-X -// messaging facility -// -// This application is only provided as a simple GUI application -// example to demonstrate the WSJT-X messaging facility. It allows the -// user to set the server details either as a unicast UDP server or, -// if a multicast group address is provided, as a multicast server. -// The benefit of the multicast server is that multiple servers can be -// active at once each receiving all WSJT-X broadcast messages and -// each able to respond to individual WSJT_X clients. To utilize the -// multicast group features each WSJT-X client must set the same -// multicast group address as the UDP server address for example -// 239.255.0.0 for a site local multicast group. -// -// The UI is a small panel to input the service port number and -// optionally the multicast group address. Below that a table -// representing the log entries where any QSO logged messages -// broadcast from WSJT-X clients are displayed. The bottom of the -// application main window is a dock area where a dock window will -// appear for each WSJT-X client, this window contains a table of the -// current decode messages broadcast from that WSJT-X client and a -// status line showing the status update messages broadcast from the -// WSJT-X client. The dock windows may be arranged in a tab bar, side -// by side, below each other or, completely detached from the dock -// area as floating windows. Double clicking the dock window title bar -// or dragging and dropping with the mouse allows these different -// arrangements. -// -// The application also provides a simple menu bar including a view -// menu that allows each dock window to be hidden or revealed. -// - -#include -#include -#include - -#include -#include -#include -#include - -#include "MessageAggregatorMainWindow.hpp" - -// deduce the size of an array -template -inline -size_t size (T (&)[N]) {return N;} - -int main (int argc, char * argv[]) -{ - QApplication app {argc, argv}; - try - { - setlocale (LC_NUMERIC, "C"); // ensure number forms are in - // consistent format, do this after - // instantiating QApplication so - // that GUI has correct l18n - - app.setApplicationName ("WSJT-X Reference UDP Message Aggregator Server"); - app.setApplicationVersion ("1.0"); - - QObject::connect (&app, SIGNAL (lastWindowClosed ()), &app, SLOT (quit ())); - - { - QFile file {":/qss/default.qss"}; - if (!file.open (QFile::ReadOnly)) - { - throw std::runtime_error { - QString {"failed to open \"" + file.fileName () + "\": " + file.errorString ()} - .toLocal8Bit ().constData ()}; - } - app.setStyleSheet (file.readAll()); - } - - MessageAggregatorMainWindow window; - return app.exec (); - } - catch (std::exception const & e) - { - QMessageBox::critical (nullptr, app.applicationName (), e.what ()); - std::cerr << "Error: " << e.what () << '\n'; - } - catch (...) - { - QMessageBox::critical (nullptr, app.applicationName (), QObject::tr ("Unexpected error")); - std::cerr << "Unexpected error\n"; - } - return -1; -} diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp deleted file mode 100644 index 1d1dce5..0000000 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "MessageAggregatorMainWindow.hpp" - -#include -#include - -#include "DecodesModel.hpp" -#include "BeaconsModel.hpp" -#include "ClientWidget.hpp" - -using port_type = MessageServer::port_type; - -namespace -{ - char const * const headings[] = { - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Time On"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Time Off"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Callsign"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Grid"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Name"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Frequency"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Mode"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Sent"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Rec'd"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Power"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Operator"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Call"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Grid"), - QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Comments"), - }; -} - -MessageAggregatorMainWindow::MessageAggregatorMainWindow () - : log_ {new QStandardItemModel {0, 14, this}} - , decodes_model_ {new DecodesModel {this}} - , beacons_model_ {new BeaconsModel {this}} - , server_ {new MessageServer {this}} - , multicast_group_line_edit_ {new QLineEdit} - , log_table_view_ {new QTableView} - , add_call_of_interest_action_ {new QAction {tr ("&Add callsign"), this}} - , delete_call_of_interest_action_ {new QAction {tr ("&Delete callsign"), this}} - , last_call_of_interest_action_ {new QAction {tr ("&Highlight last only"), this}} - , call_of_interest_bg_colour_action_ {new QAction {tr ("&Background colour"), this}} - , call_of_interest_fg_colour_action_ {new QAction {tr ("&Foreground colour"), this}} -{ - // logbook - int column {0}; - for (auto const& heading : headings) - { - log_->setHeaderData (column++, Qt::Horizontal, tr (heading)); - } - connect (server_, &MessageServer::qso_logged, this, &MessageAggregatorMainWindow::log_qso); - - // menu bar - auto file_menu = menuBar ()->addMenu (tr ("&File")); - - auto exit_action = new QAction {tr ("E&xit"), this}; - exit_action->setShortcuts (QKeySequence::Quit); - exit_action->setToolTip (tr ("Exit the application")); - file_menu->addAction (exit_action); - connect (exit_action, &QAction::triggered, this, &MessageAggregatorMainWindow::close); - - view_menu_ = menuBar ()->addMenu (tr ("&View")); - - // central layout - auto central_layout = new QVBoxLayout; - - // server details - auto port_spin_box = new QSpinBox; - port_spin_box->setMinimum (1); - port_spin_box->setMaximum (std::numeric_limits::max ()); - auto group_box_layout = new QFormLayout; - group_box_layout->addRow (tr ("Port number:"), port_spin_box); - group_box_layout->addRow (tr ("Multicast Group (blank for unicast server):"), multicast_group_line_edit_); - auto group_box = new QGroupBox {tr ("Server Details")}; - group_box->setLayout (group_box_layout); - central_layout->addWidget (group_box); - - log_table_view_->setModel (log_); - log_table_view_->verticalHeader ()->hide (); - central_layout->addWidget (log_table_view_); - - // central widget - auto central_widget = new QWidget; - central_widget->setLayout (central_layout); - - // main window setup - setCentralWidget (central_widget); - setDockOptions (AnimatedDocks | AllowNestedDocks | AllowTabbedDocks); - setTabPosition (Qt::BottomDockWidgetArea, QTabWidget::North); - - QDockWidget * calls_dock {new QDockWidget {tr ("Calls of Interest"), this}}; - calls_dock->setAllowedAreas (Qt::RightDockWidgetArea); - calls_of_interest_ = new QListWidget {calls_dock}; - calls_of_interest_->setContextMenuPolicy (Qt::ActionsContextMenu); - calls_of_interest_->insertAction (nullptr, add_call_of_interest_action_); - connect (add_call_of_interest_action_, &QAction::triggered, [this] () { - auto item = new QListWidgetItem {}; - item->setFlags (item->flags () | Qt::ItemIsEditable); - item->setData (Qt::UserRole, QString {}); - calls_of_interest_->addItem (item); - calls_of_interest_->editItem (item); - }); - calls_of_interest_->insertAction (nullptr, delete_call_of_interest_action_); - connect (delete_call_of_interest_action_, &QAction::triggered, [this] () { - for (auto item : calls_of_interest_->selectedItems ()) - { - auto old_call = item->data (Qt::UserRole); - if (old_call.isValid ()) change_highlighting (old_call.toString ()); - delete item; - } - }); - calls_of_interest_->insertAction (nullptr, last_call_of_interest_action_); - connect (last_call_of_interest_action_, &QAction::triggered, [this] () { - for (auto item : calls_of_interest_->selectedItems ()) - { - auto old_call = item->data (Qt::UserRole); - change_highlighting (old_call.toString ()); - change_highlighting (old_call.toString () - , item->background ().color (), item->foreground ().color (), true); - delete item; - } - }); - calls_of_interest_->insertAction (nullptr, call_of_interest_bg_colour_action_); - connect (call_of_interest_bg_colour_action_, &QAction::triggered, [this] () { - for (auto item : calls_of_interest_->selectedItems ()) - { - auto old_call = item->data (Qt::UserRole); - auto new_colour = QColorDialog::getColor (item->background ().color () - , this, tr ("Select background color")); - if (new_colour.isValid ()) - { - change_highlighting (old_call.toString (), new_colour, item->foreground ().color ()); - item->setBackground (new_colour); - } - } - }); - calls_of_interest_->insertAction (nullptr, call_of_interest_fg_colour_action_); - connect (call_of_interest_fg_colour_action_, &QAction::triggered, [this] () { - for (auto item : calls_of_interest_->selectedItems ()) - { - auto old_call = item->data (Qt::UserRole); - auto new_colour = QColorDialog::getColor (item->foreground ().color () - , this, tr ("Select foreground color")); - if (new_colour.isValid ()) - { - change_highlighting (old_call.toString (), item->background ().color (), new_colour); - item->setForeground (new_colour); - } - } - }); - connect (calls_of_interest_, &QListWidget::itemChanged, [this] (QListWidgetItem * item) { - auto old_call = item->data (Qt::UserRole); - auto new_call = item->text ().toUpper (); - if (new_call != old_call) - { - // tell all clients - if (old_call.isValid ()) - { - change_highlighting (old_call.toString ()); - } - item->setData (Qt::UserRole, new_call); - item->setText (new_call); - auto bg = item->listWidget ()->palette ().text ().color (); - auto fg = item->listWidget ()->palette ().base ().color (); - item->setBackground (bg); - item->setForeground (fg); - change_highlighting (new_call, bg, fg); - } - }); - - calls_dock->setWidget (calls_of_interest_); - addDockWidget (Qt::RightDockWidgetArea, calls_dock); - view_menu_->addAction (calls_dock->toggleViewAction ()); - view_menu_->addSeparator (); - - // connect up server - connect (server_, &MessageServer::error, [this] (QString const& message) { - QMessageBox::warning (this, QApplication::applicationName (), tr ("Network Error"), message); - }); - 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::decode, [this] (bool is_new, QString const& id, QTime time - , qint32 snr, float delta_time - , quint32 delta_frequency, QString const& mode - , QString const& message, bool low_confidence - , bool off_air) { - 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 (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply); - - // UI behaviour - connect (port_spin_box, static_cast (&QSpinBox::valueChanged) - , [this] (port_type port) {server_->start (port);}); - connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this, port_spin_box] () { - server_->start (port_spin_box->value (), QHostAddress {multicast_group_line_edit_->text ()}); - }); - - port_spin_box->setValue (2237); // start up in unicast mode - show (); -} - -void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, 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 - , QString const& name, QDateTime time_on, QString const& operator_call - , QString const& my_call, QString const& my_grid) -{ - QList row; - row << new QStandardItem {time_on.toString ("dd-MMM-yyyy hh:mm:ss")} - << new QStandardItem {time_off.toString ("dd-MMM-yyyy hh:mm:ss")} - << new QStandardItem {dx_call} - << new QStandardItem {dx_grid} - << new QStandardItem {name} - << new QStandardItem {Radio::frequency_MHz_string (dial_frequency)} - << new QStandardItem {mode} - << new QStandardItem {report_sent} - << new QStandardItem {report_received} - << new QStandardItem {tx_power} - << new QStandardItem {operator_call} - << new QStandardItem {my_call} - << new QStandardItem {my_grid} - << new QStandardItem {comments}; - log_->appendRow (row); - log_table_view_->resizeColumnsToContents (); - log_table_view_->horizontalHeader ()->setStretchLastSection (true); - log_table_view_->scrollToBottom (); -} - -void MessageAggregatorMainWindow::add_client (QString const& id, QString const& version, QString const& revision) -{ - auto dock = new ClientWidget {decodes_model_, beacons_model_, id, version, revision, calls_of_interest_, this}; - dock->setAttribute (Qt::WA_DeleteOnClose); - auto view_action = dock->toggleViewAction (); - view_action->setEnabled (true); - view_menu_->addAction (view_action); - addDockWidget (Qt::BottomDockWidgetArea, dock); - 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 (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 (dock, &ClientWidget::location, server_, &MessageServer::location); - connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible); - connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign); - dock_widgets_[id] = dock; - server_->replay (id); // request decodes and status -} - -void MessageAggregatorMainWindow::remove_client (QString const& id) -{ - auto iter = dock_widgets_.find (id); - if (iter != std::end (dock_widgets_)) - { - (*iter)->close (); - dock_widgets_.erase (iter); - } -} - -MessageAggregatorMainWindow::~MessageAggregatorMainWindow () -{ - for (auto client : dock_widgets_) - { - delete client; - } -} - -void MessageAggregatorMainWindow::change_highlighting (QString const& call, QColor const& bg, QColor const& fg - , bool last_only) -{ - for (auto id : dock_widgets_.keys ()) - { - server_->highlight_callsign (id, call, bg, fg, last_only); - } -} - -#include "moc_MessageAggregatorMainWindow.cpp" diff --git a/UDPExamples/MessageAggregatorMainWindow.hpp b/UDPExamples/MessageAggregatorMainWindow.hpp deleted file mode 100644 index b699bee..0000000 --- a/UDPExamples/MessageAggregatorMainWindow.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef WSJTX_MESSAGE_AGGREGATOR_MAIN_WINDOW_MODEL_HPP__ -#define WSJTX_MESSAGE_AGGREGATOR_MAIN_WINDOW_MODEL_HPP__ - -#include -#include -#include - -#include "MessageServer.hpp" - -class QDateTime; -class QStandardItemModel; -class QMenu; -class DecodesModel; -class BeaconsModel; -class QLineEdit; -class QTableView; -class ClientWidget; -class QListWidget; - -using Frequency = MessageServer::Frequency; - -class MessageAggregatorMainWindow - : public QMainWindow -{ - Q_OBJECT; - -public: - MessageAggregatorMainWindow (); - ~MessageAggregatorMainWindow (); - - Q_SLOT void log_qso (QString const& /*id*/, 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 - , QString const& name, QDateTime time_on, QString const& operator_call - , QString const& my_call, QString const& my_grid); - -private: - void add_client (QString const& id, QString const& version, QString const& revision); - void remove_client (QString const& id); - void change_highlighting (QString const& call, QColor const& bg = QColor {}, QColor const& fg = QColor {}, - bool last_only = false); - - // maps client id to widgets - using ClientsDictionary = QHash; - ClientsDictionary dock_widgets_; - - QStandardItemModel * log_; - QMenu * view_menu_; - DecodesModel * decodes_model_; - BeaconsModel * beacons_model_; - MessageServer * server_; - QLineEdit * multicast_group_line_edit_; - QTableView * log_table_view_; - QListWidget * calls_of_interest_; - QAction * add_call_of_interest_action_; - QAction * delete_call_of_interest_action_; - QAction * last_call_of_interest_action_; - QAction * call_of_interest_bg_colour_action_; - QAction * call_of_interest_fg_colour_action_; -}; - -#endif diff --git a/UDPExamples/UDPDaemon.cpp b/UDPExamples/UDPDaemon.cpp deleted file mode 100644 index b75a8ad..0000000 --- a/UDPExamples/UDPDaemon.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// UDPDaemon - an example console application that utilizes the WSJT-X -// messaging facility -// -// This application is only provided as a simple console application -// example to demonstrate the WSJT-X messaging facility. It allows -// the user to set the server details either as a unicast UDP server -// or, if a multicast group address is provided, as a multicast -// server. The benefit of the multicast server is that multiple -// servers can be active at once each receiving all WSJT-X broadcast -// messages and each able to respond to individual WSJT_X clients. To -// utilize the multicast group features each WSJT-X client must set -// the same multicast group address as the UDP server address for -// example 239.255.0.0 for a site local multicast group. -// -// - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "MessageServer.hpp" -#include "Radio.hpp" - -#include "qt_helpers.hpp" - -using port_type = MessageServer::port_type; -using Frequency = MessageServer::Frequency; - -class Client - : public QObject -{ - Q_OBJECT - -public: - explicit Client (QString const& id, QObject * parent = nullptr) - : QObject {parent} - , id_ {id} - , dial_frequency_ {0u} - { - } - - Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& /*dx_call*/ - , QString const& /*report*/, QString const& /*tx_mode*/, bool /*tx_enabled*/ - , bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/ - , QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/ - , bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/) - { - if (id == id_) - { - if (f != dial_frequency_) - { - std::cout << tr ("%1: Dial frequency changed to %2").arg (id_).arg (f).toStdString () << std::endl; - dial_frequency_ = f; - } - if (mode + sub_mode != mode_) - { - std::cout << tr ("%1: Mode changed to %2").arg (id_).arg (mode + sub_mode).toStdString () << std::endl; - mode_ = mode + sub_mode; - } - } - } - - Q_SLOT void decode_added (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) - { - if (client_id == id_) - { - qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr - << "Dt:" << delta_time << "Df:" << delta_frequency - << "mode:" << mode << "Confidence:" << (low_confidence ? "low" : "high") - << "On air:" << !off_air; - std::cout << tr ("%1: Decoded %2").arg (id_).arg (message).toStdString () << std::endl; - } - } - - Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime time, qint32 snr - , float delta_time, Frequency delta_frequency, qint32 drift, QString const& callsign - , QString const& grid, qint32 power, bool off_air) - { - if (client_id == id_) - { - qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr - << "Dt:" << delta_time << "Df:" << delta_frequency - << "drift:" << drift; - std::cout << tr ("%1: WSPR decode %2 grid %3 power: %4").arg (id_).arg (callsign).arg (grid).arg (power).toStdString () - << "On air:" << !off_air << std::endl; - } - } - - Q_SLOT void qso_logged (QString const&client_id, 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, QString const& name, QDateTime time_on - , QString const& operator_call, QString const& my_call, QString const& my_grid) - { - if (client_id == id_) - { - qDebug () << "time_on:" << time_on << "time_off:" << time_off << "dx_call:" << dx_call << "grid:" << dx_grid - << "freq:" << dial_frequency << "mode:" << mode << "rpt_sent:" << report_sent - << "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments - << "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call - << "my_grid:" << my_grid; - std::cout << QByteArray {80, '-'}.data () << '\n'; - std::cout << tr ("%1: Logged %2 grid: %3 power: %4 sent: %5 recd: %6 freq: %7 time_off: %8 op: %9 my_call: %10 my_grid: %11") - .arg (id_).arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received) - .arg (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call) - .arg (my_call).arg (my_grid).toStdString () - << std::endl; - } - } - - Q_SLOT void logged_ADIF (QString const&client_id, QByteArray const& ADIF) - { - if (client_id == id_) - { - qDebug () << "ADIF:" << ADIF; - std::cout << QByteArray {80, '-'}.data () << '\n'; - std::cout << ADIF.data () << std::endl; - } - } - -private: - QString id_; - Frequency dial_frequency_; - QString mode_; -}; - -class Server - : public QObject -{ - Q_OBJECT - -public: - Server (port_type port, QHostAddress const& multicast_group) - : server_ {new MessageServer {this}} - { - // connect up server - connect (server_, &MessageServer::error, [this] (QString const& message) { - std::cerr << tr ("Network Error: %1").arg ( message).toStdString () << std::endl; - }); - connect (server_, &MessageServer::client_opened, this, &Server::add_client); - connect (server_, &MessageServer::client_closed, this, &Server::remove_client); - - server_->start (port, multicast_group); - } - -private: - void add_client (QString const& id, QString const& version, QString const& revision) - { - auto client = new Client {id}; - connect (server_, &MessageServer::status_update, client, &Client::update_status); - connect (server_, &MessageServer::decode, client, &Client::decode_added); - connect (server_, &MessageServer::WSPR_decode, client, &Client::beacon_spot_added); - connect (server_, &MessageServer::qso_logged, client, &Client::qso_logged); - connect (server_, &MessageServer::logged_ADIF, client, &Client::logged_ADIF); - clients_[id] = client; - server_->replay (id); - std::cout << "Discovered WSJT-X instance: " << id.toStdString (); - if (version.size ()) - { - std::cout << " v" << version.toStdString (); - } - if (revision.size ()) - { - std::cout << " (" << revision.toStdString () << ")"; - } - std::cout << std::endl; - } - - void remove_client (QString const& id) - { - auto iter = clients_.find (id); - if (iter != std::end (clients_)) - { - clients_.erase (iter); - (*iter)->deleteLater (); - } - std::cout << "Removed WSJT-X instance: " << id.toStdString () << std::endl; - } - - MessageServer * server_; - - // maps client id to clients - QHash clients_; -}; - -#include "UDPDaemon.moc" - -int main (int argc, char * argv[]) -{ - QCoreApplication app {argc, argv}; - try - { - setlocale (LC_NUMERIC, "C"); // ensure number forms are in - // consistent format, do this after - // instantiating QApplication so - // that GUI has correct l18n - - app.setApplicationName ("WSJT-X UDP Message Server Daemon"); - app.setApplicationVersion ("1.0"); - - QCommandLineParser parser; - parser.setApplicationDescription ("\nWSJT-X UDP Message Server Daemon."); - auto help_option = parser.addHelpOption (); - auto version_option = parser.addVersionOption (); - - QCommandLineOption port_option (QStringList {"p", "port"}, - app.translate ("UDPDaemon", - "Where is the UDP service port number to listen on.\n" - "The default service port is 2237."), - app.translate ("UDPDaemon", "PORT"), - "2237"); - parser.addOption (port_option); - - QCommandLineOption multicast_addr_option (QStringList {"g", "multicast-group"}, - app.translate ("UDPDaemon", - "Where is the multicast group to join.\n" - "The default is a unicast server listening on all interfaces."), - app.translate ("UDPDaemon", "GROUP")); - parser.addOption (multicast_addr_option); - - parser.process (app); - - Server server {static_cast (parser.value (port_option).toUInt ()), QHostAddress {parser.value (multicast_addr_option)}}; - - return app.exec (); - } - catch (std::exception const & e) - { - std::cerr << "Error: " << e.what () << '\n'; - } - catch (...) - { - std::cerr << "Unexpected error\n"; - } - return -1; -} diff --git a/UDPExamples/message_aggregator.qrc.in b/UDPExamples/message_aggregator.qrc.in deleted file mode 100644 index 792aa44..0000000 --- a/UDPExamples/message_aggregator.qrc.in +++ /dev/null @@ -1,5 +0,0 @@ - - - @message_aggregator_RESOURCES@ - - diff --git a/UDPExamples/message_aggregator.rc b/UDPExamples/message_aggregator.rc deleted file mode 100644 index a0c922e..0000000 --- a/UDPExamples/message_aggregator.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "../icons/windows-icons/ft8call.ico" diff --git a/UDPExamples/qss/default.qss b/UDPExamples/qss/default.qss deleted file mode 100644 index 4f4bdf4..0000000 --- a/UDPExamples/qss/default.qss +++ /dev/null @@ -1,13 +0,0 @@ -/* default stylesheet for the message aggregator application */ - -[transmitting="true"] { - background-color: yellow -} - -[decoding="true"] { - background-color: cyan -} - -[watchdog_timeout="true"] { - background-color: red -} \ No newline at end of file diff --git a/UDPExamples/udp_daemon.rc b/UDPExamples/udp_daemon.rc deleted file mode 100644 index a0c922e..0000000 --- a/UDPExamples/udp_daemon.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "../icons/windows-icons/ft8call.ico"