mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2026-06-10 17:59:04 -04:00
Merge branch 'develop' into feat-boost-log
This commit is contained in:
+148
-80
@@ -6,16 +6,16 @@
|
||||
#include <limits>
|
||||
|
||||
#include <QUdpSocket>
|
||||
#include <QNetworkInterface>
|
||||
#include <QHostInfo>
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
#include <QByteArray>
|
||||
#include <QHostAddress>
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
|
||||
#include "NetworkMessage.hpp"
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "moc_MessageClient.cpp"
|
||||
@@ -34,14 +34,16 @@ class MessageClient::impl
|
||||
|
||||
public:
|
||||
impl (QString const& id, QString const& version, QString const& revision,
|
||||
port_type server_port, MessageClient * self)
|
||||
port_type server_port, int TTL, MessageClient * self)
|
||||
: self_ {self}
|
||||
, dns_lookup_id_ {-1}
|
||||
, enabled_ {false}
|
||||
, id_ {id}
|
||||
, version_ {version}
|
||||
, revision_ {revision}
|
||||
, dns_lookup_id_ {-1}
|
||||
, server_port_ {server_port}
|
||||
, TTL_ {TTL}
|
||||
, schema_ {2} // use 2 prior to negotiation not 1 which is broken
|
||||
, heartbeat_timer_ {new QTimer {this}}
|
||||
{
|
||||
@@ -49,9 +51,6 @@ public:
|
||||
connect (this, &QIODevice::readyRead, this, &impl::pending_datagrams);
|
||||
|
||||
heartbeat_timer_->start (NetworkMessage::pulse * 1000);
|
||||
|
||||
// bind to an ephemeral port
|
||||
bind ();
|
||||
}
|
||||
|
||||
~impl ()
|
||||
@@ -65,35 +64,37 @@ public:
|
||||
|
||||
enum StreamStatus {Fail, Short, OK};
|
||||
|
||||
void parse_message (QByteArray const& msg);
|
||||
void set_server (QString const& server_name, QStringList const& network_interface_names);
|
||||
Q_SLOT void host_info_results (QHostInfo);
|
||||
void start ();
|
||||
void parse_message (QByteArray const&);
|
||||
void pending_datagrams ();
|
||||
void heartbeat ();
|
||||
void closedown ();
|
||||
StreamStatus check_status (QDataStream const&) const;
|
||||
void send_message (QByteArray const&);
|
||||
void send_message (QDataStream const& out, QByteArray const& message)
|
||||
void send_message (QByteArray const&, bool queue_if_pending = true);
|
||||
void send_message (QDataStream const& out, QByteArray const& message, bool queue_if_pending = true)
|
||||
{
|
||||
if (OK == check_status (out))
|
||||
{
|
||||
send_message (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT self_->error ("Error creating UDP message");
|
||||
}
|
||||
if (OK == check_status (out))
|
||||
{
|
||||
send_message (message, queue_if_pending);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT self_->error ("Error creating UDP message");
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void host_info_results (QHostInfo);
|
||||
|
||||
MessageClient * self_;
|
||||
int dns_lookup_id_;
|
||||
bool enabled_;
|
||||
QString id_;
|
||||
QString version_;
|
||||
QString revision_;
|
||||
QString server_string_;
|
||||
port_type server_port_;
|
||||
int dns_lookup_id_;
|
||||
QHostAddress server_;
|
||||
port_type server_port_;
|
||||
int TTL_;
|
||||
std::vector<QNetworkInterface> network_interfaces_;
|
||||
quint32 schema_;
|
||||
QTimer * heartbeat_timer_;
|
||||
std::vector<QHostAddress> blocked_addresses_;
|
||||
@@ -105,37 +106,99 @@ public:
|
||||
|
||||
#include "MessageClient.moc"
|
||||
|
||||
void MessageClient::impl::set_server (QString const& server_name, QStringList const& network_interface_names)
|
||||
{
|
||||
// qDebug () << "MessageClient server:" << server_name << "port:" << server_port_ << "interfaces:" << network_interface_names;
|
||||
server_.setAddress (server_name);
|
||||
network_interfaces_.clear ();
|
||||
for (auto const& net_if_name : network_interface_names)
|
||||
{
|
||||
network_interfaces_.push_back (QNetworkInterface::interfaceFromName (net_if_name));
|
||||
}
|
||||
|
||||
if (server_.isNull () && server_name.size ()) // DNS lookup required
|
||||
{
|
||||
// queue a host address lookup
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
||||
dns_lookup_id_ = QHostInfo::lookupHost (server_name, this, &MessageClient::impl::host_info_results);
|
||||
#else
|
||||
dns_lookup_id_ = QHostInfo::lookupHost (server_name, this, SLOT (host_info_results (QHostInfo)));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
start ();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageClient::impl::host_info_results (QHostInfo host_info)
|
||||
{
|
||||
if (host_info.lookupId () != dns_lookup_id_) return;
|
||||
dns_lookup_id_ = -1;
|
||||
if (QHostInfo::NoError != host_info.error ())
|
||||
{
|
||||
Q_EMIT self_->error ("UDP server lookup failed:\n" + host_info.errorString ());
|
||||
pending_messages_.clear (); // discard
|
||||
Q_EMIT self_->error ("UDP server DNS lookup failed: " + host_info.errorString ());
|
||||
}
|
||||
else if (host_info.addresses ().size ())
|
||||
else
|
||||
{
|
||||
auto server = host_info.addresses ()[0];
|
||||
if (blocked_addresses_.end () == std::find (blocked_addresses_.begin (), blocked_addresses_.end (), server))
|
||||
auto const& server_addresses = host_info.addresses ();
|
||||
if (server_addresses.size ())
|
||||
{
|
||||
server_ = server;
|
||||
TRACE_UDP ("resulting server:" << server);
|
||||
|
||||
// send initial heartbeat which allows schema negotiation
|
||||
heartbeat ();
|
||||
|
||||
// clear any backlog
|
||||
while (pending_messages_.size ())
|
||||
{
|
||||
send_message (pending_messages_.dequeue ());
|
||||
}
|
||||
server_ = server_addresses[0];
|
||||
}
|
||||
else
|
||||
}
|
||||
start ();
|
||||
}
|
||||
|
||||
void MessageClient::impl::start ()
|
||||
{
|
||||
if (server_.isNull ())
|
||||
{
|
||||
Q_EMIT self_->close ();
|
||||
pending_messages_.clear (); // discard
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_broadcast_address (server_))
|
||||
{
|
||||
Q_EMIT self_->error ("IPv4 broadcast not supported, please specify the loop-back address, a server host address, or multicast group address");
|
||||
pending_messages_.clear (); // discard
|
||||
return;
|
||||
}
|
||||
|
||||
if (blocked_addresses_.end () != std::find (blocked_addresses_.begin (), blocked_addresses_.end (), server_))
|
||||
{
|
||||
Q_EMIT self_->error ("UDP server blocked, please try another");
|
||||
pending_messages_.clear (); // discard
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_UDP ("Trying server:" << server_.toString ());
|
||||
QHostAddress interface_addr {IPv6Protocol == server_.protocol () ? QHostAddress::AnyIPv6 : QHostAddress::AnyIPv4};
|
||||
|
||||
if (localAddress () != interface_addr)
|
||||
{
|
||||
if (UnconnectedState != state () || state ())
|
||||
{
|
||||
Q_EMIT self_->error ("UDP server blocked, please try another");
|
||||
pending_messages_.clear (); // discard
|
||||
close ();
|
||||
}
|
||||
// bind to an ephemeral port on the selected interface and set
|
||||
// up for sending datagrams
|
||||
bind (interface_addr);
|
||||
// qDebug () << "Bound to UDP port:" << localPort () << "on:" << localAddress ();
|
||||
|
||||
// set multicast TTL to limit scope when sending to multicast
|
||||
// group addresses
|
||||
setSocketOption (MulticastTtlOption, TTL_);
|
||||
}
|
||||
|
||||
// send initial heartbeat which allows schema negotiation
|
||||
heartbeat ();
|
||||
|
||||
// clear any backlog
|
||||
while (pending_messages_.size ())
|
||||
{
|
||||
send_message (pending_messages_.dequeue (), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,14 +426,11 @@ void MessageClient::impl::heartbeat ()
|
||||
if (server_port_ && !server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id_, schema_};
|
||||
hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
|
||||
<< version_.toUtf8 () << revision_.toUtf8 ();
|
||||
if (OK == check_status (hb))
|
||||
{
|
||||
TRACE_UDP ("schema:" << schema_ << "max schema:" << NetworkMessage::Builder::schema_number << "version:" << version_ << "revision:" << revision_);
|
||||
writeDatagram (message, server_, server_port_);
|
||||
}
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Heartbeat, id_, schema_};
|
||||
out << NetworkMessage::Builder::schema_number // maximum schema number accepted
|
||||
<< version_.toUtf8 () << revision_.toUtf8 ();
|
||||
TRACE_UDP ("schema:" << schema_ << "max schema:" << NetworkMessage::Builder::schema_number << "version:" << version_ << "revision:" << revision_);
|
||||
send_message (out, message, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,15 +440,12 @@ void MessageClient::impl::closedown ()
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Close, id_, schema_};
|
||||
if (OK == check_status (out))
|
||||
{
|
||||
TRACE_UDP ("");
|
||||
writeDatagram (message, server_, server_port_);
|
||||
}
|
||||
TRACE_UDP ("");
|
||||
send_message (out, message, false);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageClient::impl::send_message (QByteArray const& message)
|
||||
void MessageClient::impl::send_message (QByteArray const& message, bool queue_if_pending)
|
||||
{
|
||||
if (server_port_)
|
||||
{
|
||||
@@ -396,11 +453,25 @@ void MessageClient::impl::send_message (QByteArray const& message)
|
||||
{
|
||||
if (message != last_message_) // avoid duplicates
|
||||
{
|
||||
writeDatagram (message, server_, server_port_);
|
||||
if (is_multicast_address (server_))
|
||||
{
|
||||
// send datagram on each selected network interface
|
||||
std::for_each (network_interfaces_.begin (), network_interfaces_.end ()
|
||||
, [&] (QNetworkInterface const& net_if) {
|
||||
setMulticastInterface (net_if);
|
||||
// qDebug () << "Multicast UDP datagram sent to:" << server_ << "port:" << server_port_ << "on:" << multicastInterface ().humanReadableName ();
|
||||
writeDatagram (message, server_, server_port_);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// qDebug () << "Unicast UDP datagram sent to:" << server_ << "port:" << server_port_;
|
||||
writeDatagram (message, server_, server_port_);
|
||||
}
|
||||
last_message_ = message;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (queue_if_pending)
|
||||
{
|
||||
pending_messages_.enqueue (message);
|
||||
}
|
||||
@@ -433,9 +504,11 @@ auto MessageClient::impl::check_status (QDataStream const& stream) const -> Stre
|
||||
}
|
||||
|
||||
MessageClient::MessageClient (QString const& id, QString const& version, QString const& revision,
|
||||
QString const& server, port_type server_port, QObject * self)
|
||||
QString const& server_name, port_type server_port,
|
||||
QStringList const& network_interface_names,
|
||||
int TTL, QObject * self)
|
||||
: QObject {self}
|
||||
, m_ {id, version, revision, server_port, this}
|
||||
, m_ {id, version, revision, server_port, TTL, this}
|
||||
{
|
||||
connect (&*m_
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
@@ -454,8 +527,8 @@ MessageClient::MessageClient (QString const& id, QString const& version, QString
|
||||
#endif
|
||||
Q_EMIT error (m_->errorString ());
|
||||
}
|
||||
});
|
||||
set_server (server);
|
||||
});
|
||||
m_->set_server (server_name, network_interface_names);
|
||||
}
|
||||
|
||||
QHostAddress MessageClient::server_address () const
|
||||
@@ -468,20 +541,9 @@ auto MessageClient::server_port () const -> port_type
|
||||
return m_->server_port_;
|
||||
}
|
||||
|
||||
void MessageClient::set_server (QString const& server)
|
||||
void MessageClient::set_server (QString const& server_name, QStringList const& network_interface_names)
|
||||
{
|
||||
m_->server_.clear ();
|
||||
m_->server_string_ = server;
|
||||
if (server.size ())
|
||||
{
|
||||
// queue a host address lookup
|
||||
TRACE_UDP ("server host DNS lookup:" << server);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
|
||||
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, &MessageClient::impl::host_info_results);
|
||||
#else
|
||||
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo)));
|
||||
#endif
|
||||
}
|
||||
m_->set_server (server_name, network_interface_names);
|
||||
}
|
||||
|
||||
void MessageClient::set_server_port (port_type server_port)
|
||||
@@ -489,6 +551,12 @@ void MessageClient::set_server_port (port_type server_port)
|
||||
m_->server_port_ = server_port;
|
||||
}
|
||||
|
||||
void MessageClient::set_TTL (int TTL)
|
||||
{
|
||||
m_->TTL_ = TTL;
|
||||
m_->setSocketOption (QAbstractSocket::MulticastTtlOption, m_->TTL_);
|
||||
}
|
||||
|
||||
void MessageClient::enable (bool flag)
|
||||
{
|
||||
m_->enabled_ = flag;
|
||||
@@ -504,7 +572,7 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con
|
||||
, quint32 frequency_tolerance, quint32 tr_period
|
||||
, QString const& configuration_name)
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Status, m_->id_, m_->schema_};
|
||||
@@ -521,7 +589,7 @@ void MessageClient::decode (bool is_new, QTime time, qint32 snr, float delta_tim
|
||||
, QString const& mode, QString const& message_text, bool low_confidence
|
||||
, bool off_air)
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Decode, m_->id_, m_->schema_};
|
||||
@@ -536,7 +604,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt
|
||||
, qint32 drift, QString const& callsign, QString const& grid, qint32 power
|
||||
, bool off_air)
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::WSPRDecode, m_->id_, m_->schema_};
|
||||
@@ -549,7 +617,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt
|
||||
|
||||
void MessageClient::decodes_cleared ()
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Clear, m_->id_, m_->schema_};
|
||||
@@ -566,7 +634,7 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
|
||||
, QString const& my_grid, QString const& exchange_sent
|
||||
, QString const& exchange_rcvd, QString const& propmode)
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::QSOLogged, m_->id_, m_->schema_};
|
||||
@@ -581,7 +649,7 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
|
||||
|
||||
void MessageClient::logged_ADIF (QByteArray const& ADIF_record)
|
||||
{
|
||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||
if (m_->server_port_ && !m_->server_.isNull ())
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::LoggedADIF, m_->id_, m_->schema_};
|
||||
|
||||
Reference in New Issue
Block a user