Merge branch 'develop' into feat-boost-log

This commit is contained in:
Bill Somerville
2020-11-12 13:36:25 +00:00
50 changed files with 8148 additions and 6575 deletions
+148 -80
View File
@@ -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_};