js8call/JS8_Main/APRSISClient.h

136 lines
3.9 KiB
C++

/**
* @file APRSISClient.h
* @brief APRS-IS client interface for JS8Call.
*/
#ifndef APRSISCLIENT_H
#define APRSISCLIENT_H
#include <QDateTime>
#include <QLoggingCategory>
#include <QPair>
#include <QQueue>
#include <QTcpSocket>
#include <QTimer>
#include <QtGlobal>
Q_DECLARE_LOGGING_CATEGORY(aprsisclient_js8)
/**
* @brief APRS-IS client responsible for sending and receiving APRS frames.
*/
class APRSISClient : public QTcpSocket {
Q_OBJECT
public:
/**
* @brief Construct a new APRSISClient object.
* @param host APRS-IS server host.
* @param port APRS-IS server port.
* @param parent QObject parent.
*/
APRSISClient(QString host, quint16 port, QObject *parent = nullptr);
/**
* @brief Compute APRS-IS passcode for a callsign.
* @param callsign Callsign to hash.
* @return APRS-IS passcode.
*/
static quint32 hashCallsign(QString callsign);
/**
* @brief Build an APRS-IS login frame.
* @param callsign Local station callsign.
* @param filter Optional APRS-IS filter string.
* @return Login frame string.
*/
static QString loginFrame(QString callsign, QString filter = QString());
static QPair<float, float> grid2deg(QString grid);
static QPair<QString, QString> grid2aprs(QString grid);
static QString stripSSID(QString call);
static QString replaceCallsignSuffixWithSSID(QString call, QString base);
bool isPasscodeValid() {
return m_localPasscode == QString::number(hashCallsign(m_localCall));
}
void enqueueRaw(QString aprsFrame);
void processQueue(bool disconnect = true);
public slots:
/**
* @brief Enable or disable persistent inbound message relay.
* @param enabled True to keep a connected session for inbound relay.
*/
void setIncomingRelayEnabled(bool enabled);
void setSkipPercent(float skipPercent) { m_skipPercent = skipPercent; }
void setServer(QString host, quint16 port) {
if (state() == QTcpSocket::ConnectedState) {
disconnectFromHost();
}
m_host = host;
m_port = port;
qCDebug(aprsisclient_js8)
<< "APRSISClient Server Change:" << m_host << m_port;
}
void setPaused(bool paused) { m_paused = paused; }
void setLocalStation(QString mycall, QString passcode) {
m_localCall = mycall;
m_localPasscode = passcode;
}
void enqueueSpot(QString by_call, QString from_call, QString grid,
QString comment);
void enqueueThirdParty(QString by_call, QString from_call, QString text);
/**
* @brief Enqueue a standard APRS message ACK frame.
* @param from_call Source callsign for the ACK message.
* @param to_call Destination callsign being acknowledged.
* @param messageId APRS message identifier to acknowledge.
*/
void enqueueMessageAck(QString from_call, QString to_call,
QString messageId);
void sendReports() {
if (m_paused)
return;
processQueue(!m_incomingRelayEnabled);
}
signals:
/**
* @brief Emitted when a parsed APRS-IS message is received.
* @param from APRS sender callsign.
* @param to APRS destination callsign.
* @param message APRS message payload without message ID suffix.
* @param messageId APRS message identifier (if present).
*/
void messageReceived(QString from, QString to, QString message,
QString messageId);
private slots:
void onSocketConnected();
void onSocketReadyRead();
void onSocketDisconnected();
void onSocketError(QAbstractSocket::SocketError socketError);
private:
QString m_localCall;
QString m_localPasscode;
QQueue<QPair<QString, QDateTime>> m_frameQueue;
QString m_host;
quint16 m_port;
QTimer m_timer;
bool m_paused;
bool m_incomingRelayEnabled;
bool m_isLoggedIn;
float m_skipPercent;
};
#endif // APRSISCLIENT_H