Initial working commit of tcp based API
This commit is contained in:
parent
640ce756c0
commit
2ff74626ba
@ -180,6 +180,7 @@ set (wsjt_qt_CXXSRCS
|
||||
NetworkMessage.cpp
|
||||
Message.cpp
|
||||
MessageClient.cpp
|
||||
MessageServer.cpp
|
||||
TCPClient.cpp
|
||||
LettersSpinBox.cpp
|
||||
HintedSpinBox.cpp
|
||||
|
@ -328,10 +328,12 @@ public:
|
||||
Q_SIGNAL void colors_changed ();
|
||||
|
||||
//
|
||||
// This signal is emitted when the UDP server changes
|
||||
// This signal is emitted when the UDP & TCP server changes
|
||||
//
|
||||
Q_SIGNAL void udp_server_changed (QString const& udp_server);
|
||||
Q_SIGNAL void udp_server_port_changed (port_type server_port);
|
||||
Q_SIGNAL void udp_server_changed (QString const& host);
|
||||
Q_SIGNAL void udp_server_port_changed (port_type port);
|
||||
Q_SIGNAL void tcp_server_changed (QString const& host);
|
||||
Q_SIGNAL void tcp_server_port_changed (port_type port);
|
||||
|
||||
// This signal is emitted when the band schedule changes
|
||||
Q_SIGNAL void band_schedule_changed (StationList &stations);
|
||||
|
15
Message.cpp
15
Message.cpp
@ -53,6 +53,21 @@ Message::Message(QString const &type, QString const &value, QMap<QString, QVari
|
||||
}
|
||||
}
|
||||
|
||||
int Message::ensureId(){
|
||||
// if a non-zero id exists, we're good
|
||||
if(params_.contains("_ID")){
|
||||
auto id = params_.value("_ID", 0).toInt();
|
||||
if(id != 0){
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, generate one
|
||||
auto id = DriftingDateTime::currentMSecsSinceEpoch()-EPOCH;
|
||||
params_["_ID"] = QString::number(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void Message::read(const QJsonObject &json){
|
||||
if(json.contains("type") && json["type"].isString()){
|
||||
type_ = json["type"].toString();
|
||||
|
@ -29,7 +29,8 @@ public:
|
||||
|
||||
QString value() const { return value_; }
|
||||
void setValue(QString value){ value_ = value; }
|
||||
|
||||
int id() const { return params_.value("_ID").toInt(); }
|
||||
int ensureId();
|
||||
QMap<QString, QVariant> params() const { return params_; }
|
||||
|
||||
private:
|
||||
|
@ -1,495 +1,186 @@
|
||||
#include "MessageServer.hpp"
|
||||
#include "MessageServer.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QUdpSocket>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QHash>
|
||||
|
||||
#include "Radio.hpp"
|
||||
#include "NetworkMessage.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "moc_MessageServer.cpp"
|
||||
|
||||
class MessageServer::impl
|
||||
: public QUdpSocket
|
||||
MessageServer::MessageServer(QObject *parent) :
|
||||
QTcpServer(parent)
|
||||
{
|
||||
Q_OBJECT;
|
||||
}
|
||||
|
||||
public:
|
||||
impl (MessageServer * self, QString const& version, QString const& revision)
|
||||
: self_ {self}
|
||||
, version_ {version}
|
||||
, revision_ {revision}
|
||||
, port_ {0u}
|
||||
, clock_ {new QTimer {this}}
|
||||
{
|
||||
// register the required types with Qt
|
||||
Radio::register_types ();
|
||||
MessageServer::~MessageServer(){
|
||||
stop();
|
||||
}
|
||||
|
||||
connect (this, &QIODevice::readyRead, this, &MessageServer::impl::pending_datagrams);
|
||||
connect (this, static_cast<void (impl::*) (SocketError)> (&impl::error)
|
||||
, [this] (SocketError /* e */)
|
||||
{
|
||||
Q_EMIT self_->error (errorString ());
|
||||
});
|
||||
connect (clock_, &QTimer::timeout, this, &impl::tick);
|
||||
clock_->start (NetworkMessage::pulse * 1000);
|
||||
}
|
||||
bool MessageServer::start()
|
||||
{
|
||||
if(isListening()){
|
||||
qDebug() << "MessageServer already listening:" << m_host << m_port;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum StreamStatus {Fail, Short, OK};
|
||||
auto address = QHostAddress();
|
||||
if(!address.setAddress(m_host)){
|
||||
qDebug() << "MessageServer address invalid:" << m_host << m_port;
|
||||
return false;
|
||||
}
|
||||
|
||||
void leave_multicast_group ();
|
||||
void join_multicast_group ();
|
||||
void parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg);
|
||||
void tick ();
|
||||
void pending_datagrams ();
|
||||
StreamStatus check_status (QDataStream const&) const;
|
||||
void send_message (QDataStream const& out, QByteArray const& message, QHostAddress const& address, port_type port)
|
||||
{
|
||||
if (OK == check_status (out))
|
||||
{
|
||||
writeDatagram (message, address, port);
|
||||
bool listening = listen(address, m_port);
|
||||
qDebug() << "MessageServer listening:" << listening << m_host << m_port;
|
||||
|
||||
return listening;
|
||||
}
|
||||
|
||||
void MessageServer::stop()
|
||||
{
|
||||
// disconnect all clients
|
||||
foreach(auto client, m_clients){
|
||||
client->close();
|
||||
}
|
||||
|
||||
// then close the server
|
||||
close();
|
||||
}
|
||||
|
||||
void MessageServer::setServer(QString host, quint16 port){
|
||||
bool listening = isListening();
|
||||
if(listening && (m_host != host || m_port != port)){
|
||||
stop();
|
||||
}
|
||||
|
||||
m_host = host;
|
||||
m_port = port;
|
||||
|
||||
if(listening){
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::setPause(bool paused)
|
||||
{
|
||||
m_paused = paused;
|
||||
|
||||
if(paused){
|
||||
pauseAccepting();
|
||||
} else {
|
||||
resumeAccepting();
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::send(const Message &message){
|
||||
foreach(auto client, m_clients){
|
||||
if(!client->awaitingResponse(message.id())){
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT self_->error ("Error creating UDP message");
|
||||
client->send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::incomingConnection(qintptr handle)
|
||||
{
|
||||
qDebug() << "MessageServer incomingConnection" << handle;
|
||||
|
||||
auto client = new Client(this, this);
|
||||
client->setSocket(handle);
|
||||
|
||||
#if JS8_MESSAGESERVER_IS_SINGLE_CLIENT
|
||||
while(!m_clients.isEmpty()){
|
||||
auto client = m_clients.first();
|
||||
client->close();
|
||||
m_clients.removeFirst();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_clients.append(client);
|
||||
}
|
||||
|
||||
Client::Client(MessageServer * server, QObject *parent):
|
||||
QObject(parent),
|
||||
m_server {server}
|
||||
{
|
||||
setConnected(true);
|
||||
}
|
||||
|
||||
void Client::setSocket(qintptr handle){
|
||||
m_socket = new QTcpSocket(this);
|
||||
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &Client::onDisconnected);
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &Client::readyRead);
|
||||
|
||||
m_socket->setSocketDescriptor(handle);
|
||||
}
|
||||
|
||||
void Client::setConnected(bool connected){
|
||||
m_connected = connected;
|
||||
}
|
||||
|
||||
void Client::close(){
|
||||
if(!m_socket){
|
||||
return;
|
||||
}
|
||||
|
||||
m_socket->close();
|
||||
m_socket = nullptr;
|
||||
}
|
||||
|
||||
void Client::send(const Message &message){
|
||||
if(!isConnected()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_socket){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_socket->isOpen()){
|
||||
qDebug() << "client socket isn't open";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "client writing" << message.toJson();
|
||||
m_socket->write(message.toJson());
|
||||
m_socket->write("\n");
|
||||
m_socket->flush();
|
||||
|
||||
// remove if needed
|
||||
if(m_requests.contains(message.id())){
|
||||
m_requests.remove(message.id());
|
||||
}
|
||||
}
|
||||
|
||||
void Client::onDisconnected(){
|
||||
qDebug() << "MessageServer client disconnected";
|
||||
setConnected(false);
|
||||
}
|
||||
|
||||
void Client::readyRead(){
|
||||
qDebug() << "MessageServer client readyRead";
|
||||
|
||||
while(m_socket->canReadLine()){
|
||||
auto msg = m_socket->readLine().trimmed();
|
||||
qDebug() << "-> Client" << m_socket->socketDescriptor() << msg;
|
||||
|
||||
if(msg.isEmpty()){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MessageServer * self_;
|
||||
QString version_;
|
||||
QString revision_;
|
||||
port_type port_;
|
||||
QHostAddress multicast_group_address_;
|
||||
static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint;
|
||||
struct Client
|
||||
{
|
||||
Client () = default;
|
||||
Client (QHostAddress const& sender_address, port_type const& sender_port)
|
||||
: sender_address_ {sender_address}
|
||||
, sender_port_ {sender_port}
|
||||
, negotiated_schema_number_ {2} // not 1 because it's broken
|
||||
, last_activity_ {QDateTime::currentDateTime ()}
|
||||
{
|
||||
}
|
||||
Client (Client const&) = default;
|
||||
Client& operator= (Client const&) = default;
|
||||
|
||||
QHostAddress sender_address_;
|
||||
port_type sender_port_;
|
||||
quint32 negotiated_schema_number_;
|
||||
QDateTime last_activity_;
|
||||
};
|
||||
QHash<QString, Client> clients_; // maps id to Client
|
||||
QTimer * clock_;
|
||||
};
|
||||
|
||||
MessageServer::impl::BindMode constexpr MessageServer::impl::bind_mode_;
|
||||
|
||||
#include "MessageServer.moc"
|
||||
|
||||
void MessageServer::impl::leave_multicast_group ()
|
||||
{
|
||||
if (!multicast_group_address_.isNull () && BoundState == state ())
|
||||
{
|
||||
leaveMulticastGroup (multicast_group_address_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::impl::join_multicast_group ()
|
||||
{
|
||||
if (BoundState == state ()
|
||||
&& !multicast_group_address_.isNull ())
|
||||
{
|
||||
if (IPv4Protocol == multicast_group_address_.protocol ()
|
||||
&& IPv4Protocol != localAddress ().protocol ())
|
||||
{
|
||||
close ();
|
||||
bind (QHostAddress::AnyIPv4, port_, bind_mode_);
|
||||
QJsonParseError e;
|
||||
QJsonDocument d = QJsonDocument::fromJson(msg, &e);
|
||||
if(e.error != QJsonParseError::NoError){
|
||||
//Q_EMIT self_->error(QString {"MessageClient json parse error: %1"}.arg(e.errorString()));
|
||||
return;
|
||||
}
|
||||
if (!joinMulticastGroup (multicast_group_address_))
|
||||
{
|
||||
multicast_group_address_.clear ();
|
||||
|
||||
if(!d.isObject()){
|
||||
//Q_EMIT self_->error(QString {"MessageClient json parse error: json is not an object"});
|
||||
return;
|
||||
}
|
||||
|
||||
Message m;
|
||||
m.read(d.object());
|
||||
auto id = m.ensureId();
|
||||
m_requests[id] = m;
|
||||
|
||||
emit m_server->message(m);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::impl::pending_datagrams ()
|
||||
{
|
||||
while (hasPendingDatagrams ())
|
||||
{
|
||||
QByteArray datagram;
|
||||
datagram.resize (pendingDatagramSize ());
|
||||
QHostAddress sender_address;
|
||||
port_type sender_port;
|
||||
if (0 <= readDatagram (datagram.data (), datagram.size (), &sender_address, &sender_port))
|
||||
{
|
||||
parse_message (sender_address, sender_port, datagram);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::impl::parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
//
|
||||
// message format is described in NetworkMessage.hpp
|
||||
//
|
||||
NetworkMessage::Reader in {msg};
|
||||
|
||||
auto id = in.id ();
|
||||
if (OK == check_status (in))
|
||||
{
|
||||
if (!clients_.contains (id))
|
||||
{
|
||||
auto& client = (clients_[id] = {sender, sender_port});
|
||||
QByteArray client_version;
|
||||
QByteArray client_revision;
|
||||
|
||||
if (NetworkMessage::Heartbeat == in.type ())
|
||||
{
|
||||
// negotiate a working schema number
|
||||
in >> client.negotiated_schema_number_;
|
||||
if (OK == check_status (in))
|
||||
{
|
||||
auto sn = NetworkMessage::Builder::schema_number;
|
||||
client.negotiated_schema_number_ = std::min (sn, client.negotiated_schema_number_);
|
||||
|
||||
// reply to the new client informing it of the
|
||||
// negotiated schema number
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id, client.negotiated_schema_number_};
|
||||
hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
|
||||
<< version_.toUtf8 () << revision_.toUtf8 ();
|
||||
if (impl::OK == check_status (hb))
|
||||
{
|
||||
writeDatagram (message, client.sender_address_, client.sender_port_);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT self_->error ("Error creating UDP message");
|
||||
}
|
||||
}
|
||||
// we don't care if this fails to read
|
||||
in >> client_version >> client_revision;
|
||||
}
|
||||
Q_EMIT self_->client_opened (id, QString::fromUtf8 (client_version),
|
||||
QString::fromUtf8 (client_revision));
|
||||
}
|
||||
clients_[id].last_activity_ = QDateTime::currentDateTime ();
|
||||
|
||||
//
|
||||
// message format is described in NetworkMessage.hpp
|
||||
//
|
||||
switch (in.type ())
|
||||
{
|
||||
case NetworkMessage::Heartbeat:
|
||||
//nothing to do here as time out handling deals with lifetime
|
||||
break;
|
||||
|
||||
case NetworkMessage::Clear:
|
||||
Q_EMIT self_->clear_decodes (id);
|
||||
break;
|
||||
|
||||
case NetworkMessage::Status:
|
||||
{
|
||||
// unpack message
|
||||
Frequency f;
|
||||
QByteArray mode;
|
||||
QByteArray dx_call;
|
||||
QByteArray report;
|
||||
QByteArray tx_mode;
|
||||
bool tx_enabled {false};
|
||||
bool transmitting {false};
|
||||
bool decoding {false};
|
||||
qint32 rx_df {-1};
|
||||
qint32 tx_df {-1};
|
||||
QByteArray de_call;
|
||||
QByteArray de_grid;
|
||||
QByteArray dx_grid;
|
||||
bool watchdog_timeout {false};
|
||||
QByteArray sub_mode;
|
||||
bool fast_mode {false};
|
||||
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
|
||||
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
|
||||
>> fast_mode;
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
|
||||
, QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
|
||||
, tx_enabled, transmitting, decoding, rx_df, tx_df
|
||||
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
|
||||
, QString::fromUtf8 (dx_grid), watchdog_timeout
|
||||
, QString::fromUtf8 (sub_mode), fast_mode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::Decode:
|
||||
{
|
||||
// unpack message
|
||||
bool is_new {true};
|
||||
QTime time;
|
||||
qint32 snr;
|
||||
float delta_time;
|
||||
quint32 delta_frequency;
|
||||
QByteArray mode;
|
||||
QByteArray message;
|
||||
bool low_confidence {false};
|
||||
bool off_air {false};
|
||||
in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode
|
||||
>> message >> low_confidence >> off_air;
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
Q_EMIT self_->decode (is_new, id, time, snr, delta_time, delta_frequency
|
||||
, QString::fromUtf8 (mode), QString::fromUtf8 (message)
|
||||
, low_confidence, off_air);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::WSPRDecode:
|
||||
{
|
||||
// unpack message
|
||||
bool is_new {true};
|
||||
QTime time;
|
||||
qint32 snr;
|
||||
float delta_time;
|
||||
Frequency frequency;
|
||||
qint32 drift;
|
||||
QByteArray callsign;
|
||||
QByteArray grid;
|
||||
qint32 power;
|
||||
bool off_air {false};
|
||||
in >> is_new >> time >> snr >> delta_time >> frequency >> drift >> callsign >> grid >> power
|
||||
>> off_air;
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
Q_EMIT self_->WSPR_decode (is_new, id, time, snr, delta_time, frequency, drift
|
||||
, QString::fromUtf8 (callsign), QString::fromUtf8 (grid)
|
||||
, power, off_air);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::QSOLogged:
|
||||
{
|
||||
QDateTime time_off;
|
||||
QByteArray dx_call;
|
||||
QByteArray dx_grid;
|
||||
Frequency dial_frequency;
|
||||
QByteArray mode;
|
||||
QByteArray report_sent;
|
||||
QByteArray report_received;
|
||||
QByteArray tx_power;
|
||||
QByteArray comments;
|
||||
QByteArray name;
|
||||
QDateTime time_on; // Note: LOTW uses TIME_ON for their +/- 30-minute time window
|
||||
QByteArray operator_call;
|
||||
QByteArray my_call;
|
||||
QByteArray my_grid;
|
||||
in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received
|
||||
>> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid;
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
|
||||
, dial_frequency, QString::fromUtf8 (mode), QString::fromUtf8 (report_sent)
|
||||
, QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power)
|
||||
, QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
|
||||
, QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
|
||||
, QString::fromUtf8 (my_grid));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::Close:
|
||||
Q_EMIT self_->client_closed (id);
|
||||
clients_.remove (id);
|
||||
break;
|
||||
|
||||
case NetworkMessage::LoggedADIF:
|
||||
{
|
||||
QByteArray ADIF;
|
||||
in >> ADIF;
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
Q_EMIT self_->logged_ADIF (id, ADIF);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT self_->error ("MessageServer warning: invalid UDP message received");
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
Q_EMIT self_->error (QString {"MessageServer exception: %1"}.arg (e.what ()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Q_EMIT self_->error ("Unexpected exception in MessageServer");
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::impl::tick ()
|
||||
{
|
||||
auto now = QDateTime::currentDateTime ();
|
||||
auto iter = std::begin (clients_);
|
||||
while (iter != std::end (clients_))
|
||||
{
|
||||
if (now > (*iter).last_activity_.addSecs (NetworkMessage::pulse))
|
||||
{
|
||||
Q_EMIT self_->clear_decodes (iter.key ());
|
||||
Q_EMIT self_->client_closed (iter.key ());
|
||||
iter = clients_.erase (iter); // safe while iterating as doesn't rehash
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto MessageServer::impl::check_status (QDataStream const& stream) const -> StreamStatus
|
||||
{
|
||||
auto stat = stream.status ();
|
||||
StreamStatus result {Fail};
|
||||
switch (stat)
|
||||
{
|
||||
case QDataStream::ReadPastEnd:
|
||||
result = Short;
|
||||
break;
|
||||
|
||||
case QDataStream::ReadCorruptData:
|
||||
Q_EMIT self_->error ("Message serialization error: read corrupt data");
|
||||
break;
|
||||
|
||||
case QDataStream::WriteFailed:
|
||||
Q_EMIT self_->error ("Message serialization error: write error");
|
||||
break;
|
||||
|
||||
default:
|
||||
result = OK;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MessageServer::MessageServer (QObject * parent, QString const& version, QString const& revision)
|
||||
: QObject {parent}
|
||||
, m_ {this, version, revision}
|
||||
{
|
||||
}
|
||||
|
||||
void MessageServer::start (port_type port, QHostAddress const& multicast_group_address)
|
||||
{
|
||||
if (port != m_->port_
|
||||
|| multicast_group_address != m_->multicast_group_address_)
|
||||
{
|
||||
m_->leave_multicast_group ();
|
||||
if (impl::BoundState == m_->state ())
|
||||
{
|
||||
m_->close ();
|
||||
}
|
||||
m_->multicast_group_address_ = multicast_group_address;
|
||||
auto address = m_->multicast_group_address_.isNull ()
|
||||
|| impl::IPv4Protocol != m_->multicast_group_address_.protocol () ? QHostAddress::Any : QHostAddress::AnyIPv4;
|
||||
if (port && m_->bind (address, port, m_->bind_mode_))
|
||||
{
|
||||
m_->port_ = port;
|
||||
m_->join_multicast_group ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_->port_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode
|
||||
, QString const& message_text, bool low_confidence, quint8 modifiers)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Reply, id, (*iter).negotiated_schema_number_};
|
||||
out << time << snr << delta_time << delta_frequency << mode.toUtf8 ()
|
||||
<< message_text.toUtf8 () << low_confidence << modifiers;
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::replay (QString const& id)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Replay, id, (*iter).negotiated_schema_number_};
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::halt_tx (QString const& id, bool auto_only)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, id, (*iter).negotiated_schema_number_};
|
||||
out << auto_only;
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::free_text (QString const& id, QString const& text, bool send)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, id, (*iter).negotiated_schema_number_};
|
||||
out << text.toUtf8 () << send;
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::location (QString const& id, QString const& loc)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::Location, id, (*iter).negotiated_schema_number_};
|
||||
out << loc.toUtf8 ();
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageServer::highlight_callsign (QString const& id, QString const& callsign
|
||||
, QColor const& bg, QColor const& fg, bool last_only)
|
||||
{
|
||||
auto iter = m_->clients_.find (id);
|
||||
if (iter != std::end (m_->clients_))
|
||||
{
|
||||
QByteArray message;
|
||||
NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, id, (*iter).negotiated_schema_number_};
|
||||
out << callsign.toUtf8 () << bg << fg << last_only;
|
||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||
}
|
||||
}
|
||||
|
74
MessageServer.h
Normal file
74
MessageServer.h
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef MESSAGESERVER_H
|
||||
#define MESSAGESERVER_H
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QAbstractSocket>
|
||||
#include <QScopedPointer>
|
||||
#include <QList>
|
||||
|
||||
#include "Message.h"
|
||||
|
||||
class Client;
|
||||
|
||||
class MessageServer : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MessageServer(QObject *parent = 0);
|
||||
virtual ~MessageServer();
|
||||
|
||||
protected:
|
||||
void incomingConnection(qintptr handle);
|
||||
|
||||
signals:
|
||||
void message(Message const &message);
|
||||
void error (QString const&) const;
|
||||
|
||||
public slots:
|
||||
void setServer(QString host, quint16 port=2442);
|
||||
void setPause(bool paused);
|
||||
bool start();
|
||||
void stop();
|
||||
void setServerHost(const QString &host){ setServer(host, m_port); }
|
||||
void setServerPort(quint16 port){ setServer(m_host, port); }
|
||||
void send(Message const &message);
|
||||
|
||||
private:
|
||||
bool m_paused;
|
||||
QString m_host;
|
||||
quint16 m_port;
|
||||
|
||||
QList<Client*> m_clients;
|
||||
};
|
||||
|
||||
class Client : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Client(MessageServer *server, QObject *parent = 0);
|
||||
|
||||
bool isConnected() const { return m_connected; }
|
||||
void setSocket(qintptr handle);
|
||||
void send(const Message &message);
|
||||
void close();
|
||||
bool awaitingResponse(int id){
|
||||
return id <= 0 || m_requests.contains(id);
|
||||
}
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void setConnected(bool connected);
|
||||
void onDisconnected();
|
||||
void readyRead();
|
||||
|
||||
private:
|
||||
QMap<int, Message> m_requests;
|
||||
MessageServer * m_server;
|
||||
QTcpSocket * m_socket;
|
||||
bool m_connected;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // MESSAGESERVER_H
|
@ -1,102 +0,0 @@
|
||||
#ifndef MESSAGE_SERVER_HPP__
|
||||
#define MESSAGE_SERVER_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
#include <QHostAddress>
|
||||
#include <QColor>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
|
||||
//
|
||||
// MessageServer - a reference implementation of a message server
|
||||
// matching the MessageClient class at the other end
|
||||
// of the wire
|
||||
//
|
||||
// This class is fully functioning and suitable for use in C++
|
||||
// applications that use the Qt framework. Other applications should
|
||||
// use this classes' implementation as a reference implementation.
|
||||
//
|
||||
class MessageServer
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
using port_type = quint16;
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
MessageServer (QObject * parent = nullptr,
|
||||
QString const& version = QString {}, QString const& revision = QString {});
|
||||
|
||||
// start or restart the server, if the multicast_group_address
|
||||
// argument is given it is assumed to be a multicast group address
|
||||
// which the server will join
|
||||
Q_SLOT void start (port_type port,
|
||||
QHostAddress const& multicast_group_address = QHostAddress {});
|
||||
|
||||
// ask the client with identification 'id' to make the same action
|
||||
// as a double click on the decode would
|
||||
//
|
||||
// note that the client is not obliged to take any action and only
|
||||
// takes any action if the decode is present and is a CQ or QRZ message
|
||||
Q_SLOT 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);
|
||||
|
||||
// ask the client with identification 'id' to replay all decodes
|
||||
Q_SLOT void replay (QString const& id);
|
||||
|
||||
// ask the client with identification 'id' to halt transmitting
|
||||
// auto_only just disables auto Tx, otherwise halt is immediate
|
||||
Q_SLOT void halt_tx (QString const& id, bool auto_only);
|
||||
|
||||
// ask the client with identification 'id' to set the free text
|
||||
// message and optionally send it ASAP
|
||||
Q_SLOT void free_text (QString const& id, QString const& text, bool send);
|
||||
|
||||
// ask the client with identification 'id' to set the location provided
|
||||
Q_SLOT void location (QString const& id, QString const& location);
|
||||
|
||||
// ask the client with identification 'id' to highlight the callsign
|
||||
// specified with the given colors
|
||||
Q_SLOT void highlight_callsign (QString const& id, QString const& callsign
|
||||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||
, bool last_only = false);
|
||||
|
||||
// the following signals are emitted when a client broadcasts the
|
||||
// matching message
|
||||
Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision);
|
||||
Q_SIGNAL void status_update (QString const& id, Frequency, 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_SIGNAL void client_closed (QString const& id);
|
||||
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
|
||||
, bool low_confidence, bool off_air);
|
||||
Q_SIGNAL void WSPR_decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time, Frequency
|
||||
, qint32 drift, QString const& callsign, QString const& grid, qint32 power
|
||||
, bool off_air);
|
||||
Q_SIGNAL void qso_logged (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);
|
||||
Q_SIGNAL void clear_decodes (QString const& id);
|
||||
Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF);
|
||||
|
||||
// this signal is emitted when a network error occurs
|
||||
Q_SIGNAL void error (QString const&) const;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
@ -92,7 +92,8 @@ SOURCES += \
|
||||
ProcessThread.cpp \
|
||||
DecoderThread.cpp \
|
||||
Decoder.cpp \
|
||||
APRSISClient.cpp
|
||||
APRSISClient.cpp \
|
||||
MessageServer.cpp
|
||||
|
||||
HEADERS += qt_helpers.hpp \
|
||||
pimpl_h.hpp pimpl_impl.hpp \
|
||||
@ -135,7 +136,8 @@ HEADERS += qt_helpers.hpp \
|
||||
ProcessThread.h \
|
||||
DecoderThread.h \
|
||||
Decoder.h \
|
||||
APRSISClient.h
|
||||
APRSISClient.h \
|
||||
MessageServer.h
|
||||
|
||||
|
||||
INCLUDEPATH += qmake_only
|
||||
|
110
mainwindow.cpp
110
mainwindow.cpp
@ -435,6 +435,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
version (), revision (),
|
||||
m_config.udp_server_name (), m_config.udp_server_port (),
|
||||
this}},
|
||||
m_messageServer { new MessageServer(this) },
|
||||
m_n3fjpClient { new TCPClient{this}},
|
||||
m_spotClient { new SpotClient{m_messageClient, this}},
|
||||
m_aprsClient {new APRSISClient{"rotate.aprs2.net", 14580, this}},
|
||||
@ -480,8 +481,19 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
// notification audio operates in its own thread at a lower priority
|
||||
m_notification->moveToThread(&m_notificationAudioThread);
|
||||
|
||||
// move the aprs client to its own network thread at a lower priority
|
||||
// move the aprs client and the message server to its own network thread at a lower priority
|
||||
m_aprsClient->moveToThread(&m_networkThread);
|
||||
m_messageServer->moveToThread(&m_networkThread);
|
||||
|
||||
// hook up the message server slots and signals and disposal
|
||||
connect (m_messageServer, &MessageServer::error, this, &MainWindow::udpNetworkError);
|
||||
connect (m_messageServer, &MessageServer::message, this, &MainWindow::networkMessage);
|
||||
connect (this, &MainWindow::apiSetServer, m_messageServer, &MessageServer::setServer);
|
||||
connect (this, &MainWindow::apiStartServer, m_messageServer, &MessageServer::start);
|
||||
connect (this, &MainWindow::apiStopServer, m_messageServer, &MessageServer::stop);
|
||||
connect (&m_config, &Configuration::tcp_server_changed, m_messageServer, &MessageServer::setServerHost);
|
||||
connect (&m_config, &Configuration::tcp_server_port_changed, m_messageServer, &MessageServer::setServerPort);
|
||||
connect (&m_networkThread, &QThread::finished, m_messageServer, &QObject::deleteLater);
|
||||
|
||||
// hook up the aprs client slots and signals and disposal
|
||||
connect (this, &MainWindow::aprsClientEnqueueSpot, m_aprsClient, &APRSISClient::enqueueSpot);
|
||||
@ -536,9 +548,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
connect (m_logDlg.data (), &LogQSO::acceptQSO, this, &MainWindow::acceptQSO);
|
||||
connect (this, &MainWindow::finished, m_logDlg.data (), &LogQSO::close);
|
||||
|
||||
|
||||
// Network message handlers
|
||||
connect (m_messageClient, &MessageClient::error, this, &MainWindow::networkError);
|
||||
connect (m_messageClient, &MessageClient::error, this, &MainWindow::udpNetworkError);
|
||||
connect (m_messageClient, &MessageClient::message, this, &MainWindow::networkMessage);
|
||||
|
||||
#if 0
|
||||
@ -1005,7 +1016,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
|
||||
// prep
|
||||
prepareHeartbeatMode(canCurrentModeSendHeartbeat() && ui->actionModeJS8HB->isChecked());
|
||||
prepareSpotting();
|
||||
|
||||
auto enterFilter = new EnterKeyPressEater();
|
||||
connect(enterFilter, &EnterKeyPressEater::enterKeyPressed, this, [this](QObject *, QKeyEvent *, bool *pProcessed){
|
||||
@ -1595,6 +1605,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
ui->dialFreqDownButton->setFixedSize(30, 24);
|
||||
|
||||
// Prepare spotting configuration...
|
||||
prepareApi();
|
||||
prepareSpotting();
|
||||
|
||||
displayActivity(true);
|
||||
@ -3107,6 +3118,7 @@ void MainWindow::openSettings(int tab){
|
||||
|
||||
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
|
||||
|
||||
prepareApi();
|
||||
prepareSpotting();
|
||||
|
||||
if(m_config.restart_audio_input ()) {
|
||||
@ -3167,6 +3179,16 @@ void MainWindow::openSettings(int tab){
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::prepareApi(){
|
||||
bool enabled = true;
|
||||
if(enabled){
|
||||
emit apiSetServer("0.0.0.0", 2442);
|
||||
emit apiStartServer();
|
||||
} else {
|
||||
emit apiStopServer();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::prepareSpotting(){
|
||||
if(m_config.spot_to_reporting_networks ()){
|
||||
spotSetLocal();
|
||||
@ -3186,6 +3208,7 @@ void MainWindow::on_spotButton_clicked(bool checked){
|
||||
m_config.set_spot_to_reporting_networks(checked);
|
||||
|
||||
// 2. prepare
|
||||
prepareApi();
|
||||
prepareSpotting();
|
||||
}
|
||||
|
||||
@ -3400,6 +3423,12 @@ void MainWindow::updateCurrentBand(){
|
||||
m_wideGraph->setRxBand (band_name);
|
||||
|
||||
qDebug() << "setting band" << band_name;
|
||||
sendNetworkMessage("RIG.FREQ", "", {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"BAND", QVariant(band_name)},
|
||||
{"DIAL", QVariant((quint64)dialFrequency())},
|
||||
{"OFFSET", QVariant((quint64)currentFreqOffset())}
|
||||
});
|
||||
m_lastBand = band_name;
|
||||
|
||||
band_changed(dial_frequency);
|
||||
@ -7330,6 +7359,7 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
|
||||
// Log to JS8Call API
|
||||
if(m_config.udpEnabled()){
|
||||
sendNetworkMessage("LOG.QSO", QString(ADIF), {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())},
|
||||
{"UTC.OFF", QVariant(QSO_date_off.toMSecsSinceEpoch())},
|
||||
{"CALL", QVariant(call)},
|
||||
@ -10518,6 +10548,7 @@ void MainWindow::processRxActivity() {
|
||||
|
||||
if(canSendNetworkMessage()){
|
||||
sendNetworkMessage("RX.ACTIVITY", d.text, {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"FREQ", QVariant(d.freq)},
|
||||
{"SNR", QVariant(d.snr)},
|
||||
{"SPEED", QVariant(d.submode)},
|
||||
@ -11017,6 +11048,7 @@ void MainWindow::processCommandActivity() {
|
||||
// and send it to the network in case we want to interact with it from an external app...
|
||||
if(canSendNetworkMessage()){
|
||||
sendNetworkMessage("RX.DIRECTED", ad.text, {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"FROM", QVariant(d.from)},
|
||||
{"TO", QVariant(d.to)},
|
||||
{"CMD", QVariant(d.cmd)},
|
||||
@ -11763,6 +11795,7 @@ void MainWindow::processSpots() {
|
||||
|
||||
if(canSendNetworkMessage()){
|
||||
sendNetworkMessage("RX.SPOT", "", {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"DIAL", QVariant(dial)},
|
||||
{"OFFSET", QVariant(d.freq)},
|
||||
{"CALL", QVariant(d.call)},
|
||||
@ -12552,6 +12585,7 @@ void MainWindow::emitPTT(bool on){
|
||||
|
||||
// emit to network
|
||||
sendNetworkMessage("RIG.PTT", on ? "on" : "off", {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"PTT", QVariant(on)},
|
||||
{"UTC", QVariant(DriftingDateTime::currentDateTimeUtc().toMSecsSinceEpoch())},
|
||||
});
|
||||
@ -12570,11 +12604,12 @@ void MainWindow::emitTones(){
|
||||
}
|
||||
|
||||
sendNetworkMessage("TX.FRAME", "", {
|
||||
{"_ID", QVariant(-1)},
|
||||
{"TONES", t}
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::networkMessage(Message const &message)
|
||||
void MainWindow::udpNetworkMessage(Message const &message)
|
||||
{
|
||||
if(!m_config.udpEnabled()){
|
||||
return;
|
||||
@ -12584,16 +12619,33 @@ void MainWindow::networkMessage(Message const &message)
|
||||
return;
|
||||
}
|
||||
|
||||
networkMessage(message);
|
||||
}
|
||||
|
||||
void MainWindow::tcpNetworkMessage(Message const &message)
|
||||
{
|
||||
// if(!m_config.tcpEnabled()){
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if(!m_config.accept_tcp_requests()){
|
||||
// return;
|
||||
// }
|
||||
|
||||
networkMessage(message);
|
||||
}
|
||||
|
||||
void MainWindow::networkMessage(Message const &message)
|
||||
{
|
||||
auto type = message.type();
|
||||
|
||||
if(type == "PING"){
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "try processing network message" << type;
|
||||
auto id = message.id();
|
||||
|
||||
auto params = message.params();
|
||||
auto id = params.value("_ID", QVariant(0));
|
||||
qDebug() << "try processing network message" << type << id;
|
||||
|
||||
// Inspired by FLDigi
|
||||
// TODO: MAIN.RX - Turn on RX
|
||||
@ -12840,23 +12892,27 @@ bool MainWindow::canSendNetworkMessage(){
|
||||
}
|
||||
|
||||
void MainWindow::sendNetworkMessage(QString const &type, QString const &message){
|
||||
if(!m_config.udpEnabled()){
|
||||
if(!canSendNetworkMessage()){
|
||||
return;
|
||||
}
|
||||
|
||||
m_messageClient->send(Message(type, message));
|
||||
auto m = Message(type, message);
|
||||
m_messageClient->send(m);
|
||||
m_messageServer->send(m);
|
||||
}
|
||||
|
||||
void MainWindow::sendNetworkMessage(QString const &type, QString const &message, QMap<QString, QVariant> const ¶ms)
|
||||
{
|
||||
if(!m_config.udpEnabled()){
|
||||
if(!canSendNetworkMessage()){
|
||||
return;
|
||||
}
|
||||
|
||||
m_messageClient->send(Message(type, message, params));
|
||||
auto m = Message(type, message, params);
|
||||
m_messageClient->send(m);
|
||||
m_messageServer->send(m);
|
||||
}
|
||||
|
||||
void MainWindow::networkError (QString const& e)
|
||||
void MainWindow::udpNetworkError (QString const& e)
|
||||
{
|
||||
if(!m_config.udpEnabled()){
|
||||
return;
|
||||
@ -12865,7 +12921,7 @@ void MainWindow::networkError (QString const& e)
|
||||
if(!m_config.accept_udp_requests()){
|
||||
return;
|
||||
}
|
||||
if (m_splash && m_splash->isVisible ()) m_splash->hide ();
|
||||
|
||||
if (MessageBox::Retry == MessageBox::warning_message (this, tr ("Network Error")
|
||||
, tr ("Error: %1\nUDP server %2:%3")
|
||||
.arg (e)
|
||||
@ -12880,6 +12936,32 @@ void MainWindow::networkError (QString const& e)
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::tcpNetworkError (QString const& e)
|
||||
{
|
||||
/*
|
||||
if(!m_config.tcpEnabled()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_config.accept_tcp_requests()){
|
||||
return;
|
||||
}
|
||||
|
||||
if (MessageBox::Retry == MessageBox::warning_message (this, tr ("Network Error")
|
||||
, tr ("Error: %1\nTCP server %2:%3")
|
||||
.arg (e)
|
||||
.arg (m_config.tcp_server_name ())
|
||||
.arg (m_config.tcp_server_port ())
|
||||
, QString {}
|
||||
, MessageBox::Cancel | MessageBox::Retry
|
||||
, MessageBox::Cancel))
|
||||
{
|
||||
// retry server lookup
|
||||
//m_messageClient->set_server (m_config.udp_server_name ());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void MainWindow::on_syncSpinBox_valueChanged(int n)
|
||||
{
|
||||
m_minSync=n;
|
||||
|
12
mainwindow.h
12
mainwindow.h
@ -46,6 +46,7 @@
|
||||
#include "qpriorityqueue.h"
|
||||
#include "varicode.h"
|
||||
#include "MessageClient.hpp"
|
||||
#include "MessageServer.h"
|
||||
#include "TCPClient.h"
|
||||
#include "SpotClient.h"
|
||||
#include "APRSISClient.h"
|
||||
@ -207,6 +208,7 @@ private slots:
|
||||
void on_actionReset_Window_Sizes_triggered();
|
||||
void on_actionSettings_triggered();
|
||||
void openSettings(int tab=0);
|
||||
void prepareApi();
|
||||
void prepareSpotting();
|
||||
void on_spotButton_clicked(bool checked);
|
||||
void on_monitorButton_clicked (bool);
|
||||
@ -409,11 +411,14 @@ private slots:
|
||||
void on_cbTx6_toggled(bool b);
|
||||
void emitPTT(bool on);
|
||||
void emitTones();
|
||||
void udpNetworkMessage(Message const &message);
|
||||
void tcpNetworkMessage(Message const &message);
|
||||
void networkMessage(Message const &message);
|
||||
bool canSendNetworkMessage();
|
||||
void sendNetworkMessage(QString const &type, QString const &message);
|
||||
void sendNetworkMessage(QString const &type, QString const &message, const QMap<QString, QVariant> ¶ms);
|
||||
void networkError (QString const&);
|
||||
void udpNetworkError (QString const&);
|
||||
void tcpNetworkError (QString const&);
|
||||
void on_ClrAvgButton_clicked();
|
||||
void on_syncSpinBox_valueChanged(int n);
|
||||
void on_TxPowerComboBox_currentIndexChanged(const QString &arg1);
|
||||
@ -436,6 +441,10 @@ private slots:
|
||||
void refreshTextDisplay();
|
||||
|
||||
private:
|
||||
Q_SIGNAL void apiSetServer(QString host, quint16 port);
|
||||
Q_SIGNAL void apiStartServer();
|
||||
Q_SIGNAL void apiStopServer();
|
||||
|
||||
Q_SIGNAL void aprsClientEnqueueSpot(QString by_call, QString from_call, QString grid, QString comment);
|
||||
Q_SIGNAL void aprsClientEnqueueThirdParty(QString by_call, QString from_call, QString text);
|
||||
Q_SIGNAL void aprsClientSetServer(QString host, quint16 port);
|
||||
@ -932,6 +941,7 @@ private:
|
||||
double m_toneSpacing;
|
||||
int m_firstDecode;
|
||||
MessageClient * m_messageClient;
|
||||
MessageServer * m_messageServer;
|
||||
TCPClient * m_n3fjpClient;
|
||||
PSK_Reporter *psk_Reporter;
|
||||
SpotClient *m_spotClient;
|
||||
|
Loading…
Reference in New Issue
Block a user