diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b07684e9..d9ff2be75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1491,7 +1491,7 @@ add_executable (message_aggregator ${message_aggregator_RESOURCES_RCC} ${message_aggregator_VERSION_RESOURCES} ) -target_link_libraries (message_aggregator Qt5::Widgets wsjtx_udp-static) +target_link_libraries (message_aggregator wsjt_qt Qt5::Widgets wsjtx_udp-static) if (WSJT_CREATE_WINMAIN) set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON) diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp index 7e74342c0..d110bfc44 100644 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ b/UDPExamples/MessageAggregatorMainWindow.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "DecodesModel.hpp" #include "BeaconsModel.hpp" @@ -37,8 +39,10 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () , 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} + , port_spin_box_ {new QSpinBox {this}} + , multicast_group_line_edit_ {new QLineEdit {this}} + , network_interfaces_combo_box_ {new CheckableItemComboBox {this}} + , log_table_view_ {new QTableView {this}} , 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}} @@ -68,16 +72,66 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () 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 ()); + 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 ("Port number:"), port_spin_box_); group_box_layout->addRow (tr ("Multicast Group (blank for unicast server):"), multicast_group_line_edit_); + group_box_layout->addRow (tr ("Network interfaces:"), network_interfaces_combo_box_); + int row; + QFormLayout::ItemRole role; + group_box_layout->getWidgetPosition (network_interfaces_combo_box_, &row, &role); + Q_ASSERT (row >= 0); + network_interfaces_form_label_widget_ = static_cast (group_box_layout->itemAt (row, QFormLayout::LabelRole)->widget ()); + network_interfaces_form_label_widget_->hide (); + network_interfaces_form_label_widget_->buddy ()->hide (); + connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this] { + if (multicast_group_line_edit_->text ().size ()) + { + network_interfaces_form_label_widget_->show (); + network_interfaces_form_label_widget_->buddy ()->show (); + } + else + { + network_interfaces_form_label_widget_->hide (); + network_interfaces_form_label_widget_->buddy ()->hide (); + } + }); auto group_box = new QGroupBox {tr ("Server Details")}; group_box->setLayout (group_box_layout); central_layout->addWidget (group_box); + // populate network interface list + for (auto const& net_if : QNetworkInterface::allInterfaces ()) + { + auto flags = QNetworkInterface::IsRunning | QNetworkInterface::CanMulticast; + if ((net_if.flags () & flags) == flags) + { + auto is_loopback = net_if.flags () & QNetworkInterface::IsLoopBack; + auto item = network_interfaces_combo_box_->addCheckItem (net_if.humanReadableName () + , net_if.name () + , is_loopback ? Qt::Checked : Qt::Unchecked); + item->setEnabled (!is_loopback); + auto tip = QString {"name(index): %1(%2) - %3"}.arg (net_if.name ()).arg (net_if.index ()) + .arg (net_if.flags () & QNetworkInterface::IsUp ? "Up" : "Down"); + auto hw_addr = net_if.hardwareAddress (); + if (hw_addr.size ()) + { + tip += QString {"\nhw: %1"}.arg (net_if.hardwareAddress ()); + } + auto aes = net_if.addressEntries (); + if (aes.size ()) + { + tip += "\naddresses:"; + for (auto const& ae : aes) + { + tip += QString {"\n ip: %1/%2"}.arg (ae.ip ().toString ()).arg (ae.prefixLength ()); + } + } + item->setToolTip (tip); + } + } + log_table_view_->setModel (log_); log_table_view_->verticalHeader ()->hide (); central_layout->addWidget (log_table_view_); @@ -200,16 +254,34 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () 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 ()}); - }); + connect (port_spin_box_, static_cast (&QSpinBox::valueChanged) + , [this] (int /*port*/) {restart_server ();}); + connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this] () {restart_server ();}); + connect (network_interfaces_combo_box_, &QComboBox::currentTextChanged, [this] () {restart_server ();}); - port_spin_box->setValue (2237); // start up in unicast mode + port_spin_box_->setValue (2237); // start up in unicast mode show (); } +void MessageAggregatorMainWindow::restart_server () +{ + QSet net_ifs; + if (network_interfaces_combo_box_->isVisible ()) + { + auto model = static_cast (network_interfaces_combo_box_->model ()); + for (int row = 0; row < model->rowCount (); ++row) + { + if (Qt::Checked == model->item (row)->checkState ()) + { + net_ifs << model->item (row)->data ().toString (); + } + } + } + server_->start (port_spin_box_->value () + , QHostAddress {multicast_group_line_edit_->text ()} + , net_ifs); +} + void MessageAggregatorMainWindow::log_qso (ClientKey const& /*key*/, QDateTime time_off , QString const& dx_call , QString const& dx_grid, Frequency dial_frequency, QString const& mode diff --git a/UDPExamples/MessageAggregatorMainWindow.hpp b/UDPExamples/MessageAggregatorMainWindow.hpp index 445a5908e..1bff0d692 100644 --- a/UDPExamples/MessageAggregatorMainWindow.hpp +++ b/UDPExamples/MessageAggregatorMainWindow.hpp @@ -6,6 +6,7 @@ #include #include "MessageServer.hpp" +#include "widgets/CheckableItemComboBox.hpp" class QDateTime; class QStandardItemModel; @@ -16,6 +17,8 @@ class QLineEdit; class QTableView; class ClientWidget; class QListWidget; +class QLabel; +class QSpinBox; using Frequency = MessageServer::Frequency; @@ -38,6 +41,7 @@ public: , QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode); private: + void restart_server (); void add_client (ClientKey const&, QString const& version, QString const& revision); void remove_client (ClientKey const&); void change_highlighting (QString const& call, QColor const& bg = QColor {}, QColor const& fg = QColor {}, @@ -52,7 +56,10 @@ private: DecodesModel * decodes_model_; BeaconsModel * beacons_model_; MessageServer * server_; + QSpinBox * port_spin_box_; QLineEdit * multicast_group_line_edit_; + CheckableItemComboBox * network_interfaces_combo_box_; + QLabel * network_interfaces_form_label_widget_; QTableView * log_table_view_; QListWidget * calls_of_interest_; QAction * add_call_of_interest_action_; diff --git a/UDPExamples/MessageServer.cpp b/UDPExamples/MessageServer.cpp index 8869c4b89..6745fe0a7 100644 --- a/UDPExamples/MessageServer.cpp +++ b/UDPExamples/MessageServer.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -78,7 +77,7 @@ public: QString version_; QString revision_; QHostAddress multicast_group_address_; - QStringList network_interfaces_; + QSet network_interfaces_; static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint; struct Client { @@ -433,9 +432,12 @@ MessageServer::MessageServer (QObject * parent, QString const& version, QString } void MessageServer::start (port_type port, QHostAddress const& multicast_group_address - , QStringList const& network_interface_names) + , QSet const& network_interface_names) { - if (port != m_->localPort () || multicast_group_address != m_->multicast_group_address_) + qDebug () << "MessageServer::start port:" << port << "multicast addr:" << multicast_group_address.toString () << "network interfaces:" << network_interface_names; + if (port != m_->localPort () + || multicast_group_address != m_->multicast_group_address_ + || network_interface_names != m_->network_interfaces_) { m_->leave_multicast_group (); if (impl::UnconnectedState != m_->state ()) diff --git a/UDPExamples/MessageServer.hpp b/UDPExamples/MessageServer.hpp index 8a31e7bd5..7cf5653a0 100644 --- a/UDPExamples/MessageServer.hpp +++ b/UDPExamples/MessageServer.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ public: // which the server will join Q_SLOT void start (port_type port , QHostAddress const& multicast_group_address = QHostAddress {} - , QStringList const& network_interface_names = QStringList {}); + , QSet const& network_interface_names = QSet {}); // ask the client to clear one or both of the decode windows Q_SLOT void clear_decodes (ClientKey const&, quint8 window = 0); diff --git a/UDPExamples/UDPDaemon.cpp b/UDPExamples/UDPDaemon.cpp index 2044bfb00..0f6793028 100644 --- a/UDPExamples/UDPDaemon.cpp +++ b/UDPExamples/UDPDaemon.cpp @@ -169,7 +169,7 @@ public: connect (server_, &MessageServer::client_opened, this, &Server::add_client); connect (server_, &MessageServer::client_closed, this, &Server::remove_client); - server_->start (port, multicast_group, network_interface_names); + server_->start (port, multicast_group, QSet {network_interface_names.begin (), network_interface_names.end ()}); } private: