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
	 Jordan Sherer
						Jordan Sherer