kopia lustrzana https://gitlab.com/eliggett/wfview
Change the way that packets are handled
Packets can now be cast to a union of structs that allows each part to be accessed individually. Still a work in progress!merge-requests/2/head
rodzic
69db5e0efc
commit
df2c76588d
|
@ -0,0 +1,270 @@
|
||||||
|
#ifndef PACKETTYPES_H
|
||||||
|
#define PACKETTYPES_H
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
// Fixed Size Packets
|
||||||
|
#define CONTROL_SIZE 0x10
|
||||||
|
#define PING_SIZE 0x15
|
||||||
|
#define OPENCLOSE_SIZE 0x16
|
||||||
|
#define RETRANSMIT_SIZE 0x18
|
||||||
|
#define TOKEN_SIZE 0x40
|
||||||
|
#define STATUS_SIZE 0x50
|
||||||
|
#define LOGIN_RESPONSE_SIZE 0x60
|
||||||
|
#define LOGIN_SIZE 0x80
|
||||||
|
#define CONNINFO_SIZE 0x90
|
||||||
|
#define CAPABILITIES_SIZE 0xA8
|
||||||
|
|
||||||
|
// Variable size packets + payload
|
||||||
|
#define CIV_SIZE 0x15
|
||||||
|
#define TXAUDIO_SIZE 0x15
|
||||||
|
#define DATA_SIZE 0x15
|
||||||
|
|
||||||
|
// 0x10 length control packet (connect/disconnect/idle.)
|
||||||
|
typedef union control_packet {
|
||||||
|
struct {
|
||||||
|
quint32 len;
|
||||||
|
quint16 type;
|
||||||
|
quint16 seq;
|
||||||
|
quint32 sentid;
|
||||||
|
quint32 rcvdid;
|
||||||
|
};
|
||||||
|
char packet[CONTROL_SIZE];
|
||||||
|
} *control_packet_t;
|
||||||
|
|
||||||
|
|
||||||
|
// 0x15 length ping packet
|
||||||
|
typedef union ping_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char reply; // 0x10
|
||||||
|
union { // This contains differences between the send/receive packet
|
||||||
|
struct { // Ping
|
||||||
|
quint32 time; // 0x11
|
||||||
|
};
|
||||||
|
struct { // Send
|
||||||
|
quint16 datalen; // 0x11
|
||||||
|
quint16 sendseq; //0x13
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
char packet[PING_SIZE];
|
||||||
|
} *ping_packet_t, *data_packet_t, data_packet;
|
||||||
|
|
||||||
|
|
||||||
|
// 0x18 length txaudio packet
|
||||||
|
/* tx[0] = static_cast<quint8>(tx.length() & 0xff);
|
||||||
|
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
|
||||||
|
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
|
||||||
|
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
|
||||||
|
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
|
||||||
|
tx[23] = static_cast<quint8>(partial.length() & 0xff);*/
|
||||||
|
|
||||||
|
|
||||||
|
// 0x40 length token packet
|
||||||
|
typedef union token_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedc; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
char unusedd[16]; // 0x20
|
||||||
|
quint32 response; // 0x30
|
||||||
|
char unusede[12]; // 0x34
|
||||||
|
};
|
||||||
|
char packet[TOKEN_SIZE];
|
||||||
|
} *token_packet_t;
|
||||||
|
|
||||||
|
// 0x50 length login status packet
|
||||||
|
typedef union status_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedc; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
char unusedd[6]; // 0x20
|
||||||
|
quint16 unknown; // 0x26
|
||||||
|
char unusede; // 0x28
|
||||||
|
char unusedf[2]; // 0x29
|
||||||
|
char value[5]; // 0x2b
|
||||||
|
quint32 error; // 0x30
|
||||||
|
char unusedg[12]; // 0x34
|
||||||
|
char disc; // 0x40
|
||||||
|
char unusedh; // 0x41
|
||||||
|
quint32 civport; // 0x42 // Sent bigendian
|
||||||
|
quint32 audioport; // 0x46 // Sent bigendian
|
||||||
|
char unusedi[5]; // 0x4a
|
||||||
|
};
|
||||||
|
char packet[STATUS_SIZE];
|
||||||
|
} *status_packet_t;
|
||||||
|
|
||||||
|
// 0x60 length login status packet
|
||||||
|
typedef union login_response_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedc; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
quint16 authstartid; // 0x20
|
||||||
|
char unusedd[14]; // 0x22
|
||||||
|
quint32 error; // 0x30
|
||||||
|
char unusede[12]; // 0x34
|
||||||
|
char connection[16]; // 0x40
|
||||||
|
char unusedf[16]; // 0x50
|
||||||
|
};
|
||||||
|
char packet[LOGIN_RESPONSE_SIZE];
|
||||||
|
} *login_response_packet_t;
|
||||||
|
|
||||||
|
// 0x80 length login packet
|
||||||
|
typedef union login_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedb; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
char unusedc[32]; // 0x20
|
||||||
|
char username[16]; // 0x40
|
||||||
|
char password[16]; // 0x50
|
||||||
|
char name[16]; // 0x60
|
||||||
|
char unusedf[16]; // 0x70
|
||||||
|
};
|
||||||
|
char packet[LOGIN_SIZE];
|
||||||
|
} *login_packet_t;
|
||||||
|
|
||||||
|
// 0x90 length conninfo and stream request packet
|
||||||
|
typedef union conninfo_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedb; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
quint16 authstartid; // 0x20
|
||||||
|
char unusedd[5]; // 0x22
|
||||||
|
quint32 resb; // 0x27
|
||||||
|
char identa; // 0x2b
|
||||||
|
quint32 identb; // 0x2c
|
||||||
|
char unusedf[16]; // 0x30
|
||||||
|
char name[16]; // 0x40
|
||||||
|
char unusedg[16]; // 0x50
|
||||||
|
union { // This contains differences between the send/receive packet
|
||||||
|
struct { // Receive
|
||||||
|
quint32 ready; // 0x60
|
||||||
|
char computer[16]; // 0x64
|
||||||
|
char unusedi[16]; // 0x74
|
||||||
|
quint32 ipaddress; // 0x84
|
||||||
|
char unusedj[8]; // 0x78
|
||||||
|
};
|
||||||
|
struct { // Send
|
||||||
|
char ident[16]; // 0x60 // Not sure what this is?
|
||||||
|
char rxenable; // 0x70
|
||||||
|
char txenable; // 0x71
|
||||||
|
char rxcodec; // 0x72
|
||||||
|
char txcodec; // 0x73
|
||||||
|
quint32 rxsample; // 0x74
|
||||||
|
quint32 txsample; // 0x78
|
||||||
|
quint32 civport; // 0x7c
|
||||||
|
quint32 audioport; // 0x80
|
||||||
|
char unusedk; // 0x84
|
||||||
|
quint32 txbuffer; // 0x85
|
||||||
|
char unusedl[7]; // 0x89
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
char packet[CONNINFO_SIZE];
|
||||||
|
} *conninfo_packet_t;
|
||||||
|
|
||||||
|
// 0xA8 length capabilities packet
|
||||||
|
typedef union capabilities_packet {
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
quint32 len; // 0x00
|
||||||
|
quint16 type; // 0x04
|
||||||
|
quint16 seq; // 0x06
|
||||||
|
quint32 sentid; // 0x08
|
||||||
|
quint32 rcvdid; // 0x0c
|
||||||
|
char unuseda[3]; // 0x10
|
||||||
|
quint16 code; // 0x13
|
||||||
|
quint16 res; // 0x15
|
||||||
|
quint16 innerseq; // 0x17
|
||||||
|
char unusedc; // 0x19
|
||||||
|
quint16 tokrequest; // 0x1a
|
||||||
|
quint32 token; // 0x1c
|
||||||
|
char unusedd[33]; // 0x20
|
||||||
|
char capa; // 0x41
|
||||||
|
char unusede[7]; // 0x42
|
||||||
|
quint16 capb; // 0x49
|
||||||
|
char unusedf[2]; // 0x4b
|
||||||
|
char capc; // 0x4d
|
||||||
|
quint32 capd; // 0x4e
|
||||||
|
char name[32]; // 0x52
|
||||||
|
char audio[32]; // 0x72
|
||||||
|
char caparray[10]; // 0x92
|
||||||
|
char unusedh[2]; // 0x9c
|
||||||
|
quint16 cape; // 0x9e
|
||||||
|
quint16 capf; // 0xa0
|
||||||
|
char unusedi; // 0xa2
|
||||||
|
quint16 capg; // 0xa3
|
||||||
|
char unusedj[3]; // 0xa5
|
||||||
|
};
|
||||||
|
char packet[CAPABILITIES_SIZE];
|
||||||
|
} *capabilities_packet_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
|
||||||
|
#endif // PACKETTYPES_H
|
742
udphandler.cpp
742
udphandler.cpp
|
@ -112,212 +112,237 @@ void udpHandler::dataReceived()
|
||||||
while (udp->hasPendingDatagrams()) {
|
while (udp->hasPendingDatagrams()) {
|
||||||
lastReceived = time(0);
|
lastReceived = time(0);
|
||||||
QNetworkDatagram datagram = udp->receiveDatagram();
|
QNetworkDatagram datagram = udp->receiveDatagram();
|
||||||
//qDebug() << "Received: " << datagram.data();
|
|
||||||
QByteArray r = datagram.data();
|
QByteArray r = datagram.data();
|
||||||
|
|
||||||
switch (r.length())
|
switch (r.length())
|
||||||
{
|
{
|
||||||
case (16):
|
case (CONTROL_SIZE): // control packet
|
||||||
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x04\x00\x00\x00")) {
|
|
||||||
// If timer is active, stop it as they are obviously there!
|
|
||||||
if (areYouThereTimer.isActive()) {
|
|
||||||
areYouThereTimer.stop();
|
|
||||||
// send ping packets every second
|
|
||||||
pingTimer.start(PING_PERIOD);
|
|
||||||
idleTimer.start(IDLE_PERIOD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// This is "I am ready" in response to "Are you ready" so send login.
|
|
||||||
else if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
|
|
||||||
{
|
{
|
||||||
qDebug() << this->metaObject()->className() << ": Received I am ready";
|
control_packet_t in = (control_packet_t)r.constData();
|
||||||
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
|
if (in->type == 0x04) {
|
||||||
sendLogin(); // second login packet
|
// If timer is active, stop it as they are obviously there!
|
||||||
}
|
if (areYouThereTimer.isActive()) {
|
||||||
break;
|
areYouThereTimer.stop();
|
||||||
case (21): // pkt7,
|
// send ping packets every second
|
||||||
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00") && r[16] == (char)0x01 && serialAndAudioOpened)
|
pingTimer.start(PING_PERIOD);
|
||||||
{
|
idleTimer.start(IDLE_PERIOD);
|
||||||
// This is a response to our ping request so measure latency
|
|
||||||
latency += lastPingSentTime.msecsTo(QDateTime::currentDateTime());
|
|
||||||
latency /= 2;
|
|
||||||
quint32 totalsent = packetsSent;
|
|
||||||
quint32 totallost = packetsLost/2;
|
|
||||||
if (audio != Q_NULLPTR) {
|
|
||||||
totalsent = totalsent + audio->packetsSent;
|
|
||||||
totallost = totallost + audio->packetsLost/2;
|
|
||||||
}
|
|
||||||
if (civ != Q_NULLPTR) {
|
|
||||||
totalsent = totalsent + civ->packetsSent;
|
|
||||||
totallost = totallost + civ->packetsLost/2;
|
|
||||||
}
|
|
||||||
//double perclost = 1.0 * totallost / totalsent * 100.0 ;
|
|
||||||
|
|
||||||
emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms, loss: (" +QString::number(packetsLost)+ "/"+ QString::number(packetsSent) +")");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (64): // Response to Auth packet?
|
|
||||||
if (r.mid(0, 6) == QByteArrayLiteral("\x40\x00\x00\x00\x00\x00") && r[21] == (char)0x05) {
|
|
||||||
if (r.mid(0x30, 4) == QByteArrayLiteral("\x00\x00\x00\x00")) {
|
|
||||||
qDebug() << this->metaObject()->className() << ": Token renewal successful";
|
|
||||||
tokenTimer.start(TOKEN_RENEWAL);
|
|
||||||
gotAuthOK = true;
|
|
||||||
if (!serialAndAudioOpened)
|
|
||||||
{
|
|
||||||
sendRequestSerialAndAudio();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (r.mid(0x30, 4) == QByteArrayLiteral("\xff\xff\xff\xff"))
|
|
||||||
{
|
|
||||||
qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login";
|
|
||||||
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
|
|
||||||
isAuthenticated = false;
|
|
||||||
sendLogin(); // Try sending login packet (didn't seem to work?)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qWarning() << this->metaObject()->className() << ": Unknown response to token renewal? " << r.mid(0x30,4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case (80): // Status packet
|
|
||||||
if (r.mid(0, 6) == QByteArrayLiteral("\x50\x00\x00\x00\x00\x00"))
|
|
||||||
{
|
|
||||||
if (r.mid(48, 3) == QByteArrayLiteral("\xff\xff\xff"))
|
|
||||||
{
|
|
||||||
if (!serialAndAudioOpened)
|
|
||||||
{
|
|
||||||
emit haveNetworkError(radioIP.toString(), "Auth failed, try rebooting the radio.");
|
|
||||||
qDebug() << this->metaObject()->className() << ": Auth failed, try rebooting the radio.";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (r.mid(48, 3) == QByteArrayLiteral("\x00\x00\x00") && r[64] == (char)0x01)
|
// This is "I am ready" in response to "Are you ready" so send login.
|
||||||
|
else if (in->type == 0x06)
|
||||||
|
{
|
||||||
|
qDebug() << this->metaObject()->className() << ": Received I am ready";
|
||||||
|
sendLogin(); // send login packet
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (PING_SIZE): // ping packet
|
||||||
|
{
|
||||||
|
ping_packet_t in = (ping_packet_t)r.constData();
|
||||||
|
if (in->type == 0x07 && in->reply == 0x01 && streamOpened)
|
||||||
|
{
|
||||||
|
// This is a response to our ping request so measure latency
|
||||||
|
latency += lastPingSentTime.msecsTo(QDateTime::currentDateTime());
|
||||||
|
latency /= 2;
|
||||||
|
quint32 totalsent = packetsSent;
|
||||||
|
quint32 totallost = packetsLost / 2;
|
||||||
|
if (audio != Q_NULLPTR) {
|
||||||
|
totalsent = totalsent + audio->packetsSent;
|
||||||
|
totallost = totallost + audio->packetsLost / 2;
|
||||||
|
}
|
||||||
|
if (civ != Q_NULLPTR) {
|
||||||
|
totalsent = totalsent + civ->packetsSent;
|
||||||
|
totallost = totallost + civ->packetsLost / 2;
|
||||||
|
}
|
||||||
|
//double perclost = 1.0 * totallost / totalsent * 100.0 ;
|
||||||
|
emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms, loss: (" + QString::number(packetsLost) + "/" + QString::number(packetsSent) + ")");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (TOKEN_SIZE): // Response to Token request
|
||||||
|
{
|
||||||
|
token_packet_t in = (token_packet_t)r.constData();
|
||||||
|
if (in->res == 0x05)
|
||||||
|
{
|
||||||
|
if (in->response == 0x0000)
|
||||||
|
{
|
||||||
|
qDebug() << this->metaObject()->className() << ": Token renewal successful";
|
||||||
|
tokenTimer.start(TOKEN_RENEWAL);
|
||||||
|
gotAuthOK = true;
|
||||||
|
if (!streamOpened)
|
||||||
|
{
|
||||||
|
sendRequestStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (in->response == 0xffffffff)
|
||||||
|
{
|
||||||
|
qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login";
|
||||||
|
remoteId = in->sentid;
|
||||||
|
isAuthenticated = false;
|
||||||
|
sendLogin(); // Try sending login packet (didn't seem to work?)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning() << this->metaObject()->className() << ": Unknown response to token renewal? " << in->response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (STATUS_SIZE): // Status packet
|
||||||
|
{
|
||||||
|
status_packet_t in = (status_packet_t)r.constData();
|
||||||
|
if (in->error == 0x00ffffff && !streamOpened)
|
||||||
|
{
|
||||||
|
emit haveNetworkError(radioIP.toString(), "Auth failed, try rebooting the radio.");
|
||||||
|
qDebug() << this->metaObject()->className() << ": Auth failed, try rebooting the radio.";
|
||||||
|
}
|
||||||
|
else if (in->error == 0x00000000 && in->disc == 0x01)
|
||||||
{
|
{
|
||||||
emit haveNetworkError(radioIP.toString(), "Got radio disconnected.");
|
emit haveNetworkError(radioIP.toString(), "Got radio disconnected.");
|
||||||
qDebug() << this->metaObject()->className() << ": Got radio disconnected.";
|
qDebug() << this->metaObject()->className() << ": Got radio disconnected.";
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case(LOGIN_RESPONSE_SIZE): // Response to Login packet.
|
||||||
|
|
||||||
case(96): // Response to Login packet.
|
|
||||||
if (r.mid(0, 6) == QByteArrayLiteral("\x60\x00\x00\x00\x00\x00"))
|
|
||||||
{
|
{
|
||||||
if (r.mid(48, 4) == QByteArrayLiteral("\xff\xff\xff\xfe"))
|
login_response_packet_t in = (login_response_packet_t)r.constData();
|
||||||
|
if (in->error == 0xfeffffff)
|
||||||
{
|
{
|
||||||
emit haveNetworkStatus("Invalid Username/Password");
|
emit haveNetworkStatus("Invalid Username/Password");
|
||||||
qDebug() << this->metaObject()->className() << ": Invalid Username/Password";
|
qDebug() << this->metaObject()->className() << ": Invalid Username/Password";
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (!isAuthenticated)
|
else if (!isAuthenticated)
|
||||||
{
|
{
|
||||||
emit haveNetworkStatus("Radio Login OK!");
|
|
||||||
qDebug() << this->metaObject()->className() << ": Received Login OK";
|
if (in->tokrequest == tokRequest)
|
||||||
|
{
|
||||||
authId = r.mid(0x1a, 6);
|
emit haveNetworkStatus("Radio Login OK!");
|
||||||
sendToken(0x02);
|
qDebug() << this->metaObject()->className() << ": Received matching token response to our request";
|
||||||
tokenTimer.start(TOKEN_RENEWAL); // Start token request timer
|
token = in->token;
|
||||||
|
sendToken(0x02);
|
||||||
isAuthenticated = true;
|
tokenTimer.start(TOKEN_RENEWAL); // Start token request timer
|
||||||
|
isAuthenticated = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << this->metaObject()->className() << ": Token response did not match, sent:" << tokRequest << " got " << in->tokrequest;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.mid(0x40, 4) == QByteArrayLiteral("FTTH"))
|
if (!strcmp(in->connection, "FTTH"))
|
||||||
{
|
{
|
||||||
highBandwidthConnection = true;
|
highBandwidthConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << this->metaObject()->className() << ": Detected connection speed " << QString::fromUtf8(parseNullTerminatedString(r, 0x40));
|
qDebug() << this->metaObject()->className() << ": Detected connection speed " << QString::fromUtf8(parseNullTerminatedString(r, 0x40));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case (CONNINFO_SIZE):
|
||||||
case (144):
|
|
||||||
if (!serialAndAudioOpened && r.mid(0, 6) == QByteArrayLiteral("\x90\x00\x00\x00\x00\x00") && r[0x60] == (char)0x00)
|
|
||||||
{
|
{
|
||||||
devName = parseNullTerminatedString(r, 0x40);
|
conninfo_packet_t in = (conninfo_packet_t)r.constData();
|
||||||
QHostAddress ip = QHostAddress(qFromBigEndian<quint32>(r.mid(0x84, 4)));
|
|
||||||
if (!ip.isEqual(QHostAddress("0.0.0.0")) && parseNullTerminatedString(r, 0x64) != compName) // || ip != localIP ) // TODO: More testing of IP address detection code!
|
if (!streamOpened && !in->ready)
|
||||||
{
|
{
|
||||||
emit haveNetworkStatus(QString::fromUtf8(devName) + "in use by: " + QString::fromUtf8(parseNullTerminatedString(r, 0x64)) + " (" + ip.toString() + ")");
|
devName = parseNullTerminatedString(in->computer,0x00);
|
||||||
}
|
QHostAddress ip = QHostAddress(in->ipaddress);
|
||||||
else {
|
if (!ip.isEqual(QHostAddress("0.0.0.0")) && parseNullTerminatedString(r, 0x64) != compName) // || ip != localIP ) // TODO: More testing of IP address detection code!
|
||||||
emit haveNetworkStatus(QString::fromUtf8(devName) + " available");
|
{
|
||||||
sendRequestSerialAndAudio();
|
emit haveNetworkStatus(QString::fromUtf8(devName) + "in use by: " + QString::fromUtf8(parseNullTerminatedString(r, 0x64)) + " (" + ip.toString() + ")");
|
||||||
}
|
sendControl(false, 0x00, in->seq); // Respond with an idle
|
||||||
}
|
}
|
||||||
else if (!serialAndAudioOpened && r.mid(0, 6) == QByteArrayLiteral("\x90\x00\x00\x00\x00\x00") && r[0x60] == (char)0x01)
|
else {
|
||||||
{
|
emit haveNetworkStatus(QString::fromUtf8(devName) + " available");
|
||||||
devName = parseNullTerminatedString(r, 0x40);
|
|
||||||
|
|
||||||
|
identa = in->identa;
|
||||||
|
identb = in->identb;
|
||||||
|
|
||||||
|
sendRequestStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!streamOpened && in->ready)
|
||||||
|
{
|
||||||
|
devName = parseNullTerminatedString(in->computer, 0);
|
||||||
|
|
||||||
civ = new udpCivData(localIP, radioIP, civPort);
|
civ = new udpCivData(localIP, radioIP, civPort);
|
||||||
audio = new udpAudio(localIP, radioIP, audioPort,rxBufferSize,rxSampleRate, rxCodec,txSampleRate,txCodec);
|
audio = new udpAudio(localIP, radioIP, audioPort, rxBufferSize, rxSampleRate, rxCodec, txSampleRate, txCodec);
|
||||||
|
|
||||||
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
|
||||||
QObject::connect(this, SIGNAL(haveChangeBufferSize(quint16)), audio, SLOT(changeBufferSize(quint16)));
|
QObject::connect(this, SIGNAL(haveChangeBufferSize(quint16)), audio, SLOT(changeBufferSize(quint16)));
|
||||||
|
|
||||||
serialAndAudioOpened = true;
|
streamOpened = true;
|
||||||
|
|
||||||
emit haveNetworkStatus(QString::fromUtf8(devName));
|
emit haveNetworkStatus(QString::fromUtf8(devName));
|
||||||
|
|
||||||
qDebug() << this->metaObject()->className() << "Got serial and audio request success, device name: " << QString::fromUtf8(devName);
|
qDebug() << this->metaObject()->className() << "Got serial and audio request success, device name: " << QString::fromUtf8(devName);
|
||||||
|
|
||||||
// Stuff can change in the meantime because of a previous login...
|
// Stuff can change in the meantime because of a previous login...
|
||||||
remoteId = qFromLittleEndian<quint32>(r.mid(0x08, 4));
|
remoteId = in->sentid;
|
||||||
myId = qFromLittleEndian<quint32>(r.mid(0x0c, 4));
|
myId = in->rcvdid;
|
||||||
authId = r.mid(0x1a, 6);
|
tokRequest = in->tokrequest;
|
||||||
// Is there already somebody connected to the radio?
|
token = in->token;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
case (CAPABILITIES_SIZE):
|
||||||
case (168):
|
{
|
||||||
audioType = parseNullTerminatedString(r, 0x72);
|
capabilities_packet_t in = (capabilities_packet_t)r.constData();
|
||||||
devName = parseNullTerminatedString(r, 0x52);
|
audioType = parseNullTerminatedString(in->audio, 0);
|
||||||
replyId = r.mid(0x42, 16);
|
devName = parseNullTerminatedString(in->name, 0);
|
||||||
qDebug() << this->metaObject()->className() << "Received radio capabilities, Name:" <<
|
//replyId = r.mid(0x42, 16);
|
||||||
QString::fromUtf8(devName) << " Audio:" <<
|
qDebug() << this->metaObject()->className() << "Received radio capabilities, Name:" <<
|
||||||
QString::fromUtf8(audioType);
|
QString::fromUtf8(devName) << " Audio:" <<
|
||||||
|
QString::fromUtf8(audioType);
|
||||||
break;
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||||
r.clear();
|
r.clear();
|
||||||
datagram.clear();
|
datagram.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define SERIALAUDIO_SIZE 0x90
|
void udpHandler::sendRequestStream()
|
||||||
void udpHandler::sendRequestSerialAndAudio()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
QByteArray usernameEncoded;
|
QByteArray usernameEncoded;
|
||||||
passcode(username, usernameEncoded);
|
passcode(username, usernameEncoded);
|
||||||
int txSeqBufLengthMs = 50;
|
int txSeqBufLengthMs = 300;
|
||||||
|
|
||||||
quint8 p[SERIALAUDIO_SIZE];
|
conninfo_packet p;
|
||||||
memset(p, 0x0, sizeof(p));
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
qToLittleEndian(sizeof(p), p + 0x00);
|
p.len = sizeof(p);
|
||||||
qToLittleEndian(myId, p + 0x08);
|
p.sentid = myId;
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
p.rcvdid = remoteId;
|
||||||
memcpy(p + 0x13, QByteArrayLiteral("\x80\x01\x03").constData(), 3);
|
p.code = 0x0180;
|
||||||
qToLittleEndian(authInnerSendSeq, p + 0x16);
|
p.res = 0x03;
|
||||||
memcpy(p + 0x1a, authId.constData(), authId.length());
|
p.resb = 0x8010;
|
||||||
memcpy(p + 0x20, replyId.constData(), replyId.length());
|
p.identa = identa;
|
||||||
memcpy(p + 0x40, devName.constData(), devName.length());
|
p.identb = identb;
|
||||||
memcpy(p + 0x60, usernameEncoded.constData(), usernameEncoded.length());
|
p.innerseq = authInnerSendSeq;
|
||||||
memcpy(p + 0x70, QByteArrayLiteral("\x01\x01").constData(), 2);
|
p.tokrequest = tokRequest;
|
||||||
p[0x72] = rxCodec;
|
p.token = token;
|
||||||
p[0x73] = txCodec;
|
memcpy(&p.name, devName.constData(), devName.length());
|
||||||
qToBigEndian(rxSampleRate, p + 0x76);
|
p.rxenable = 1;
|
||||||
qToBigEndian(txSampleRate, p + 0x7a);
|
p.txenable = 1;
|
||||||
qToBigEndian(civPort, p + 0x7e);
|
p.rxcodec = rxCodec;
|
||||||
qToBigEndian(audioPort, p + 0x82);
|
p.txcodec = txCodec;
|
||||||
qToBigEndian(txSeqBufLengthMs, p + 0x86);
|
memcpy(&p.ident, QByteArrayLiteral("&96D7").constData(), 5);
|
||||||
p[0x88] = 0x01;
|
p.rxsample = qToBigEndian((quint32)rxSampleRate);
|
||||||
|
p.txsample = qToBigEndian((quint32)txSampleRate);
|
||||||
|
p.civport = qToBigEndian((quint32)civPort);
|
||||||
|
p.audioport = qToBigEndian((quint32)audioPort);
|
||||||
|
p.txbuffer = qToBigEndian((quint32)txSeqBufLengthMs);
|
||||||
|
|
||||||
authInnerSendSeq++;
|
authInnerSendSeq++;
|
||||||
|
|
||||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,53 +359,52 @@ void udpHandler::sendAreYouThere()
|
||||||
udpBase::sendControl(false,0x03,0x00);
|
udpBase::sendControl(false,0x03,0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOGIN_SIZE 0x80
|
|
||||||
void udpHandler::sendLogin() // Only used on control stream.
|
void udpHandler::sendLogin() // Only used on control stream.
|
||||||
{
|
{
|
||||||
|
|
||||||
qDebug() << this->metaObject()->className() << ": Sending login packet";
|
qDebug() << this->metaObject()->className() << ": Sending login packet";
|
||||||
|
|
||||||
uint16_t authStartID = rand() | rand() << 8;
|
tokRequest = static_cast<quint16>(rand() | rand() << 8); // Generate random token request.
|
||||||
//uint16_t authStartID = 0x7866;
|
|
||||||
QByteArray usernameEncoded;
|
QByteArray usernameEncoded;
|
||||||
QByteArray passwordEncoded;
|
QByteArray passwordEncoded;
|
||||||
passcode(username, usernameEncoded);
|
passcode(username, usernameEncoded);
|
||||||
passcode(password, passwordEncoded);
|
passcode(password, passwordEncoded);
|
||||||
|
|
||||||
quint8 p[LOGIN_SIZE];
|
login_packet p;
|
||||||
memset(p, 0x0, sizeof(p));
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
qToLittleEndian(sizeof(p), p + 0x00);
|
p.len = sizeof(p);
|
||||||
qToLittleEndian(myId, p + 0x08);
|
p.sentid = myId;
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
p.rcvdid = remoteId;
|
||||||
memcpy(p + 0x13, QByteArrayLiteral("\x70\x01").constData(), 2);
|
p.code = 0x0170; // Not sure what this is?
|
||||||
qToLittleEndian(authInnerSendSeq, p + 0x17);
|
p.innerseq = authInnerSendSeq;
|
||||||
qToLittleEndian(authStartID, p + 0x20);
|
p.tokrequest = tokRequest;
|
||||||
memcpy(p + 0x40, usernameEncoded.constData(), usernameEncoded.length());
|
memcpy(p.username, usernameEncoded.constData(), usernameEncoded.length());
|
||||||
memcpy(p + 0x50, passwordEncoded.constData(), passwordEncoded.length());
|
memcpy(p.password, passwordEncoded.constData(), passwordEncoded.length());
|
||||||
memcpy(p + 0x60, compName.constData(), compName.length());
|
memcpy(p.name, compName.constData(), compName.length());
|
||||||
|
|
||||||
authInnerSendSeq++;
|
authInnerSendSeq++;
|
||||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TOKEN_SIZE 0x40
|
|
||||||
void udpHandler::sendToken(uint8_t magic)
|
void udpHandler::sendToken(uint8_t magic)
|
||||||
{
|
{
|
||||||
qDebug() << this->metaObject()->className() << "Sending Token request: " << magic;
|
qDebug() << this->metaObject()->className() << "Sending Token request: " << magic;
|
||||||
|
|
||||||
quint8 p[TOKEN_SIZE];
|
token_packet p;
|
||||||
memset(p, 0x0, sizeof(p));
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
qToLittleEndian(sizeof(p), p + 0x00);
|
p.len = sizeof(p);
|
||||||
qToLittleEndian(myId, p + 0x08);
|
p.sentid = myId;
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
p.rcvdid = remoteId;
|
||||||
memcpy(p + 0x13, QByteArrayLiteral("\x30\x01").constData(), 2);
|
p.code = 0x0130; // Not sure what this is?
|
||||||
qToLittleEndian(magic, p + 0x15);
|
p.res = magic;
|
||||||
qToLittleEndian(authInnerSendSeq, p + 0x17);
|
p.innerseq = authInnerSendSeq;
|
||||||
memcpy(p + 0x1a, authId.constData(), authId.length());
|
p.tokrequest = tokRequest;
|
||||||
|
p.token = token;
|
||||||
|
|
||||||
authInnerSendSeq++;
|
authInnerSendSeq++;
|
||||||
sendTrackedPacket(QByteArray::fromRawData((const char *)p, sizeof(p)));
|
sendTrackedPacket(QByteArray::fromRawData((const char *)p.packet, sizeof(p)));
|
||||||
tokenTimer.start(100); // Set 100ms timer for retry (this will be cancelled if a response is received)
|
tokenTimer.start(100); // Set 100ms timer for retry (this will be cancelled if a response is received)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -416,21 +440,19 @@ udpCivData::~udpCivData() {
|
||||||
sendOpenClose(true);
|
sendOpenClose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CIV_SIZE 0x15
|
|
||||||
void udpCivData::send(QByteArray d)
|
void udpCivData::send(QByteArray d)
|
||||||
{
|
{
|
||||||
// qDebug() << "Sending: (" << d.length() << ") " << d;
|
// qDebug() << "Sending: (" << d.length() << ") " << d;
|
||||||
|
data_packet p;
|
||||||
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
|
p.len = sizeof(p);
|
||||||
|
p.sentid = myId;
|
||||||
|
p.rcvdid = remoteId;
|
||||||
|
p.reply = (char)0xc1;
|
||||||
|
p.datalen = d.length();
|
||||||
|
p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN!
|
||||||
|
|
||||||
quint8 p[CIV_SIZE];
|
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||||
memset(p, 0x0, sizeof(p));
|
|
||||||
qToLittleEndian((quint16)sizeof(p) + d.length(), p + 0x00);
|
|
||||||
qToLittleEndian(myId, p + 0x08);
|
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
|
||||||
p[0x10] = 0xc1;
|
|
||||||
qToLittleEndian((quint16)d.length(), p + 0x11);
|
|
||||||
qToBigEndian(sendSeqB, p + 0x13); // THIS IS BIG ENDIAN!
|
|
||||||
|
|
||||||
QByteArray t = QByteArray::fromRawData((const char*)p, sizeof(p));
|
|
||||||
t.append(d);
|
t.append(d);
|
||||||
sendTrackedPacket(t);
|
sendTrackedPacket(t);
|
||||||
sendSeqB++;
|
sendSeqB++;
|
||||||
|
@ -438,7 +460,6 @@ void udpCivData::send(QByteArray d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define OPENCLOSE_SIZE 0x16
|
|
||||||
void udpCivData::sendOpenClose(bool close)
|
void udpCivData::sendOpenClose(bool close)
|
||||||
{
|
{
|
||||||
uint8_t magic = 0x05;
|
uint8_t magic = 0x05;
|
||||||
|
@ -467,45 +488,49 @@ void udpCivData::sendOpenClose(bool close)
|
||||||
|
|
||||||
void udpCivData::dataReceived()
|
void udpCivData::dataReceived()
|
||||||
{
|
{
|
||||||
while (udp->hasPendingDatagrams()) {
|
while (udp->hasPendingDatagrams())
|
||||||
|
{
|
||||||
QNetworkDatagram datagram = udp->receiveDatagram();
|
QNetworkDatagram datagram = udp->receiveDatagram();
|
||||||
//qDebug() << "Received: " << datagram.data();
|
//qDebug() << "Received: " << datagram.data();
|
||||||
QByteArray r = datagram.data();
|
QByteArray r = datagram.data();
|
||||||
|
|
||||||
switch (r.length())
|
switch (r.length())
|
||||||
{
|
{
|
||||||
case (16): // Response to pkt0
|
case (CONTROL_SIZE): // Control packet
|
||||||
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
|
|
||||||
{
|
{
|
||||||
// Update remoteId
|
control_packet_t in = (control_packet_t)r.constData();
|
||||||
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
|
if (in->type == 0x06)
|
||||||
sendOpenClose(false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (r.length() > 21) {
|
|
||||||
// First check if we are missing any packets?
|
|
||||||
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
||||||
if (lastReceivedSeq == 0 || lastReceivedSeq > gotSeq) {
|
|
||||||
lastReceivedSeq = gotSeq;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
|
|
||||||
// Do we need to request a retransmit?
|
|
||||||
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lastReceivedSeq = gotSeq;
|
|
||||||
|
|
||||||
quint8 temp = r[0] - 0x15;
|
|
||||||
if ((quint8)r[16] == 0xc1 && (quint8)r[17] == temp)
|
|
||||||
{
|
{
|
||||||
emit receive(r.mid(0x15));
|
// Update remoteId
|
||||||
}
|
remoteId = in->sentid;
|
||||||
|
sendOpenClose(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
default:
|
||||||
|
{
|
||||||
|
if (r.length() > 21) {
|
||||||
|
// First check if we are missing any packets?
|
||||||
|
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
||||||
|
if (lastReceivedSeq == 0 || lastReceivedSeq > gotSeq) {
|
||||||
|
lastReceivedSeq = gotSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
|
||||||
|
// Do we need to request a retransmit?
|
||||||
|
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lastReceivedSeq = gotSeq;
|
||||||
|
|
||||||
|
quint8 temp = r[0] - 0x15;
|
||||||
|
if ((quint8)r[16] == 0xc1 && (quint8)r[17] == temp)
|
||||||
|
{
|
||||||
|
emit receive(r.mid(0x15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||||
r.clear();
|
r.clear();
|
||||||
|
@ -617,29 +642,25 @@ udpAudio::~udpAudio()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define TXAUDIO_SIZE 0x18
|
|
||||||
void udpAudio::sendTxAudio()
|
void udpAudio::sendTxAudio()
|
||||||
{
|
{
|
||||||
quint8 p[TXAUDIO_SIZE];
|
|
||||||
memset(p, 0x0, sizeof(p));
|
|
||||||
qToLittleEndian(myId, p + 0x08);
|
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
|
||||||
p[0x10] = 0x80;
|
|
||||||
|
|
||||||
if (txaudio->chunkAvailable) {
|
if (txaudio->chunkAvailable) {
|
||||||
QByteArray audio;
|
QByteArray audio;
|
||||||
txaudio->getNextAudioChunk(audio);
|
txaudio->getNextAudioChunk(audio);
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
while (counter < audio.length()) {
|
while (counter < audio.length()) {
|
||||||
QByteArray tx = QByteArray::fromRawData((const char*)p, sizeof(p));
|
|
||||||
QByteArray partial = audio.mid(counter, 1364);
|
QByteArray partial = audio.mid(counter, 1364);
|
||||||
|
ping_packet p;
|
||||||
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
|
p.len = sizeof(p);
|
||||||
|
p.sentid = myId;
|
||||||
|
p.rcvdid = remoteId;
|
||||||
|
p.reply = (char)0x80;
|
||||||
|
p.datalen = partial.length();
|
||||||
|
p.sendseq = qToBigEndian(sendAudioSeq); // THIS IS BIG ENDIAN!
|
||||||
|
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
|
||||||
tx.append(partial);
|
tx.append(partial);
|
||||||
tx[0] = static_cast<quint8>(tx.length() & 0xff);
|
|
||||||
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
|
|
||||||
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
|
|
||||||
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
|
|
||||||
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
|
|
||||||
tx[23] = static_cast<quint8>(partial.length() & 0xff);
|
|
||||||
counter = counter + partial.length();
|
counter = counter + partial.length();
|
||||||
//qDebug() << "Sending audio packet length: " << tx.length();
|
//qDebug() << "Sending audio packet length: " << tx.length();
|
||||||
sendTrackedPacket(tx);
|
sendTrackedPacket(tx);
|
||||||
|
@ -664,10 +685,11 @@ void udpAudio::dataReceived()
|
||||||
|
|
||||||
switch (r.length())
|
switch (r.length())
|
||||||
{
|
{
|
||||||
case (16): // Response to idle packet handled in udpBase
|
case (16): // Response to control packet handled in udpBase
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
{
|
||||||
/* Audio packets start as follows:
|
/* Audio packets start as follows:
|
||||||
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
|
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
|
||||||
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
|
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
|
||||||
|
@ -675,7 +697,7 @@ void udpAudio::dataReceived()
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
|
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\x44\x02") ||
|
r.mid(0, 2) == QByteArrayLiteral("\x44\x02") ||
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\xd8\x03") ||
|
r.mid(0, 2) == QByteArrayLiteral("\xd8\x03") ||
|
||||||
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
||||||
|
@ -686,7 +708,7 @@ void udpAudio::dataReceived()
|
||||||
lastReceivedSeq = gotSeq;
|
lastReceivedSeq = gotSeq;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint16_t f = lastReceivedSeq+1 ; f < gotSeq; f++) {
|
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
|
||||||
// Do we need to request a retransmit?
|
// Do we need to request a retransmit?
|
||||||
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
|
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
|
||||||
}
|
}
|
||||||
|
@ -697,6 +719,7 @@ void udpAudio::dataReceived()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
udpBase::dataReceived(r); // Call parent function to process the rest.
|
udpBase::dataReceived(r); // Call parent function to process the rest.
|
||||||
r.clear();
|
r.clear();
|
||||||
|
@ -708,6 +731,7 @@ void udpAudio::dataReceived()
|
||||||
|
|
||||||
void udpBase::init()
|
void udpBase::init()
|
||||||
{
|
{
|
||||||
|
timeStarted.start();
|
||||||
udp = new QUdpSocket(this);
|
udp = new QUdpSocket(this);
|
||||||
udp->bind(); // Bind to random port.
|
udp->bind(); // Bind to random port.
|
||||||
localPort = udp->localPort();
|
localPort = udp->localPort();
|
||||||
|
@ -740,138 +764,142 @@ void udpBase::dataReceived(QByteArray r)
|
||||||
{
|
{
|
||||||
switch (r.length())
|
switch (r.length())
|
||||||
{
|
{
|
||||||
case (16): // Empty response used for simple comms and retransmit requests.
|
case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests.
|
||||||
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x04\x00\x00\x00")) {
|
{
|
||||||
// If timer is active, stop it as they are obviously there!
|
control_packet_t in = (control_packet_t)r.constData();
|
||||||
qDebug() << this->metaObject()->className() << ": Received I am here";
|
// We should check for missing packets here
|
||||||
areYouThereCounter = 0;
|
// for now just store received seq.
|
||||||
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
|
lastReceivedSeq = in->seq;
|
||||||
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
|
if (in->type == 0x04) {
|
||||||
sendControl(false,0x06,0x01); // Send Are you ready - untracked.
|
qDebug() << this->metaObject()->className() << ": Received I am here";
|
||||||
}
|
areYouThereCounter = 0;
|
||||||
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x00\x00")) {
|
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
|
||||||
// Just get the seqnum and ignore the rest.
|
remoteId = in->sentid;
|
||||||
lastReceivedSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
sendControl(false, 0x06, 0x01); // Send Are you ready - untracked.
|
||||||
}
|
}
|
||||||
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x01\x00")) {
|
else if (in->type == 0x06)
|
||||||
// retransmit request
|
|
||||||
// Send an idle with the requested seqnum if not found.
|
|
||||||
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
||||||
bool found=false;
|
|
||||||
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
|
|
||||||
{
|
{
|
||||||
packetsLost++;
|
// Just get the seqnum and ignore the rest.
|
||||||
if (txSeqBuf[f].seqNum == gotSeq) {
|
}
|
||||||
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
|
else if (in->type == 0x01) // retransmit request
|
||||||
QMutexLocker locker(&mutex);
|
{
|
||||||
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
|
// retransmit request
|
||||||
found = true;
|
// Send an idle with the requested seqnum if not found.
|
||||||
break;
|
bool found = false;
|
||||||
|
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
|
||||||
|
{
|
||||||
|
packetsLost++;
|
||||||
|
if (txSeqBuf[f].seqNum == in->seq) {
|
||||||
|
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
// Packet was not found in buffer
|
||||||
|
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
||||||
|
sendControl(false, 0, in->seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
// Packet was not found in buffer
|
|
||||||
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
|
||||||
sendControl(false, 0, gotSeq);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (r.mid(0, 6) == QByteArrayLiteral("\x18\x00\x00\x00\x01\x00"))
|
case (PING_SIZE): // ping packet
|
||||||
{ // retransmit range request, can contain multiple ranges.
|
{
|
||||||
for (int f = 16; f < r.length() - 4; f = f + 4)
|
ping_packet_t in = (ping_packet_t)r.constData();
|
||||||
|
if (in->type == 0x07)
|
||||||
{
|
{
|
||||||
quint16 start = qFromLittleEndian<quint16>(r.mid(f, 2));
|
// It is a ping request/response
|
||||||
quint16 end = qFromLittleEndian<quint16>(r.mid(f + 2, 2));
|
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
||||||
packetsLost=packetsLost + (end-start);
|
if (in->reply == 0x00)
|
||||||
qDebug() << this->metaObject()->className() << ": Retransmit range request for:" << start << " to " << end;
|
{
|
||||||
for (quint16 gotSeq = start; gotSeq <= end; gotSeq++)
|
ping_packet p;
|
||||||
{
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
bool found=false;
|
p.len = sizeof(p);
|
||||||
for (int h = txSeqBuf.length() - 1; h >= 0; h--)
|
p.type = 0x07;
|
||||||
if (txSeqBuf[h].seqNum == gotSeq) {
|
p.sentid = myId;
|
||||||
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
|
p.rcvdid = remoteId;
|
||||||
QMutexLocker locker(&mutex);
|
p.reply = 0x01;
|
||||||
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
|
p.seq = in->seq;
|
||||||
found = true;
|
p.time = in->time;
|
||||||
break;
|
QMutexLocker locker(&mutex);
|
||||||
}
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||||
if (!found)
|
}
|
||||||
|
else if (r[0x10] == (char)0x01) {
|
||||||
|
if (in->seq == pingSendSeq)
|
||||||
{
|
{
|
||||||
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
// This is response to OUR request so increment counter
|
||||||
sendControl(false, 0, gotSeq);
|
pingSendSeq++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Not sure what to do here, need to spend more time with the protocol but try sending ping with same seq next time?
|
||||||
|
//qDebug() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << gotSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qDebug() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case (0x18):
|
||||||
|
{
|
||||||
|
if (r.mid(0, 6) == QByteArrayLiteral("\x18\x00\x00\x00\x01\x00"))
|
||||||
|
{ // retransmit range request, can contain multiple ranges.
|
||||||
|
for (int f = 16; f < r.length() - 4; f = f + 4)
|
||||||
|
{
|
||||||
|
quint16 start = qFromLittleEndian<quint16>(r.mid(f, 2));
|
||||||
|
quint16 end = qFromLittleEndian<quint16>(r.mid(f + 2, 2));
|
||||||
|
packetsLost = packetsLost + (end - start);
|
||||||
|
qDebug() << this->metaObject()->className() << ": Retransmit range request for:" << start << " to " << end;
|
||||||
|
for (quint16 gotSeq = start; gotSeq <= end; gotSeq++)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (int h = txSeqBuf.length() - 1; h >= 0; h--)
|
||||||
|
if (txSeqBuf[h].seqNum == gotSeq) {
|
||||||
|
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
||||||
|
sendControl(false, 0, gotSeq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
default:
|
||||||
|
break;
|
||||||
case (21):
|
|
||||||
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00"))
|
|
||||||
{
|
|
||||||
// It is a ping request/response
|
|
||||||
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
||||||
|
|
||||||
if (r[0x10] == (char)0x00)
|
|
||||||
{
|
|
||||||
quint8 p[0x15];
|
|
||||||
memset(p, 0x0, sizeof(p));
|
|
||||||
qToLittleEndian(sizeof(p), p + 0x00);
|
|
||||||
p[0x04] = 0x07;
|
|
||||||
qToLittleEndian(myId, p + 0x08);
|
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
|
||||||
p[0x10] = (char)0x01;
|
|
||||||
p[0x11] = (char)r[0x11];
|
|
||||||
p[0x12] = (char)r[0x12];
|
|
||||||
p[0x13] = (char)r[0x13];
|
|
||||||
p[0x14] = (char)r[0x14];
|
|
||||||
|
|
||||||
QMutexLocker locker(&mutex);
|
|
||||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
||||||
}
|
|
||||||
else if (r[0x10] == (char)0x01) {
|
|
||||||
if (gotSeq == pingSendSeq)
|
|
||||||
{
|
|
||||||
// This is response to OUR request so increment counter
|
|
||||||
pingSendSeq++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Not sure what to do here, need to spend more time with the protocol but try sending ping with same seq next time?
|
|
||||||
//qDebug() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << gotSeq;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
qDebug() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CONTROL_SIZE 0x10
|
|
||||||
// Used to send idle and other "control" style messages
|
// Used to send idle and other "control" style messages
|
||||||
void udpBase::sendControl(bool tracked=true, quint8 id=0, quint16 seq=0)
|
void udpBase::sendControl(bool tracked=true, quint8 type=0, quint16 seq=0)
|
||||||
{
|
{
|
||||||
quint8 p[CONTROL_SIZE];
|
control_packet p;
|
||||||
memset(p, 0x0, sizeof(p));
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
qToLittleEndian((quint16)sizeof(p), p + 0x00);
|
p.len = sizeof(p);
|
||||||
qToLittleEndian(myId, p + 0x08);
|
p.type = type;
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
p.sentid = myId;
|
||||||
p[0x04] = id;
|
p.rcvdid = remoteId;
|
||||||
|
|
||||||
lastControlPacketSentTime = QDateTime::currentDateTime(); // Is this used?
|
|
||||||
if (!tracked) {
|
if (!tracked) {
|
||||||
qToLittleEndian(seq, p + 0x06);
|
p.seq = seq;
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
|
||||||
}
|
}
|
||||||
if (idleTimer.isActive()) {
|
if (idleTimer.isActive()) {
|
||||||
idleTimer.start(IDLE_PERIOD); // Reset idle counter if it's running
|
idleTimer.start(IDLE_PERIOD); // Reset idle counter if it's running
|
||||||
|
@ -879,22 +907,20 @@ void udpBase::sendControl(bool tracked=true, quint8 id=0, quint16 seq=0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PING_SIZE 0x15
|
|
||||||
// Send periodic ping packets
|
// Send periodic ping packets
|
||||||
void udpBase::sendPing()
|
void udpBase::sendPing()
|
||||||
{
|
{
|
||||||
quint32 pingSeq = (quint32)((quint8)(rand() & 0xff)) | (quint16)innerSendSeq << 8 | (quint8)0x06 << 24;
|
ping_packet p;
|
||||||
|
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
|
||||||
quint8 p[PING_SIZE];
|
p.len = sizeof(p);
|
||||||
memset(p, 0x0, sizeof(p));
|
p.type = 0x07;
|
||||||
qToLittleEndian((quint16)sizeof(p), p + 0x00);
|
p.sentid = myId;
|
||||||
p[0x04] = 0x07;
|
p.rcvdid = remoteId;
|
||||||
qToLittleEndian(myId, p + 0x08);
|
p.seq = pingSendSeq;
|
||||||
qToLittleEndian(remoteId, p + 0x0c);
|
p.time = timeStarted.msecsSinceStartOfDay();
|
||||||
qToLittleEndian(pingSeq, p + 0x11);
|
|
||||||
lastPingSentTime = QDateTime::currentDateTime();
|
lastPingSentTime = QDateTime::currentDateTime();
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
|
||||||
innerSendSeq++;
|
innerSendSeq++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
18
udphandler.h
18
udphandler.h
|
@ -21,6 +21,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "audiohandler.h"
|
#include "audiohandler.h"
|
||||||
|
#include "packettypes.h"
|
||||||
|
|
||||||
#define PURGE_SECONDS 5
|
#define PURGE_SECONDS 5
|
||||||
#define TOKEN_RENEWAL 60000
|
#define TOKEN_RENEWAL 60000
|
||||||
|
@ -46,6 +47,8 @@ public:
|
||||||
void sendPing(); // Periodic type 0x07 ping packet sending
|
void sendPing(); // Periodic type 0x07 ping packet sending
|
||||||
void sendControl(bool tracked,quint8 id, quint16 seq);
|
void sendControl(bool tracked,quint8 id, quint16 seq);
|
||||||
|
|
||||||
|
QTime timeStarted;
|
||||||
|
|
||||||
QUdpSocket* udp=Q_NULLPTR;
|
QUdpSocket* udp=Q_NULLPTR;
|
||||||
uint32_t myId = 0;
|
uint32_t myId = 0;
|
||||||
uint32_t remoteId = 0;
|
uint32_t remoteId = 0;
|
||||||
|
@ -89,8 +92,6 @@ public:
|
||||||
QDateTime lastPingSentTime;
|
QDateTime lastPingSentTime;
|
||||||
uint16_t pingSendSeq = 0;
|
uint16_t pingSendSeq = 0;
|
||||||
|
|
||||||
QDateTime lastControlPacketSentTime;
|
|
||||||
|
|
||||||
quint16 areYouThereCounter=0;
|
quint16 areYouThereCounter=0;
|
||||||
|
|
||||||
quint32 packetsSent=0;
|
quint32 packetsSent=0;
|
||||||
|
@ -184,7 +185,7 @@ public:
|
||||||
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
|
||||||
~udpHandler();
|
~udpHandler();
|
||||||
|
|
||||||
bool serialAndAudioOpened = false;
|
bool streamOpened = false;
|
||||||
|
|
||||||
udpCivData* civ = Q_NULLPTR;
|
udpCivData* civ = Q_NULLPTR;
|
||||||
udpAudio* audio = Q_NULLPTR;
|
udpAudio* audio = Q_NULLPTR;
|
||||||
|
@ -203,12 +204,13 @@ signals:
|
||||||
void haveChangeBufferSize(quint16 value);
|
void haveChangeBufferSize(quint16 value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
void sendAreYouThere();
|
void sendAreYouThere();
|
||||||
|
|
||||||
void dataReceived();
|
void dataReceived();
|
||||||
|
|
||||||
void sendRequestSerialAndAudio();
|
void sendRequestStream();
|
||||||
void sendLogin();
|
void sendLogin();
|
||||||
void sendToken(uint8_t magic);
|
void sendToken(uint8_t magic);
|
||||||
|
|
||||||
|
@ -236,8 +238,12 @@ private:
|
||||||
QByteArray compName;
|
QByteArray compName;
|
||||||
QByteArray audioType;
|
QByteArray audioType;
|
||||||
QByteArray replyId;
|
QByteArray replyId;
|
||||||
QByteArray authId;
|
quint16 tokRequest;
|
||||||
|
quint32 token;
|
||||||
|
// These are for stream ident info.
|
||||||
|
char identa;
|
||||||
|
quint32 identb;
|
||||||
|
|
||||||
QTimer tokenTimer;
|
QTimer tokenTimer;
|
||||||
QTimer areYouThereTimer;
|
QTimer areYouThereTimer;
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,8 @@ HEADERS += wfmain.h \
|
||||||
calibrationwindow.h \
|
calibrationwindow.h \
|
||||||
satellitesetup.h \
|
satellitesetup.h \
|
||||||
udpserversetup.h \
|
udpserversetup.h \
|
||||||
udpserver.h
|
udpserver.h \
|
||||||
|
packettypes.h
|
||||||
|
|
||||||
|
|
||||||
FORMS += wfmain.ui \
|
FORMS += wfmain.ui \
|
||||||
|
|
Ładowanie…
Reference in New Issue