Improve missing packet handling

merge-requests/9/merge
Phil Taylor 2022-01-27 19:11:16 +00:00
rodzic 4a1be30c40
commit 2f4fe061b3
5 zmienionych plików z 389 dodań i 409 usunięć

Wyświetl plik

@ -14,7 +14,7 @@
#define RETRANSMIT_PERIOD 100 // How often to attempt retransmit
#define LOCK_PERIOD 10 // How long to try to lock mutex (ms)
#define STALE_CONNECTION 15 // Not heard from in this many seconds
#define BUFSIZE 50 // Number of packets to buffer
#define BUFSIZE 500 // Number of packets to buffer
#define TXAUDIO_PERIOD 20

Wyświetl plik

@ -48,14 +48,16 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q
servermain::~servermain()
{
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio.rigThread != Q_NULLPTR)
if (radio->rigThread != Q_NULLPTR)
{
radio.rigThread->quit();
radio.rigThread->wait();
radio->rigThread->quit();
radio->rigThread->wait();
}
delete radio; // This has been created by new in loadSettings();
}
serverConfig.rigs.clear();
if (serverThread != Q_NULLPTR) {
serverThread->quit();
serverThread->wait();
@ -79,26 +81,14 @@ void servermain::openRig()
makeRig();
if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty()))
{
findSerialPort();
} else {
if(serialPortCL.isEmpty())
{
serialPortRig = prefs.serialPortRadio;
} else {
serialPortRig = serialPortCL;
}
}
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
//qInfo(logSystem()) << "Opening rig";
if (radio.rigThread != Q_NULLPTR)
if (radio->rigThread != Q_NULLPTR)
{
//qInfo(logSystem()) << "Got rig";
QMetaObject::invokeMethod(radio.rig, [=]() {
radio.rig->commSetup(radio.civAddr, radio.serialPort, radio.baudRate, QString("none"));
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"));
}, Qt::QueuedConnection);
}
}
@ -106,49 +96,49 @@ void servermain::openRig()
void servermain::makeRig()
{
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio.rigThread == Q_NULLPTR)
if (radio->rigThread == Q_NULLPTR)
{
qInfo(logSystem()) << "Creating new rigThread()";
radio.rig = new rigCommander(radio.guid);
radio.rigThread = new QThread(this);
radio->rig = new rigCommander(radio->guid);
radio->rigThread = new QThread(this);
// Thread:
radio.rig->moveToThread(radio.rigThread);
connect(radio.rigThread, SIGNAL(started()), radio.rig, SLOT(process()));
connect(radio.rigThread, SIGNAL(finished()), radio.rig, SLOT(deleteLater()));
radio.rigThread->start();
radio->rig->moveToThread(radio->rigThread);
connect(radio->rigThread, SIGNAL(started()), radio->rig, SLOT(process()));
connect(radio->rigThread, SIGNAL(finished()), radio->rig, SLOT(deleteLater()));
radio->rigThread->start();
// Rig status and Errors:
connect(radio.rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
connect(radio.rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus)));
connect(radio->rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
connect(radio->rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus)));
// Rig comm setup:
//connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio.rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)));
//connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio.rig, SLOT(commSetup(unsigned char, QString, quint32, QString)));
connect(this, SIGNAL(setRTSforPTT(bool)), radio.rig, SLOT(setRTSforPTT(bool)));
//connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), radio->rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)));
//connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32, QString)), radio->rig, SLOT(commSetup(unsigned char, QString, quint32, QString)));
connect(this, SIGNAL(setRTSforPTT(bool)), radio->rig, SLOT(setRTSforPTT(bool)));
connect(radio.rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32)));
connect(radio->rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32)));
connect(this, SIGNAL(sendCloseComm()), radio.rig, SLOT(closeComm()));
connect(this, SIGNAL(sendChangeLatency(quint16)), radio.rig, SLOT(changeLatency(quint16)));
//connect(this, SIGNAL(getRigCIV()), radio.rig, SLOT(findRigs()));
//connect(this, SIGNAL(setRigID(unsigned char)), radio.rig, SLOT(setRigID(unsigned char)));
connect(radio.rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
connect(radio.rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
connect(this, SIGNAL(sendCloseComm()), radio->rig, SLOT(closeComm()));
connect(this, SIGNAL(sendChangeLatency(quint16)), radio->rig, SLOT(changeLatency(quint16)));
//connect(this, SIGNAL(getRigCIV()), radio->rig, SLOT(findRigs()));
//connect(this, SIGNAL(setRigID(unsigned char)), radio->rig, SLOT(setRigID(unsigned char)));
connect(radio->rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
connect(radio->rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
connect(this, SIGNAL(requestRigState()), radio.rig, SLOT(sendState()));
connect(this, SIGNAL(stateUpdated()), radio.rig, SLOT(stateUpdated()));
connect(radio.rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*)));
connect(this, SIGNAL(requestRigState()), radio->rig, SLOT(sendState()));
connect(this, SIGNAL(stateUpdated()), radio->rig, SLOT(stateUpdated()));
connect(radio->rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*)));
//Other connections
connect(this, SIGNAL(setCIVAddr(unsigned char)), radio.rig, SLOT(setCIVAddr(unsigned char)));
connect(this, SIGNAL(setCIVAddr(unsigned char)), radio->rig, SLOT(setCIVAddr(unsigned char)));
connect(radio.rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool)));
connect(this, SIGNAL(setPTT(bool)), radio.rig, SLOT(setPTT(bool)));
connect(this, SIGNAL(getPTT()), radio.rig, SLOT(getPTT()));
connect(this, SIGNAL(getDebug()), radio.rig, SLOT(getDebug()));
if (radio.rigThread->isRunning()) {
connect(radio->rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool)));
connect(this, SIGNAL(setPTT(bool)), radio->rig, SLOT(setPTT(bool)));
connect(this, SIGNAL(getPTT()), radio->rig, SLOT(getPTT()));
connect(this, SIGNAL(getDebug()), radio->rig, SLOT(getDebug()));
if (radio->rigThread->isRunning()) {
qInfo(logSystem()) << "Rig thread is running";
}
else {
@ -163,15 +153,15 @@ void servermain::makeRig()
void servermain::removeRig()
{
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio.rigThread != Q_NULLPTR)
if (radio->rigThread != Q_NULLPTR)
{
radio.rigThread->disconnect();
radio.rig->disconnect();
delete radio.rigThread;
delete radio.rig;
radio.rig = Q_NULLPTR;
radio->rigThread->disconnect();
radio->rig->disconnect();
delete radio->rigThread;
delete radio->rig;
radio->rig = Q_NULLPTR;
}
}
}
@ -242,28 +232,28 @@ void servermain::receiveCommReady()
// Use the GUID to determine which radio the response is from
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid)))
if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid)))
{
qInfo(logSystem()) << "Received CommReady!! ";
if (radio.civAddr == 0)
if (radio->civAddr == 0)
{
// tell rigCommander to broadcast a request for all rig IDs.
// qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)";
if (!radio.rigAvailable) {
QMetaObject::invokeMethod(radio.rig, [=]() {
radio.rig->findRigs();
if (!radio->rigAvailable) {
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->findRigs();
}, Qt::QueuedConnection);
}
}
else {
// don't bother, they told us the CIV they want, stick with it.
// We still query the rigID to find the model, but at least we know the CIV.
qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio.civAddr;
QMetaObject::invokeMethod(radio.rig, [=]() {
radio.rig->setRigID(radio.civAddr);
qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio->civAddr;
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->setRigID(radio->civAddr);
}, Qt::QueuedConnection);
}
}
@ -279,10 +269,10 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps)
rigCommander* sender = qobject_cast<rigCommander*>(QObject::sender());
// Use the GUID to determine which radio the response is from
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !radio.rigAvailable && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid)))
if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !radio->rigAvailable && !memcmp(sender->getGUID(), radio->guid, sizeof(radio->guid)))
{
qDebug(logSystem()) << "Rig name: " << rigCaps.modelName;
@ -309,7 +299,7 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps)
.arg(rigCaps.guid[14], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[15], 2, 16, QLatin1Char('0'))
;
radio.rigCaps = rigCaps;
radio->rigCaps = rigCaps;
// Added so that server receives rig capabilities.
emit sendRigCaps(rigCaps);
}
@ -372,7 +362,7 @@ void servermain::setServerToPrefs()
udp = Q_NULLPTR;
}
udp = new udpServer(&serverConfig, serverTxSetup, serverRxSetup);
udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup);
serverThread = new QThread(this);
@ -385,15 +375,15 @@ void servermain::setServerToPrefs()
// Step through all radios and connect them to the server,
// server will then use GUID to determine which actual radio it belongs to.
for (RIGCONFIG& radio : serverConfig.rigs)
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio.rigThread != Q_NULLPTR)
if (radio->rigThread != Q_NULLPTR)
{
if (radio.rig != Q_NULLPTR) {
connect(radio.rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
connect(radio.rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
//connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio.rig, SLOT(dataFromServer(QByteArray)));
if (radio->rig != Q_NULLPTR) {
connect(radio->rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
connect(radio->rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
//connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio->rig, SLOT(dataFromServer(QByteArray)));
connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities)));
}
}
@ -508,21 +498,21 @@ void servermain::loadSettings()
else {
settings->setArrayIndex(i);
}
RIGCONFIG tempPrefs;
tempPrefs.civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt();
tempPrefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool();
tempPrefs.serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString();
tempPrefs.rigName = settings->value("RigName", "<NONE>").toString();
tempPrefs.baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt();
RIGCONFIG* tempPrefs = new RIGCONFIG();
tempPrefs->civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt();
tempPrefs->forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool();
tempPrefs->serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString();
tempPrefs->rigName = settings->value("RigName", "<NONE>").toString();
tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt();
if (tempPrefs.rigName=="<NONE>")
if (tempPrefs->rigName=="<NONE>")
{
foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts())
{
//qInfo(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber();
if (serialPortInfo.portName() == tempPrefs.serialPort && !serialPortInfo.serialNumber().isEmpty())
if (serialPortInfo.portName() == tempPrefs->serialPort && !serialPortInfo.serialNumber().isEmpty())
{
tempPrefs.rigName = serialPortInfo.serialNumber();
tempPrefs->rigName = serialPortInfo.serialNumber();
}
}
}
@ -531,30 +521,33 @@ void servermain::loadSettings()
guid = QUuid::createUuid().toString();
settings->setValue("GUID", guid);
}
memcpy(tempPrefs.guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs.guid));
memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), sizeof(tempPrefs->guid));
tempPrefs.rxAudioSetup.isinput = true;
tempPrefs.txAudioSetup.isinput = false;
tempPrefs.rxAudioSetup.localAFgain = 255;
tempPrefs.txAudioSetup.localAFgain = 255;
tempPrefs.rxAudioSetup.resampleQuality = 4;
tempPrefs.txAudioSetup.resampleQuality = 4;
tempPrefs.rxAudioSetup.name = settings->value("AudioInput", "").toString();
tempPrefs.txAudioSetup.name = settings->value("AudioOutput", "").toString();
tempPrefs->rxAudioSetup.isinput = true;
tempPrefs->txAudioSetup.isinput = false;
tempPrefs->rxAudioSetup.localAFgain = 255;
tempPrefs->txAudioSetup.localAFgain = 255;
tempPrefs->rxAudioSetup.resampleQuality = 4;
tempPrefs->txAudioSetup.resampleQuality = 4;
tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "").toString();
tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "").toString();
bool rxDeviceFound = false;
bool txDeviceFound = false;
// Find the actual audio devices
#if defined(RTAUDIO)
for (unsigned int i = 1; i < devices; i++) {
info = audio->getDeviceInfo(i);
if (info.outputChannels > 0) {
if (tempPrefs.txAudio.name == info->name) {
tempPrefs.txAudio.port = i;
if (tempPrefs->txAudio.name == info->name) {
tempPrefs->txAudio.port = i;
txDeviceFound = true;
}
}
if (info.inputChannels > 0) {
if (tempPrefs.rxAudio.name == info->name) {
tempPrefs.rxAudio.port = i;
if (tempPrefs->rxAudio.name == info->name) {
tempPrefs->rxAudio.port = i;
rxDeviceFound = true;
}
}
}
@ -563,14 +556,16 @@ void servermain::loadSettings()
{
info = Pa_GetDeviceInfo(i);
if (info->maxInputChannels > 0) {
if (tempPrefs.txAudio.name == info->name) {
tempPrefs.txAudio.port = i;
if (tempPrefs->txAudio.name == info->name) {
tempPrefs->txAudio.port = i;
txDeviceFound = true;
}
}
if (info->maxOutputChannels > 0) {
if (tempPrefs.rxAudio.name == info->name) {
tempPrefs.rxAudio.port = i;
}
if (tempPrefs->rxAudio.name == info->name) {
tempPrefs->rxAudio.port = i;
rxDeviceFound = true;
}
}
}
#else
@ -581,20 +576,29 @@ void servermain::loadSettings()
//qInfo(logAudio()) << "Looking for audio output devices";
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
if (deviceInfo.deviceName() == tempPrefs.txAudioSetup.name) {
tempPrefs.txAudioSetup.port = deviceInfo;
if (deviceInfo.deviceName() == tempPrefs->txAudioSetup.name) {
tempPrefs->txAudioSetup.port = deviceInfo;
txDeviceFound = true;
}
}
//qInfo(logAudio()) << "Looking for audio input devices";
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
if (deviceInfo.deviceName() == tempPrefs.rxAudioSetup.name) {
tempPrefs.rxAudioSetup.port = deviceInfo;
if (deviceInfo.deviceName() == tempPrefs->rxAudioSetup.name) {
tempPrefs->rxAudioSetup.port = deviceInfo;
rxDeviceFound = true;
}
}
#endif
tempPrefs.rig = Q_NULLPTR;
tempPrefs.rigThread = Q_NULLPTR;
if (!txDeviceFound) {
qInfo() << "Cannot find txAudioDevice" << tempPrefs->txAudioSetup.name;
}
if (!rxDeviceFound) {
qInfo() << "Cannot find rxAudioDevice" << tempPrefs->rxAudioSetup.name;
}
tempPrefs->rig = Q_NULLPTR;
tempPrefs->rigThread = Q_NULLPTR;
serverConfig.rigs.append(tempPrefs);
if (tempNum == 0) {
settings->endGroup();

Wyświetl plik

@ -1246,7 +1246,6 @@ void udpBase::dataReceived(QByteArray r)
}
else
{
//std::sort(rxSeqBuf.begin(), rxSeqBuf.end());
if (in->seq < rxSeqBuf.firstKey())
{
qInfo(logUdp()) << this->metaObject()->className() << ": ******* seq number has rolled over ****** previous highest: " << hex << rxSeqBuf.lastKey() << " current: " << hex << in->seq;
@ -1261,10 +1260,43 @@ void udpBase::dataReceived(QByteArray r)
if (!rxSeqBuf.contains(in->seq))
{
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
rxSeqBuf.insert(in->seq, QTime::currentTime());
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
if (in->seq > rxSeqBuf.lastKey() + 1) {
// We are likely missing packets then!
missingMutex.lock();
int missCounter = 0;
for (quint16 f = rxSeqBuf.lastKey() + 1; f < in->seq; f++)
{
if (missCounter > 50) {
// More than 50 packets missing, something horrific has happened!
qDebug(logUdp()) << "Too many missing packets, full reset!";
rxSeqBuf.clear();
rxMissing.clear();
missingMutex.unlock();
break;
}
qDebug(logUdp()) << "Detected missing packet" << f;
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(f, QTime::currentTime());
if (!rxMissing.contains(f))
{
rxMissing.insert(f, 0);
}
}
missingMutex.unlock();
}
else {
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
}
else {

Wyświetl plik

@ -4,8 +4,8 @@
#define STALE_CONNECTION 15
#define LOCK_PERIOD 10 // time to attempt to lock Mutex in ms
#define AUDIO_SEND_PERIOD 20 //
udpServer::udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio) :
config(*config),
udpServer::udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio) :
config(config),
outAudio(outAudio),
inAudio(inAudio)
{
@ -15,29 +15,29 @@ udpServer::udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAud
void udpServer::init()
{
for (RIGCONFIG rig : config.rigs)
for (RIGCONFIG* rig : config.rigs)
{
qDebug(logUdpServer()) << "CIV:" << rig.civAddr;
qDebug(logUdpServer()) << "Model:" << rig.modelName;
qDebug(logUdpServer()) << "Name:" << rig.rigName;
qDebug(logUdpServer()) << "CIV:" << rig.civAddr;
qDebug(logUdpServer()) << "CIV:" << rig->civAddr;
qDebug(logUdpServer()) << "Model:" << rig->modelName;
qDebug(logUdpServer()) << "Name:" << rig->rigName;
qDebug(logUdpServer()) << "CIV:" << rig->civAddr;
qDebug(logUdpServer()).noquote() << QString("GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}")
.arg(rig.guid[0], 2, 16, QLatin1Char('0'))
.arg(rig.guid[1], 2, 16, QLatin1Char('0'))
.arg(rig.guid[2], 2, 16, QLatin1Char('0'))
.arg(rig.guid[3], 2, 16, QLatin1Char('0'))
.arg(rig.guid[4], 2, 16, QLatin1Char('0'))
.arg(rig.guid[5], 2, 16, QLatin1Char('0'))
.arg(rig.guid[6], 2, 16, QLatin1Char('0'))
.arg(rig.guid[7], 2, 16, QLatin1Char('0'))
.arg(rig.guid[8], 2, 16, QLatin1Char('0'))
.arg(rig.guid[9], 2, 16, QLatin1Char('0'))
.arg(rig.guid[10], 2, 16, QLatin1Char('0'))
.arg(rig.guid[11], 2, 16, QLatin1Char('0'))
.arg(rig.guid[12], 2, 16, QLatin1Char('0'))
.arg(rig.guid[13], 2, 16, QLatin1Char('0'))
.arg(rig.guid[14], 2, 16, QLatin1Char('0'))
.arg(rig.guid[15], 2, 16, QLatin1Char('0'))
.arg(rig->guid[0], 2, 16, QLatin1Char('0'))
.arg(rig->guid[1], 2, 16, QLatin1Char('0'))
.arg(rig->guid[2], 2, 16, QLatin1Char('0'))
.arg(rig->guid[3], 2, 16, QLatin1Char('0'))
.arg(rig->guid[4], 2, 16, QLatin1Char('0'))
.arg(rig->guid[5], 2, 16, QLatin1Char('0'))
.arg(rig->guid[6], 2, 16, QLatin1Char('0'))
.arg(rig->guid[7], 2, 16, QLatin1Char('0'))
.arg(rig->guid[8], 2, 16, QLatin1Char('0'))
.arg(rig->guid[9], 2, 16, QLatin1Char('0'))
.arg(rig->guid[10], 2, 16, QLatin1Char('0'))
.arg(rig->guid[11], 2, 16, QLatin1Char('0'))
.arg(rig->guid[12], 2, 16, QLatin1Char('0'))
.arg(rig->guid[13], 2, 16, QLatin1Char('0'))
.arg(rig->guid[14], 2, 16, QLatin1Char('0'))
.arg(rig->guid[15], 2, 16, QLatin1Char('0'))
;
}
srand(time(NULL)); // Generate random key
@ -85,13 +85,6 @@ void udpServer::init()
udpAudio->bind(config.audioPort);
QUdpSocket::connect(udpAudio, &QUdpSocket::readyRead, this, &udpServer::audioReceived);
#if !defined(PORTAUDIO) && !defined(RTAUDIO)
qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.port.deviceName();
qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.port.deviceName();
#else
qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name;
qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name;
#endif
wdTimer = new QTimer();
connect(wdTimer, &QTimer::timeout, this, &udpServer::watchdog);
wdTimer->start(500);
@ -137,14 +130,14 @@ udpServer::~udpServer()
void udpServer::receiveRigCaps(rigCapabilities caps)
{
for (RIGCONFIG &rig: config.rigs) {
if (!memcmp(rig.guid, caps.guid, sizeof(rig.guid))) {
for (RIGCONFIG* rig: config.rigs) {
if (!memcmp(rig->guid, caps.guid, sizeof(rig->guid))) {
// Matching rig, fill-in missing details
rig.rigAvailable = true;
rig.modelName = caps.modelName;
rig.civAddr = caps.civ;
if (rig.rigName=="<NONE>") {
rig.rigName = caps.modelName;
rig->rigAvailable = true;
rig->modelName = caps.modelName;
rig->civAddr = caps.civ;
if (rig->rigName=="<NONE>") {
rig->rigName = caps.modelName;
}
}
}
@ -267,8 +260,8 @@ void udpServer::controlReceived()
// Request for new token
qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request";
sendCapabilities(current);
for (RIGCONFIG& radio : config.rigs) {
sendConnectionInfo(current, radio.guid);
for (RIGCONFIG* radio : config.rigs) {
sendConnectionInfo(current, radio->guid);
}
}
else if (in->res == 0x01) {
@ -280,10 +273,10 @@ void udpServer::controlReceived()
// Disconnect audio/civ
sendTokenResponse(current, in->res);
current->isStreaming = false;
for (RIGCONFIG& radio : config.rigs) {
if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)))
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)))
{
sendConnectionInfo(current, radio.guid);
sendConnectionInfo(current, radio->guid);
}
}
}
@ -350,68 +343,71 @@ void udpServer::controlReceived()
" txSampleRate" << current->txSampleRate <<
" txBufferLen" << current->txBufferLen;
if (!config.lan) {
// Radio is connected by USB/Serial and we assume that audio is connected as well.
// Create audio TX/RX threads if they don't already exist (first client chooses samplerate/codec)
audioSetup setup;
setup.resampleQuality = config.resampleQuality;
for (RIGCONFIG& radio : config.rigs) {
if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.txaudio == Q_NULLPTR)
{
radio.txAudioSetup.codec = current->txCodec;
radio.txAudioSetup.samplerate = current->txSampleRate;
radio.txAudioSetup.isinput = false;
radio.txAudioSetup.latency = current->txBufferLen;
outAudio.isinput = false;
audioSetup setup;
setup.resampleQuality = config.resampleQuality;
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->txaudio == Q_NULLPTR)
{
radio->txAudioSetup.codec = current->txCodec;
radio->txAudioSetup.samplerate = current->txSampleRate;
radio->txAudioSetup.isinput = false;
radio->txAudioSetup.latency = current->txBufferLen;
outAudio.isinput = false;
radio.txaudio = new audioHandler();
radio.txAudioThread = new QThread(this);
radio->txaudio = new audioHandler();
radio->txAudioThread = new QThread(this);
radio.txaudio->moveToThread(radio.txAudioThread);
radio->txaudio->moveToThread(radio->txAudioThread);
radio.txAudioThread->start(QThread::TimeCriticalPriority);
radio->txAudioThread->start(QThread::TimeCriticalPriority);
//connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(radio.txAudioThread, SIGNAL(finished()), radio.txaudio, SLOT(deleteLater()));
//connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(radio->txAudioThread, SIGNAL(finished()), radio->txaudio, SLOT(deleteLater()));
QMetaObject::invokeMethod(radio.txaudio, [=]() {
radio.txaudio->init(radio.txAudioSetup);
}, Qt::QueuedConnection);
QMetaObject::invokeMethod(radio->txaudio, [=]() {
radio->txaudio->init(radio->txAudioSetup);
}, Qt::QueuedConnection);
emit setupTxAudio(outAudio);
hasTxAudio = datagram.senderAddress();
emit setupTxAudio(outAudio);
hasTxAudio = datagram.senderAddress();
connect(this, SIGNAL(haveAudioData(audioPacket)), radio.txaudio, SLOT(incomingAudio(audioPacket)));
connect(this, SIGNAL(haveAudioData(audioPacket)), radio->txaudio, SLOT(incomingAudio(audioPacket)));
}
if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)) && radio.rxaudio == Q_NULLPTR)
{
radio.rxAudioSetup.codec = current->rxCodec;
radio.rxAudioSetup.samplerate = current->rxSampleRate;
radio.rxAudioSetup.latency = current->txBufferLen;
radio.rxAudioSetup.isinput = true;
}
if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)) && radio->rxaudio == Q_NULLPTR)
{
#if !defined(PORTAUDIO) && !defined(RTAUDIO)
qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio input(RX) :" << radio->rxAudioSetup.port.deviceName();
qInfo(logUdpServer()) << "Radio" << radio->rigName << "audio output(TX) :" << radio->txAudioSetup.port.deviceName();
#else
qInfo(logUdpServer()) << "Server audio input (RX):" << inAudio.name;
qInfo(logUdpServer()) << "Server audio output (TX):" << outAudio.name;
#endif
radio.rxaudio = new audioHandler();
radio->rxAudioSetup.codec = current->rxCodec;
radio->rxAudioSetup.samplerate = current->rxSampleRate;
radio->rxAudioSetup.latency = current->txBufferLen;
radio->rxAudioSetup.isinput = true;
radio.rxAudioThread = new QThread(this);
radio->rxaudio = new audioHandler();
radio.rxaudio->moveToThread(radio.rxAudioThread);
radio->rxAudioThread = new QThread(this);
radio.rxAudioThread->start(QThread::TimeCriticalPriority);
radio->rxaudio->moveToThread(radio->rxAudioThread);
//connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup)));
connect(radio.rxAudioThread, SIGNAL(finished()), radio.rxaudio, SLOT(deleteLater()));
radio->rxAudioThread->start(QThread::TimeCriticalPriority);
QMetaObject::invokeMethod(radio.rxaudio, [=]() {
radio.rxaudio->init(radio.rxAudioSetup);
}, Qt::QueuedConnection);
connect(radio->rxAudioThread, SIGNAL(finished()), radio->rxaudio, SLOT(deleteLater()));
radio.rxAudioTimer = new QTimer();
radio.rxAudioTimer->setTimerType(Qt::PreciseTimer);
connect(radio.rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this));
radio.rxAudioTimer->start(TXAUDIO_PERIOD);
}
QMetaObject::invokeMethod(radio->rxaudio, [=]() {
radio->rxaudio->init(radio->rxAudioSetup);
}, Qt::QueuedConnection);
radio->rxAudioTimer = new QTimer();
radio->rxAudioTimer->setTimerType(Qt::PreciseTimer);
connect(radio->rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this));
radio->rxAudioTimer->start(TXAUDIO_PERIOD);
}
}
@ -568,12 +564,12 @@ void udpServer::civReceived()
qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected invalid remote CI-V:" << hex << (quint8)r[lastFE+2];
}
for (RIGCONFIG& radio : config.rigs) {
if (!memcmp(radio.guid, current->guid, sizeof(radio.guid)))
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(radio->guid, current->guid, sizeof(radio->guid)))
{
// Only send to the rig that it belongs to!
QMetaObject::invokeMethod(radio.rig, [=]() {
radio.rig->dataFromServer(r.mid(0x15));;
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->dataFromServer(r.mid(0x15));;
}, Qt::DirectConnection);
}
}
@ -886,14 +882,53 @@ void udpServer::commonReceived(QList<CLIENT*>* l, CLIENT* current, QByteArray r)
if (!current->rxSeqBuf.contains(in->seq))
{
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
if (current->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
if (current->rxSeqBuf.size() > BUFSIZE)
{
current->rxSeqBuf.remove(current->rxSeqBuf.firstKey());
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
int missCounter = 0;
if (in->seq > current->rxSeqBuf.lastKey() + 1) {
// We are likely missing packets then!
if (current->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
for (quint16 f = current->rxSeqBuf.lastKey() + 1; f < in->seq; f++)
{
if (missCounter > 50) {
// More than 50 packets missing, something horrific has happened!
qDebug(logUdpServer()) << "Too many missing packets, full reset!";
current->rxSeqBuf.clear();
current->rxMissing.clear();
current->missMutex.unlock();
break;
}
qInfo(logUdpServer()) << "Detected missing packet" << f;
if (current->rxSeqBuf.size() > BUFSIZE)
{
current->rxSeqBuf.remove(current->rxSeqBuf.firstKey());
}
current->rxSeqBuf.insert(f, QTime::currentTime());
if (!current->rxMissing.contains(f))
{
current->rxMissing.insert(f, 0);
}
}
current->missMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock missMutex()";
}
}
else {
if (current->rxSeqBuf.size() > BUFSIZE)
{
current->rxSeqBuf.remove(current->rxSeqBuf.firstKey());
}
current->rxSeqBuf.insert(in->seq, QTime::currentTime());
}
current->rxSeqBuf.insert(in->seq, QTime::currentTime());
current->rxMutex.unlock();
}
else {
@ -944,6 +979,11 @@ void udpServer::sendControl(CLIENT* c, quint8 type, quint16 seq)
s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
if (c->txSeqBuf.size() > BUFSIZE)
{
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
}
c->txSeqBuf.insert(seq, s);
c->txSeq++;
c->txMutex.unlock();
@ -1069,6 +1109,11 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed)
s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
if (c->txSeqBuf.size() > BUFSIZE)
{
c->txSeqBuf.remove(c->txSeqBuf.firstKey());
}
c->txSeqBuf.insert(c->txSeq, s);
c->txSeq++;
c->txMutex.unlock();
@ -1111,24 +1156,24 @@ void udpServer::sendCapabilities(CLIENT* c)
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
for (RIGCONFIG &rig : config.rigs) {
qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig.modelName;
for (RIGCONFIG* rig : config.rigs) {
qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq << "for" << rig->modelName;
radio_cap_packet r;
memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00!
memcpy(r.guid, rig.guid, sizeof(r.guid));
memcpy(r.name, rig.rigName.toLocal8Bit(), sizeof(r.name));
memcpy(r.guid, rig->guid, sizeof(r.guid));
memcpy(r.name, rig->rigName.toLocal8Bit(), sizeof(r.name));
memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11);
if (rig.hasWiFi && !rig.hasEthernet) {
r.conntype = 0x0707; // 0x0707 for wifi rig.
if (rig->hasWiFi && !rig->hasEthernet) {
r.conntype = 0x0707; // 0x0707 for wifi rig->
}
else {
r.conntype = 0x073f; // 0x073f for ethernet rig.
r.conntype = 0x073f; // 0x073f for ethernet rig->
}
r.civ = rig.civAddr;
r.baudrate = (quint32)qToBigEndian(rig.baudRate);
r.civ = rig->civAddr;
r.baudrate = (quint32)qToBigEndian(rig->baudRate);
/*
0x80 = 12K only
0x40 = 44.1K only
@ -1139,7 +1184,7 @@ void udpServer::sendCapabilities(CLIENT* c)
0x02 = 16K only
0x01 = 8K only
*/
if (rig.rxaudio == Q_NULLPTR) {
if (rig->rxaudio == Q_NULLPTR) {
r.rxsample = 0x8b01; // all rx sample frequencies supported
}
else {
@ -1160,7 +1205,7 @@ void udpServer::sendCapabilities(CLIENT* c)
}
}
if (rig.txaudio == Q_NULLPTR) {
if (rig->txaudio == Q_NULLPTR) {
r.txsample = 0x8b01; // all tx sample frequencies supported
r.enablea = 0x01; // 0x01 enables TX 24K mode?
qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Client will have TX audio";
@ -1217,8 +1262,8 @@ void udpServer::sendCapabilities(CLIENT* c)
// Also used to display currently connected used information.
void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16])
{
for (RIGCONFIG& radio : config.rigs) {
if (!memcmp(guid, radio.guid, sizeof(guid)))
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(guid, radio->guid, sizeof(guid)))
{
qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq;
conninfo_packet p;
@ -1232,10 +1277,10 @@ void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16])
p.tokrequest = c->tokenRx;
p.token = c->tokenTx;
p.code = 0x0380;
memcpy(p.guid, radio.guid, sizeof(p.guid));
memcpy(p.name, radio.rigName.toLocal8Bit(), sizeof(p.name));
memcpy(p.guid, radio->guid, sizeof(p.guid));
memcpy(p.name, radio->rigName.toLocal8Bit(), sizeof(p.name));
if (radio.rigAvailable) {
if (radio->rigAvailable) {
if (c->isStreaming) {
p.busy = 0x01;
}
@ -1465,7 +1510,6 @@ void udpServer::sendStatus(CLIENT* c)
void udpServer::dataForServer(QByteArray d)
{
rigCommander* sender = qobject_cast<rigCommander*>(QObject::sender());
if (sender == Q_NULLPTR)
@ -1479,67 +1523,63 @@ void udpServer::dataForServer(QByteArray d)
{
continue;
}
for (RIGCONFIG& radio : config.rigs)
// Use the GUID to determine which radio the response is from
if (memcmp(sender->getGUID(), client->guid, sizeof(client->guid)))
{
if (memcmp(radio.guid, client->guid, sizeof(radio.guid)))
continue; // Rig guid doesn't match the one requested by the client.
}
int lastFE = d.lastIndexOf((quint8)0xfe);
//qInfo(logUdpServer()) << "Server got CIV data from" << radio->rigName << "length" << d.length();
if (client->connected && d.length() > lastFE + 2 &&
((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId ||
(quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1))
{
data_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = (quint16)d.length() + sizeof(p);
p.seq = client->txSeq;
p.sentid = client->myId;
p.rcvdid = client->remoteId;
p.reply = (char)0xc1;
p.datalen = (quint16)d.length();
p.sendseq = client->innerSeq;
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d);
SEQBUFENTRY s;
s.seqNum = p.seq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = t;
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
continue;
}
int lastFE = d.lastIndexOf((quint8)0xfe);
// Use the GUID to determine which radio the response is from
qInfo(logUdpServer()) << "Server got CIV data from" << radio.rigName << "length" << d.length();
if (client->connected && d.length() > lastFE + 2 &&
((quint8)d[lastFE + 1] == client->civId || (quint8)d[lastFE + 2] == client->civId ||
(quint8)d[lastFE + 1] == 0x00 || (quint8)d[lastFE + 2] == 0x00 || (quint8)d[lastFE + 1] == 0xE1 || (quint8)d[lastFE + 2] == 0xE1))
{
data_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = (quint16)d.length() + sizeof(p);
p.seq = client->txSeq;
p.sentid = client->myId;
p.rcvdid = client->remoteId;
p.reply = (char)0xc1;
p.datalen = (quint16)d.length();
p.sendseq = client->innerSeq;
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d);
SEQBUFENTRY s;
s.seqNum = p.seq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = t;
if (client->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
if (client->txSeqBuf.size() > BUFSIZE)
{
if (client->txSeqBuf.size() > BUFSIZE)
{
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
}
client->txSeqBuf.insert(p.seq, s);
client->txSeq++;
client->innerSeq++;
client->txMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock txMutex()";
}
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
client->socket->writeDatagram(t, client->ipAddress, client->port);
udpMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
client->txSeqBuf.remove(client->txSeqBuf.firstKey());
}
client->txSeqBuf.insert(p.seq, s);
client->txSeq++;
client->innerSeq++;
client->txMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2];
qInfo(logUdpServer()) << "Unable to lock txMutex()";
}
if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
client->socket->writeDatagram(t, client->ipAddress, client->port);
udpMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock udpMutex()";
}
}
else {
qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2];
}
}
return;
@ -1548,13 +1588,13 @@ void udpServer::dataForServer(QByteArray d)
void udpServer::sendRxAudio()
{
QByteArray audio;
for (RIGCONFIG &rig : config.rigs) {
for (RIGCONFIG* rig : config.rigs) {
if (rig.rxaudio != Q_NULLPTR) {
if (rig->rxaudio != Q_NULLPTR) {
if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
audio.clear();
rig.rxaudio->getNextAudioChunk(audio);
rig->rxaudio->getNextAudioChunk(audio);
int len = 0;
while (len < audio.length()) {
audioPacket partial;
@ -1641,122 +1681,26 @@ void udpServer::sendRetransmitRequest(CLIENT* c)
QByteArray missingSeqs;
QTime missingTime = QTime::currentTime();
if (!c->rxSeqBuf.empty() && c->rxSeqBuf.size() <= c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey())
{
if ((c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size()) > 20)
{
// Too many packets to process, flush buffers and start again!
qDebug(logUdp()) << "Too many missing packets, flushing buffer: " << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1;
if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
c->rxMissing.clear();
c->missMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock missMutex()";
}
if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
c->rxSeqBuf.clear();
c->rxMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock rxMutex()";
}
}
else {
// We have at least 1 missing packet!
qDebug(logUdp()) << "Missing Seq: size=" << c->rxSeqBuf.size() << "firstKey=" << c->rxSeqBuf.firstKey() << "lastKey=" << c->rxSeqBuf.lastKey() << "missing=" << c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() + 1;
// We are missing packets so iterate through the buffer and add the missing ones to missing packet list
if (c->rxMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
int missCounter = 0;
auto i = std::adjacent_find(c->rxSeqBuf.keys().begin(), c->rxSeqBuf.keys().end(), [](int l, int r) {return l + 1 < r; });
while (i != c->rxSeqBuf.keys().end())
{
quint16 j = 1 + *i;
++i;
if (i == c->rxSeqBuf.keys().end())
{
continue;
}
if (c->rxSeqBuf.lastKey() - c->rxSeqBuf.firstKey() - c->rxSeqBuf.size() == 0 && c->type == "AUDIO" &&
(c->txCodec == 0x40 || c->txCodec == 0x80))
{
// Single missing audio packet ignore it!
qDebug(logUdpServer()) << "Single missing audio packet will be handled by FEC (" << hex << j << ")";
c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer so it doesn't try to retransmit
c->missMutex.unlock();
c->rxMutex.unlock();
return;
}
missCounter++;
if (missCounter > 20) {
// More than 20 packets missing, something horrific has happened!
qDebug(logUdpServer()) << ": Too many missing packets, clearing buffer";
c->rxSeqBuf.clear();
c->rxMissing.clear();
c->missMutex.unlock();
c->rxMutex.unlock();
return;
}
auto s = c->rxMissing.find(j);
if (s == c->rxMissing.end())
{
// We haven't seen this missing packet before
qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << c->rxMissing.size() << "): " << j << dec << missingTime.msecsTo(QTime::currentTime()) << "ms";
c->rxMissing.insert(j, 0);
if (c->rxSeqBuf.size() > BUFSIZE)
{
c->rxSeqBuf.remove(c->rxSeqBuf.firstKey());
}
c->rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it.
}
else {
if (s.value() == 4)
{
// We have tried 4 times to request this packet, time to give up!
s = c->rxMissing.erase(s);
}
}
}
}
else {
qInfo(logUdpServer()) << "Unable to lock missMutex()";
}
c->rxMutex.unlock();
}
else {
qInfo(logUdpServer()) << "Unable to lock rxMutex()";
}
c->missMutex.unlock();
}
}
if (missingTime.msecsTo(QTime::currentTime()) > 10) {
qInfo(logUdpServer()) << "Initial missing processing has been running for" << missingTime.msecsTo(QTime::currentTime()) << "(ms)";
}
if (c->missMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD)))
{
for (auto it = c->rxMissing.begin(); it != c->rxMissing.end(); ++it)
{
if (it.value() < 10)
if (it.value() < 4)
{
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
it.value()++;
}
else {
// We have tried 4 times to request this packet, time to give up!
qDebug(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << it.key() << "deleting";
it = c->rxMissing.erase(it);
}
}
if (missingSeqs.length() != 0)
@ -1889,27 +1833,27 @@ void udpServer::deleteConnection(QList<CLIENT*>* l, CLIENT* c)
}
if (len == 0) {
for (RIGCONFIG& radio : config.rigs) {
if (!memcmp(radio.guid, guid, sizeof(radio.guid)))
for (RIGCONFIG* radio : config.rigs) {
if (!memcmp(radio->guid, guid, sizeof(radio->guid)))
{
if (radio.rxAudioTimer != Q_NULLPTR) {
radio.rxAudioTimer->stop();
delete radio.rxAudioTimer;
radio.rxAudioTimer = Q_NULLPTR;
if (radio->rxAudioTimer != Q_NULLPTR) {
radio->rxAudioTimer->stop();
delete radio->rxAudioTimer;
radio->rxAudioTimer = Q_NULLPTR;
}
if (radio.rxAudioThread != Q_NULLPTR) {
radio.rxAudioThread->quit();
radio.rxAudioThread->wait();
radio.rxaudio = Q_NULLPTR;
radio.rxAudioThread = Q_NULLPTR;
if (radio->rxAudioThread != Q_NULLPTR) {
radio->rxAudioThread->quit();
radio->rxAudioThread->wait();
radio->rxaudio = Q_NULLPTR;
radio->rxAudioThread = Q_NULLPTR;
}
if (radio.txAudioThread != Q_NULLPTR) {
radio.txAudioThread->quit();
radio.txAudioThread->wait();
radio.txaudio = Q_NULLPTR;
radio.txAudioThread = Q_NULLPTR;
if (radio->txAudioThread != Q_NULLPTR) {
radio->txAudioThread->quit();
radio->txAudioThread->wait();
radio->txaudio = Q_NULLPTR;
radio->txAudioThread = Q_NULLPTR;
}
}
}

Wyświetl plik

@ -79,7 +79,7 @@ struct SERVERCONFIG {
quint8 resampleQuality;
quint32 baudRate;
QList <SERVERUSER> users;
QList <RIGCONFIG> rigs;
QList <RIGCONFIG*> rigs;
};
@ -88,7 +88,7 @@ class udpServer : public QObject
Q_OBJECT
public:
udpServer(SERVERCONFIG* config, audioSetup outAudio, audioSetup inAudio);
udpServer(SERVERCONFIG& config, audioSetup outAudio, audioSetup inAudio);
~udpServer();
public slots: