diff --git a/main.cpp b/main.cpp index 89f6792..c232ca8 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,11 @@ #include #endif +#ifdef Q_OS_WIN +#include +#include +#endif + #include #include "wfmain.h" #include "logcategories.h" @@ -15,6 +20,29 @@ QScopedPointer m_logFile; QMutex logMutex; bool debugMode=false; +#ifdef BUILD_WFSERVER + servermain* w=Q_NULLPTR; + + #ifdef Q_OS_WIN + bool __stdcall cleanup(DWORD sig) + #else + static void cleanup(int sig) + #endif + { + Q_UNUSED(sig) + qDebug() << "Exiting via SIGNAL"; + if (w!=Q_NULLPTR) w->deleteLater(); + qApp->quit(); + + #ifdef Q_OS_WIN + return true; + #else + return; + #endif + } + +#endif + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); int main(int argc, char *argv[]) @@ -140,9 +168,12 @@ int main(int argc, char *argv[]) qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); #ifdef BUILD_WFSERVER - servermain *w = new servermain(serialPortCL, hostCL, settingsFile); - Q_UNUSED(w); // Prevent warning! - +#ifdef Q_OS_WIN + SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); +#else + signal(SIGINT, cleanup); +#endif + w = new servermain(serialPortCL, hostCL, settingsFile); #else a.setWheelScrollLines(1); // one line per wheel click wfmain w(serialPortCL, hostCL, settingsFile); diff --git a/rigcommander.cpp b/rigcommander.cpp index 6e461bb..4094da5 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -22,11 +22,20 @@ rigCommander::rigCommander() { - state.set(SCOPEFUNC,true,false); + qInfo(logRig()) << "creating instance of rigCommander()"; + state.set(SCOPEFUNC, true, false); +} + +rigCommander::rigCommander(quint8 guid[16]) +{ + qInfo(logRig()) << "creating instance of rigCommander()"; + state.set(SCOPEFUNC, true, false); + memcpy(this->guid, guid, sizeof(this->guid)); } rigCommander::~rigCommander() { + qInfo(logRig()) << "closing instance of rigCommander()"; closeComm(); } @@ -40,13 +49,14 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu // civAddr = 0x94; // address of the radio. Decimal is 148. civAddr = rigCivAddr; // address of the radio. Decimal is 148. usingNativeLAN = false; - + //qInfo(logRig()) << "Opening connection to Rig:" << hex << (unsigned char)rigCivAddr << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate; // --- setup(); // --- this->rigSerialPort = rigSerialPort; this->rigBaudRate = rigBaudRate; + rigCaps.baudRate = rigBaudRate; comm = new commHandler(rigSerialPort, rigBaudRate); ptty = new pttyHandler(vsp); @@ -217,6 +227,7 @@ bool rigCommander::usingLAN() } void rigCommander::receiveBaudRate(quint32 baudrate) { + rigCaps.baudRate = baudrate; emit haveBaudRate(baudrate); } @@ -3612,6 +3623,9 @@ void rigCommander::determineRigCaps() } haveRigCaps = true; + // Copy received guid so we can recognise this radio. + memcpy(rigCaps.guid, this->guid, sizeof(rigCaps.guid)); + if(!usingNativeLAN) { if(useRTSforPTT_isSet) @@ -4676,6 +4690,9 @@ void rigCommander::dataFromServer(QByteArray data) emit dataForComm(data); } +quint8* rigCommander::getGUID() { + return guid; +} diff --git a/rigcommander.h b/rigcommander.h index d930dac..e343555 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -69,13 +69,16 @@ class rigCommander : public QObject public: rigCommander(); + rigCommander(quint8 guid[16]); ~rigCommander(); bool usingLAN(); + quint8* getGUID(); + public slots: void process(); - void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp); + void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp); void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp); void closeComm(); void stateUpdated(); @@ -475,7 +478,7 @@ private: QString serialPortError; unsigned char localVolume=0; - + quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; }; diff --git a/rigidentities.h b/rigidentities.h index cae1b75..222a73c 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -138,6 +138,8 @@ struct rigCapabilities { std::vector modes; QByteArray transceiveCommand; + quint8 guid[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + quint32 baudRate; }; diff --git a/servermain.cpp b/servermain.cpp index e0be201..2ab49e8 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -13,16 +13,16 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q this->serialPortCL = serialPortCL; this->hostCL = hostCL; - qRegisterMetaType(); // Needs to be registered early. - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType (); // Needs to be registered early. + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); + qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType (); qRegisterMetaType (); @@ -30,10 +30,6 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q qRegisterMetaType(); qRegisterMetaType>(); - signal(SIGINT, handleCtrlC); - - haveRigCaps = false; - setDefPrefs(); getSettingsFilePath(settingsFile); @@ -44,8 +40,6 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q openRig(); - rigConnections(); - setServerToPrefs(); amTransmitting = false; @@ -54,15 +48,19 @@ servermain::servermain(const QString serialPortCL, const QString hostCL, const Q servermain::~servermain() { - rigThread->quit(); - rigThread->wait(); + for (RIGCONFIG& radio : serverConfig.rigs) + { + if (radio.rigThread != Q_NULLPTR) + { + radio.rigThread->quit(); + radio.rigThread->wait(); + } + } if (serverThread != Q_NULLPTR) { serverThread->quit(); serverThread->wait(); } - if (rigCtl != Q_NULLPTR) { - delete rigCtl; - } + delete settings; #if defined(PORTAUDIO) @@ -78,205 +76,104 @@ void servermain::openRig() // and this function is also responsible for initiating the search for a rig model and capabilities. // Any errors, such as unable to open connection or unable to open port, are to be reported to the user. - //TODO: if(hasRunPreviously) - - //TODO: if(useNetwork){... - - // } else { - - // if (prefs.fileWasNotFound) { - // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc - // TODO: How do we know if the setting was loaded? - - - // TODO: Use these if they are found - if(!serialPortCL.isEmpty()) - { - qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; - } else { - qDebug(logSystem()) << "Serial port not specified. "; - } - - if(!hostCL.isEmpty()) - { - qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; - } - makeRig(); - if (prefs.enableLAN) + if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) { - usingLAN = true; - // We need to setup the tx/rx audio: - emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort); + findSerialPort(); + } else { - if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) + if(serialPortCL.isEmpty()) { - findSerialPort(); - + serialPortRig = prefs.serialPortRadio; } else { - if(serialPortCL.isEmpty()) - { - serialPortRig = prefs.serialPortRadio; - } else { - serialPortRig = serialPortCL; - } + serialPortRig = serialPortCL; + } + } + for (RIGCONFIG& radio : serverConfig.rigs) + { + //qInfo(logSystem()) << "Opening rig"; + if (radio.rigThread != Q_NULLPTR) + { + //qInfo(logSystem()) << "Got rig"; + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->commSetup(radio.civAddr, radio.serialPort, radio.baudRate, QString("none")); + }, Qt::QueuedConnection); } - usingLAN = false; - emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort); } - - - } -void servermain::rigConnections() -{ - connect(this, SIGNAL(setCIVAddr(unsigned char)), rig, SLOT(setCIVAddr(unsigned char))); - - connect(this, SIGNAL(sendPowerOn()), rig, SLOT(powerOn())); - connect(this, SIGNAL(sendPowerOff()), rig, SLOT(powerOff())); - - connect(rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool))); - connect(this, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool))); - connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT())); - connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug())); - - - connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); - connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); - connect(this, SIGNAL(getTSQL()), rig, SLOT(getTSQL())); - connect(this, SIGNAL(getRptAccessMode()), rig, SLOT(getRptAccessMode())); - - connect(this, SIGNAL(setScopeMode(spectrumMode)), rig, SLOT(setSpectrumMode(spectrumMode))); - connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); - - connect(this, SIGNAL(setFrequency(unsigned char, freqt)), rig, SLOT(setFrequency(unsigned char, freqt))); - connect(this, SIGNAL(setScopeEdge(char)), rig, SLOT(setScopeEdge(char))); - connect(this, SIGNAL(setScopeSpan(char)), rig, SLOT(setScopeSpan(char))); - //connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode())); - connect(this, SIGNAL(getScopeEdge()), rig, SLOT(getScopeEdge())); - connect(this, SIGNAL(getScopeSpan()), rig, SLOT(getScopeSpan())); - connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char))); - - connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char))); - connect(this, SIGNAL(setMode(mode_info)), rig, SLOT(setMode(mode_info))); - - // Levels (read and write) - // Levels: Query: - connect(this, SIGNAL(getLevels()), rig, SLOT(getLevels())); - connect(this, SIGNAL(getRfGain()), rig, SLOT(getRfGain())); - connect(this, SIGNAL(getAfGain()), rig, SLOT(getAfGain())); - connect(this, SIGNAL(getSql()), rig, SLOT(getSql())); - connect(this, SIGNAL(getIfShift()), rig, SLOT(getIFShift())); - connect(this, SIGNAL(getTPBFInner()), rig, SLOT(getTPBFInner())); - connect(this, SIGNAL(getTPBFOuter()), rig, SLOT(getTPBFOuter())); - connect(this, SIGNAL(getTxPower()), rig, SLOT(getTxLevel())); - connect(this, SIGNAL(getMicGain()), rig, SLOT(getMicGain())); - connect(this, SIGNAL(getSpectrumRefLevel()), rig, SLOT(getSpectrumRefLevel())); - connect(this, SIGNAL(getModInputLevel(rigInput)), rig, SLOT(getModInputLevel(rigInput))); - - - // Levels: Set: - connect(this, SIGNAL(setRfGain(unsigned char)), rig, SLOT(setRfGain(unsigned char))); - connect(this, SIGNAL(setAfGain(unsigned char)), rig, SLOT(setAfGain(unsigned char))); - connect(this, SIGNAL(setSql(unsigned char)), rig, SLOT(setSquelch(unsigned char))); - connect(this, SIGNAL(setIFShift(unsigned char)), rig, SLOT(setIFShift(unsigned char))); - connect(this, SIGNAL(setTPBFInner(unsigned char)), rig, SLOT(setTPBFInner(unsigned char))); - connect(this, SIGNAL(setTPBFOuter(unsigned char)), rig, SLOT(setTPBFOuter(unsigned char))); - connect(this, SIGNAL(setTxPower(unsigned char)), rig, SLOT(setTxPower(unsigned char))); - connect(this, SIGNAL(setMicGain(unsigned char)), rig, SLOT(setMicGain(unsigned char))); - connect(this, SIGNAL(setMonitorLevel(unsigned char)), rig, SLOT(setMonitorLevel(unsigned char))); - connect(this, SIGNAL(setVoxGain(unsigned char)), rig, SLOT(setVoxGain(unsigned char))); - connect(this, SIGNAL(setAntiVoxGain(unsigned char)), rig, SLOT(setAntiVoxGain(unsigned char))); - connect(this, SIGNAL(setSpectrumRefLevel(int)), rig, SLOT(setSpectrumRefLevel(int))); - connect(this, SIGNAL(setModLevel(rigInput, unsigned char)), rig, SLOT(setModInputLevel(rigInput, unsigned char))); - - // Rig and ATU info: - connect(this, SIGNAL(startATU()), rig, SLOT(startATU())); - connect(this, SIGNAL(setATU(bool)), rig, SLOT(setATU(bool))); - connect(this, SIGNAL(getATUStatus()), rig, SLOT(getATUStatus())); - connect(this, SIGNAL(getRigID()), rig, SLOT(getRigID())); - connect(rig, SIGNAL(haveRigID(rigCapabilities)), this, SLOT(receiveRigID(rigCapabilities))); - connect(this, SIGNAL(setAttenuator(unsigned char)), rig, SLOT(setAttenuator(unsigned char))); - connect(this, SIGNAL(setPreamp(unsigned char)), rig, SLOT(setPreamp(unsigned char))); - connect(this, SIGNAL(setAntenna(unsigned char, bool)), rig, SLOT(setAntenna(unsigned char, bool))); - connect(this, SIGNAL(getPreamp()), rig, SLOT(getPreamp())); - connect(this, SIGNAL(getAttenuator()), rig, SLOT(getAttenuator())); - connect(this, SIGNAL(getAntenna()), rig, SLOT(getAntenna())); - - - // Speech (emitted from rig speaker) - connect(this, SIGNAL(sayAll()), rig, SLOT(sayAll())); - connect(this, SIGNAL(sayFrequency()), rig, SLOT(sayFrequency())); - connect(this, SIGNAL(sayMode()), rig, SLOT(sayMode())); - - // Date and Time: - connect(this, SIGNAL(setTime(timekind)), rig, SLOT(setTime(timekind))); - connect(this, SIGNAL(setDate(datekind)), rig, SLOT(setDate(datekind))); - connect(this, SIGNAL(setUTCOffset(timekind)), rig, SLOT(setUTCOffset(timekind))); - -} - - void servermain::makeRig() { - if (rigThread == Q_NULLPTR) + for (RIGCONFIG& radio : serverConfig.rigs) { - rig = new rigCommander(); - rigThread = new QThread(this); + if (radio.rigThread == Q_NULLPTR) + { + qInfo(logSystem()) << "Creating new rigThread()"; + radio.rig = new rigCommander(radio.guid); + radio.rigThread = new QThread(this); - // Thread: - rig->moveToThread(rigThread); - connect(rigThread, SIGNAL(started()), rig, SLOT(process())); - connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater())); - rigThread->start(); + // 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(); + // 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))); - // Rig status and Errors: - connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); - connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString))); + // 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))); - // Rig comm setup: - connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString)), rig, SLOT(commSetup(unsigned char, udpPreferences, audioSetup, audioSetup, QString))); - connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32,QString)), rig, SLOT(commSetup(unsigned char, QString, quint32,QString))); - connect(this, SIGNAL(setRTSforPTT(bool)), rig, SLOT(setRTSforPTT(bool))); + connect(radio.rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32))); - connect(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()), rig, SLOT(closeComm())); - connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs())); - connect(this, SIGNAL(setRigID(unsigned char)), rig, SLOT(setRigID(unsigned char))); - connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities))); - connect(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*))); + + //Other connections + 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()) { + qInfo(logSystem()) << "Rig thread is running"; + } + else { + qInfo(logSystem()) << "Rig thread is not running"; + } - connect(this, SIGNAL(requestRigState()), rig, SLOT(sendState())); - connect(this, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); - connect(rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*))); - if (rigCtl != Q_NULLPTR) { - connect(rig, SIGNAL(stateInfo(rigstate*)), rigCtl, SLOT(receiveStateInfo(rigstate*))); - connect(rigCtl, SIGNAL(stateUpdated()), rig, SLOT(stateUpdated())); } + } + } void servermain::removeRig() { - if (rigThread != Q_NULLPTR) + for (RIGCONFIG& radio : serverConfig.rigs) { - if (rigCtl != Q_NULLPTR) { - rigCtl->disconnect(); + if (radio.rigThread != Q_NULLPTR) + { + radio.rigThread->disconnect(); + radio.rig->disconnect(); + delete radio.rigThread; + delete radio.rig; + radio.rig = Q_NULLPTR; } - rigThread->disconnect(); - - rig->disconnect(); - - delete rigThread; - delete rig; - rig = Q_NULLPTR; } - } @@ -330,43 +227,45 @@ void servermain::findSerialPort() } } -void servermain::receiveStatusUpdate(QString text) +void servermain::receiveStatusUpdate(networkStatus status) { - if (text != lastMessage) { - std::cout << text.toLocal8Bit().toStdString() << "\n"; - lastMessage = text; + if (status.message != lastMessage) { + std::cout << status.message.toLocal8Bit().toStdString() << "\n"; + lastMessage = status.message; } } void servermain::receiveCommReady() { - qInfo(logSystem()) << "Received CommReady!! "; - if(!usingLAN) + rigCommander* sender = qobject_cast(QObject::sender()); + + // Use the GUID to determine which radio the response is from + + for (RIGCONFIG& radio : serverConfig.rigs) { - // usingLAN gets set when we emit the sendCommSetup signal. - // If we're not using the LAN, then we're on serial, and - // we already know the baud rate and can calculate the timing parameters. - calculateTimingParameters(); - } - if(prefs.radioCIVAddr == 0) - { - // tell rigCommander to broadcast a request for all rig IDs. - // qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)"; - emit getRigCIV(); - issueDelayedCommand(cmdGetRigCIV); - delayedCommand->start(); - } 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 " << prefs.radioCIVAddr; - if(prefs.CIVisRadioModel) + if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) { - qInfo(logSystem()) << "Skipping Rig ID query, using user-supplied model from CI-V address: " << prefs.radioCIVAddr; - emit setRigID(prefs.radioCIVAddr); - } else { - emit getRigID(); - getInitialRigState(); + + qInfo(logSystem()) << "Received CommReady!! "; + 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(); + }, 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); + }, Qt::QueuedConnection); + } } } } @@ -376,18 +275,45 @@ void servermain::receiveFoundRigID(rigCapabilities rigCaps) { // Entry point for unknown rig being identified at the start of the program. //now we know what the rig ID is: - //qInfo(logSystem()) << "In wfview, we now have a reply to our request for rig identity sent to CIV BROADCAST."; - if(rig->usingLAN()) + rigCommander* sender = qobject_cast(QObject::sender()); + + // Use the GUID to determine which radio the response is from + for (RIGCONFIG& radio : serverConfig.rigs) { - usingLAN = true; - } else { - usingLAN = false; + + if (sender != Q_NULLPTR && radio.rig != Q_NULLPTR && !radio.rigAvailable && !memcmp(sender->getGUID(), radio.guid, sizeof(radio.guid))) + { + + qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; + qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan; + qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax; + qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax; + qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum; + qDebug(logSystem()).noquote() << QString("Rig ID received into servermain: GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}") + .arg(rigCaps.guid[0], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[1], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[2], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[3], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[4], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[5], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[6], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[7], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[8], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[9], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[10], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[11], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[12], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[13], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[14], 2, 16, QLatin1Char('0')) + .arg(rigCaps.guid[15], 2, 16, QLatin1Char('0')) + ; + radio.rigCaps = rigCaps; + // Added so that server receives rig capabilities. + emit sendRigCaps(rigCaps); + } } - - receiveRigID(rigCaps); - getInitialRigState(); - return; } @@ -429,25 +355,10 @@ void servermain::getSettingsFilePath(QString settingsFile) void servermain::setInitialTiming() { loopTickCounter = 0; - delayedCmdIntervalLAN_ms = 70; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdIntervalSerial_ms = 100; // interval for regular delayed commands, including initial rig/UI state queries - delayedCmdStartupInterval_ms = 250; // interval for rigID polling - delayedCommand = new QTimer(this); - delayedCommand->setInterval(delayedCmdStartupInterval_ms); // 250ms until we find rig civ and id, then 100ms. - delayedCommand->setSingleShot(false); - connect(delayedCommand, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - - // TODO: Remove this: -// periodicPollingTimer = new QTimer(this); -// periodicPollingTimer->setInterval(10); -// periodicPollingTimer->setSingleShot(false); - //connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - pttTimer = new QTimer(this); pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms pttTimer->setSingleShot(true); connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit())); - } void servermain::setServerToPrefs() @@ -461,63 +372,49 @@ void servermain::setServerToPrefs() udp = Q_NULLPTR; } - if (serverConfig.enabled) { - serverConfig.lan = prefs.enableLAN; - qInfo(logAudio()) << "Audio Input device " << serverRxSetup.name; - qInfo(logAudio()) << "Audio Output device " << serverTxSetup.name; - udp = new udpServer(serverConfig, serverTxSetup, serverRxSetup); + udp = new udpServer(&serverConfig, serverTxSetup, serverRxSetup); - serverThread = new QThread(this); + serverThread = new QThread(this); - udp->moveToThread(serverThread); + udp->moveToThread(serverThread); - connect(this, SIGNAL(initServer()), udp, SLOT(init())); - connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); + connect(this, SIGNAL(initServer()), udp, SLOT(init())); + connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater())); - if (rig != Q_NULLPTR) { - connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket))); - connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray))); - connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray))); + // 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) + { + 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))); + connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); + } } - - if (!prefs.enableLAN) { - connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); - } - - serverThread->start(); - - emit initServer(); - - connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities))); - } + + connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(receiveStatusUpdate(QString))); + + serverThread->start(); + + emit initServer(); } void servermain::setDefPrefs() { - defPrefs.useFullScreen = false; - defPrefs.useDarkMode = true; - defPrefs.useSystemTheme = false; - defPrefs.drawPeaks = true; - defPrefs.wfAntiAlias = false; - defPrefs.wfInterpolate = true; defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300. defPrefs.CIVisRadioModel = false; defPrefs.forceRTSasPTT = false; defPrefs.serialPortRadio = QString("auto"); defPrefs.serialPortBaud = 115200; - defPrefs.enablePTT = false; - defPrefs.niceTS = true; - defPrefs.enableRigCtlD = false; - defPrefs.rigCtlPort = 4533; - defPrefs.virtualSerialPort = QString("none"); defPrefs.localAFgain = 255; - defPrefs.wflength = 160; - defPrefs.confirmExit = true; - defPrefs.confirmPowerOff = true; - defPrefs.meter2Type = meterNone; udpDefPrefs.ipAddress = QString(""); udpDefPrefs.controlLANPort = 50001; @@ -532,157 +429,12 @@ void servermain::loadSettings() { qInfo(logSystem()) << "Loading settings from " << settings->fileName(); - // Radio and Comms: C-IV addr, port to use - settings->beginGroup("Radio"); - prefs.radioCIVAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt(); - prefs.CIVisRadioModel = (bool)settings->value("CIVisRadioModel", defPrefs.CIVisRadioModel).toBool(); - prefs.forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool(); - prefs.serialPortRadio = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString(); - prefs.serialPortBaud = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); - - if (prefs.serialPortBaud > 0) - { - serverConfig.baudRate = prefs.serialPortBaud; - } - - prefs.virtualSerialPort = settings->value("VirtualSerialPort", defPrefs.virtualSerialPort).toString(); - - prefs.localAFgain = (unsigned char)settings->value("localAFgain", defPrefs.localAFgain).toUInt(); - rxSetup.localAFgain = prefs.localAFgain; - txSetup.localAFgain = 255; - - settings->endGroup(); - - // Misc. user settings (enable PTT, draw peaks, etc) - settings->beginGroup("Controls"); - prefs.enablePTT = settings->value("EnablePTT", defPrefs.enablePTT).toBool(); - prefs.niceTS = settings->value("NiceTS", defPrefs.niceTS).toBool(); - settings->endGroup(); - - settings->beginGroup("LAN"); - - prefs.enableLAN = settings->value("EnableLAN", defPrefs.enableLAN).toBool(); - - prefs.enableRigCtlD = settings->value("EnableRigCtlD", defPrefs.enableRigCtlD).toBool(); - prefs.rigCtlPort = settings->value("RigCtlPort", defPrefs.rigCtlPort).toInt(); - udpPrefs.ipAddress = settings->value("IPAddress", udpDefPrefs.ipAddress).toString(); - udpPrefs.controlLANPort = settings->value("ControlLANPort", udpDefPrefs.controlLANPort).toInt(); - udpPrefs.username = settings->value("Username", udpDefPrefs.username).toString(); - udpPrefs.password = settings->value("Password", udpDefPrefs.password).toString(); - - rxSetup.isinput = false; - txSetup.isinput = true; - - rxSetup.latency = settings->value("AudioRXLatency", "150").toInt(); - txSetup.latency = settings->value("AudioTXLatency", "150").toInt(); - rxSetup.samplerate = settings->value("AudioRXSampleRate", "48000").toInt(); - txSetup.samplerate = rxSetup.samplerate; - rxSetup.codec = settings->value("AudioRXCodec", "4").toInt(); - txSetup.codec = settings->value("AudioTXCodec", "4").toInt(); - rxSetup.name = settings->value("AudioOutput", "").toString(); - qInfo(logGui()) << "Got Audio Output: " << rxSetup.name; - - txSetup.name = settings->value("AudioInput", "").toString(); - // qInfo(logGui()) << "Got Audio Input: " << txSetup.name; int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); - - rxSetup.resampleQuality = settings->value("ResampleQuality", "4").toInt(); - txSetup.resampleQuality = rxSetup.resampleQuality; - - udpPrefs.clientName = settings->value("ClientName", udpDefPrefs.clientName).toString(); - - settings->endGroup(); - - - settings->beginGroup("Server"); - serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); - serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); - serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); - serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); - - serverConfig.users.clear(); - - int numUsers = settings->beginReadArray("Users"); - if (numUsers > 0) { - { - for (int f = 0; f < numUsers; f++) - { - settings->setArrayIndex(f); - SERVERUSER user; - user.username = settings->value("Username", "").toString(); - user.password = settings->value("Password", "").toString(); - user.userType = settings->value("UserType", 0).toInt(); - serverConfig.users.append(user); - } - } + int numRadios=settings->beginReadArray("Radios"); + int tempNum = numRadios; + if (numRadios == 0) { settings->endArray(); + numRadios = 1; } - else { - /* Support old way of storing users*/ - settings->endArray(); - numUsers = settings->value("ServerNumUsers", 2).toInt(); - for (int f = 0; f < numUsers; f++) - { - SERVERUSER user; - user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); - user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); - user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); - serverConfig.users.append(user); - } - } - - serverRxSetup.isinput = true; - serverTxSetup.isinput = false; - serverRxSetup.localAFgain = 255; - serverTxSetup.localAFgain = 255; - - serverRxSetup.name = settings->value("ServerAudioInput", "").toString(); - qInfo(logGui()) << "Got Server Audio Input: " << serverRxSetup.name; - - serverRxSetup.resampleQuality = rxSetup.resampleQuality; - serverTxSetup.resampleQuality = serverRxSetup.resampleQuality; - - serverTxSetup.name = settings->value("ServerAudioOutput", "").toString(); - qInfo(logGui()) << "Got Server Audio Output: " << serverTxSetup.name; - - settings->endGroup(); - - // Memory channels - - settings->beginGroup("Memory"); - int size = settings->beginReadArray("Channel"); - int chan = 0; - double freq; - unsigned char mode; - bool isSet; - - // Annoying: QSettings will write the array to the - // preference file starting the array at 1 and ending at 100. - // Thus, we are writing the channel number each time. - // It is also annoying that they get written with their array - // numbers in alphabetical order without zero padding. - // Also annoying that the preference groups are not written in - // the order they are specified here. - - for (int i = 0; i < size; i++) - { - settings->setArrayIndex(i); - chan = settings->value("chan", 0).toInt(); - freq = settings->value("freq", 12.345).toDouble(); - mode = settings->value("mode", 0).toInt(); - isSet = settings->value("isSet", false).toBool(); - - if (isSet) - { - mem.setPreset(chan, freq, (mode_kind)mode); - } - } - - settings->endArray(); - settings->endGroup(); - - - - #if defined(RTAUDIO) @@ -723,22 +475,6 @@ void servermain::loadSettings() unsigned int devices = audio->getDeviceCount(); qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; - for (unsigned int i = 1; i < devices; i++) { - info = audio->getDeviceInfo(i); - if (info.outputChannels > 0) { - if (serverTxSetup.name == info->name) { - serverTxSetup.port = i; - } - } - if (info.inputChannels > 0) { - if (serverRxSetup.name == info->name) { - serverRxSetup.port = i; - } - } - } - - delete audio; - #elif defined(PORTAUDIO) // Use PortAudio device enumeration @@ -758,154 +494,165 @@ void servermain::loadSettings() qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; const PaDeviceInfo* info; - for (int i = 0; i < numDevices; i++) - { - info = Pa_GetDeviceInfo(i); - if (info->maxInputChannels > 0) { - if (serverTxSetup.name == info->name) { - serverTxSetup.port = i; - } - } - if (info->maxOutputChannels > 0) { - if (serverRxSetup.name == info->name) { - serverRxSetup.port = i; - } - } - } + #else - // If no external library is configured, use QTMultimedia - // - // Set these to default audio devices initially. - rxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - txSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverRxSetup.port = QAudioDeviceInfo::defaultInputDevice(); - serverTxSetup.port = QAudioDeviceInfo::defaultOutputDevice(); - - - // Enumerate audio devices, need to do before settings are loaded. const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { - if (deviceInfo.deviceName() == serverTxSetup.name) { - serverTxSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Server Audio Input: " << serverTxSetup.name; - } - } - const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); - for (const QAudioDeviceInfo& deviceInfo : audioInputs) { - if (deviceInfo.deviceName() == serverRxSetup.name) { - serverRxSetup.port = deviceInfo; - qInfo(logGui()) << "Setting Server Audio Output: " << serverRxSetup.name; + +#endif + for (int i = 0; i < numRadios; i++) { + if (tempNum == 0) { + settings->beginGroup("Radio"); + } + 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", "").toString(); + tempPrefs.baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt(); + + if (tempPrefs.rigName=="") + { + 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()) + { + tempPrefs.rigName = serialPortInfo.serialNumber(); + } + } + } + QString guid = settings->value("GUID", "").toString(); + if (guid.isEmpty()) { + guid = QUuid::createUuid().toString(); + settings->setValue("GUID", 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(); + + // 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 (info.inputChannels > 0) { + if (tempPrefs.rxAudio.name == info->name) { + tempPrefs.rxAudio.port = i; + } + } + } +#elif defined(PORTAUDIO) + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + if (info->maxInputChannels > 0) { + if (tempPrefs.txAudio.name == info->name) { + tempPrefs.txAudio.port = i; + } + } + if (info->maxOutputChannels > 0) { + if (tempPrefs.rxAudio.name == info->name) { + tempPrefs.rxAudio.port = i; + } + } + } +#else + + /* If no external library is configured, use QTMultimedia + // Set these to default audio devices initially. + */ + + //qInfo(logAudio()) << "Looking for audio output devices"; + for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { + if (deviceInfo.deviceName() == tempPrefs.txAudioSetup.name) { + tempPrefs.txAudioSetup.port = deviceInfo; + } + } + + //qInfo(logAudio()) << "Looking for audio input devices"; + for (const QAudioDeviceInfo& deviceInfo : audioInputs) { + if (deviceInfo.deviceName() == tempPrefs.rxAudioSetup.name) { + tempPrefs.rxAudioSetup.port = deviceInfo; + } + } +#endif + tempPrefs.rig = Q_NULLPTR; + tempPrefs.rigThread = Q_NULLPTR; + serverConfig.rigs.append(tempPrefs); + if (tempNum == 0) { + settings->endGroup(); + } + } + if (tempNum > 0) { + settings->endArray(); + } + + + settings->beginGroup("Server"); + serverConfig.enabled = settings->value("ServerEnabled", false).toBool(); + serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt(); + serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt(); + serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt(); + + serverConfig.users.clear(); + + int numUsers = settings->beginReadArray("Users"); + if (numUsers > 0) { + { + for (int f = 0; f < numUsers; f++) + { + settings->setArrayIndex(f); + SERVERUSER user; + user.username = settings->value("Username", "").toString(); + user.password = settings->value("Password", "").toString(); + user.userType = settings->value("UserType", 0).toInt(); + serverConfig.users.append(user); + + } + } + settings->endArray(); + } + else { + /* Support old way of storing users just to get them loaded*/ + settings->endArray(); + numUsers = settings->value("ServerNumUsers", 2).toInt(); + for (int f = 0; f < numUsers; f++) + { + SERVERUSER user; + user.username = settings->value("ServerUsername_" + QString::number(f), "").toString(); + user.password = settings->value("ServerPassword_" + QString::number(f), "").toString(); + user.userType = settings->value("ServerUserType_" + QString::number(f), 0).toInt(); + serverConfig.users.append(user); } } + settings->endGroup(); + settings->sync(); + +#if defined(RTAUDIO) + delete audio; #endif } - -void servermain:: getInitialRigState() -{ - // Initial list of queries to the radio. - // These are made when the program starts up - // and are used to adjust the UI to match the radio settings - // the polling interval is set at 200ms. Faster is possible but slower - // computers will glitch occassionally. - - issueDelayedCommand(cmdGetFreq); - issueDelayedCommand(cmdGetMode); - - issueDelayedCommand(cmdNone); - - issueDelayedCommand(cmdGetFreq); - issueDelayedCommand(cmdGetMode); - - // From left to right in the UI: - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetDataMode); - issueDelayedCommand(cmdGetModInput); - issueDelayedCommand(cmdGetModDataInput); - } - issueDelayedCommand(cmdGetRxGain); - issueDelayedCommand(cmdGetAfGain); - issueDelayedCommand(cmdGetSql); - - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetTxPower); - issueDelayedCommand(cmdGetCurrentModLevel); // level for currently selected mod sources - } - - issueDelayedCommand(cmdGetSpectrumRefLevel); - issueDelayedCommand(cmdGetDuplexMode); - - if(rigCaps.hasSpectrum) - { - issueDelayedCommand(cmdDispEnable); - issueDelayedCommand(cmdSpecOn); - } - - if (rigCaps.hasTransmit) - { - issueDelayedCommand(cmdGetModInput); - issueDelayedCommand(cmdGetModDataInput); - } - - if(rigCaps.hasCTCSS) - { - issueDelayedCommand(cmdGetTone); - issueDelayedCommand(cmdGetTSQL); - } - if(rigCaps.hasDTCS) - { - issueDelayedCommand(cmdGetDTCS); - } - issueDelayedCommand(cmdGetRptAccessMode); - - if(rigCaps.hasAntennaSel) - { - issueDelayedCommand(cmdGetAntenna); - } - if(rigCaps.hasAttenuator) - { - issueDelayedCommand(cmdGetAttenuator); - } - if(rigCaps.hasPreamp) - { - issueDelayedCommand(cmdGetPreamp); - } - - issueDelayedCommand(cmdGetRitEnabled); - issueDelayedCommand(cmdGetRitValue); - - if(rigCaps.hasIFShift) - issueDelayedCommand(cmdGetIFShift); - if(rigCaps.hasTBPF) - { - issueDelayedCommand(cmdGetTPBFInner); - issueDelayedCommand(cmdGetTPBFOuter); - } - - if(rigCaps.hasSpectrum) - { - issueDelayedCommand(cmdGetSpectrumMode); - issueDelayedCommand(cmdGetSpectrumSpan); - } - - issueDelayedCommand(cmdNone); - issueDelayedCommand(cmdStartRegularPolling); - - if(rigCaps.hasATU) - { - issueDelayedCommand(cmdGetATUStatus); - } - - delayedCommand->start(); -} - void servermain::receivePTTstatus(bool pttOn) { // This is the only place where amTransmitting and the transmit button text should be changed: @@ -913,700 +660,32 @@ void servermain::receivePTTstatus(bool pttOn) amTransmitting = pttOn; } - -void servermain::doCmd(commandtype cmddata) -{ - cmds cmd = cmddata.cmd; - std::shared_ptr data = cmddata.data; - - // This switch is for commands with parameters. - // the "default" for non-parameter commands is to call doCmd(cmd). - switch (cmd) - { - case cmdSetFreq: - { - lastFreqCmdTime_ms = QDateTime::currentMSecsSinceEpoch(); - freqt f = (*std::static_pointer_cast(data)); - emit setFrequency(0,f); - break; - } - case cmdSetMode: - { - mode_info m = (*std::static_pointer_cast(data)); - emit setMode(m); - break; - } - case cmdSetTxPower: - { - unsigned char txpower = (*std::static_pointer_cast(data)); - emit setTxPower(txpower); - break; - } - case cmdSetMicGain: - { - unsigned char micgain = (*std::static_pointer_cast(data)); - emit setTxPower(micgain); - break; - } - case cmdSetRxRfGain: - { - unsigned char rfgain = (*std::static_pointer_cast(data)); - emit setRfGain(rfgain); - break; - } - case cmdSetModLevel: - { - unsigned char modlevel = (*std::static_pointer_cast(data)); - rigInput currentIn; - if(usingDataMode) - { - currentIn = currentModDataSrc; - } else { - currentIn = currentModSrc; - } - emit setModLevel(currentIn, modlevel); - break; - } - case cmdSetAfGain: - { - unsigned char afgain = (*std::static_pointer_cast(data)); - emit setAfGain(afgain); - break; - } - case cmdSetSql: - { - unsigned char sqlLevel = (*std::static_pointer_cast(data)); - emit setSql(sqlLevel); - break; - } - case cmdSetIFShift: - { - unsigned char IFShiftLevel = (*std::static_pointer_cast(data)); - emit setIFShift(IFShiftLevel); - break; - } - case cmdSetTPBFInner: - { - unsigned char innterLevel = (*std::static_pointer_cast(data)); - emit setTPBFInner(innterLevel); - break; - } - case cmdSetTPBFOuter: - { - unsigned char outerLevel = (*std::static_pointer_cast(data)); - emit setTPBFOuter(outerLevel); - break; - } - case cmdSetPTT: - { - bool pttrequest = (*std::static_pointer_cast(data)); - emit setPTT(pttrequest); - break; - } - case cmdSetATU: - { - bool atuOn = (*std::static_pointer_cast(data)); - emit setATU(atuOn); - break; - } - case cmdSetUTCOffset: - { - timekind u = (*std::static_pointer_cast(data)); - emit setUTCOffset(u); - break; - } - case cmdSetTime: - { - timekind t = (*std::static_pointer_cast(data)); - emit setTime(t); - break; - } - case cmdSetDate: - { - datekind d = (*std::static_pointer_cast(data)); - emit setDate(d); - break; - } - default: - doCmd(cmd); - break; - } - -} - - -void servermain::doCmd(cmds cmd) -{ - // Use this function to take action upon a command. - switch (cmd) - { - case cmdNone: - //qInfo(logSystem()) << "NOOP"; - break; - case cmdGetRigID: - emit getRigID(); - break; - case cmdGetRigCIV: - // if(!know rig civ already) - if (!haveRigCaps) - { - emit getRigCIV(); - issueDelayedCommand(cmdGetRigCIV); // This way, we stay here until we get an answer. - } - break; - case cmdGetFreq: - emit getFrequency(); - break; - case cmdGetMode: - emit getMode(); - break; - case cmdGetDataMode: - if (rigCaps.hasDataModes) - emit getDataMode(); - break; - case cmdSetModeFilter: - emit setMode(setModeVal, setFilterVal); - break; - case cmdSetDataModeOff: - break; - case cmdSetDataModeOn: - break; - case cmdGetRitEnabled: - emit getRitEnabled(); - break; - case cmdGetRitValue: - emit getRitValue(); - break; - case cmdGetModInput: - emit getModInput(false); - break; - case cmdGetModDataInput: - emit getModInput(true); - break; - case cmdGetCurrentModLevel: - // TODO: Add delay between these queries - emit getModInputLevel(currentModSrc); - emit getModInputLevel(currentModDataSrc); - break; - case cmdGetDuplexMode: - emit getDuplexMode(); - break; - case cmdGetTone: - emit getTone(); - break; - case cmdGetTSQL: - emit getTSQL(); - break; - case cmdGetDTCS: - emit getDTCS(); - break; - case cmdGetRptAccessMode: - emit getRptAccessMode(); - break; - case cmdDispEnable: - emit scopeDisplayEnable(); - break; - case cmdDispDisable: - emit scopeDisplayDisable(); - break; - case cmdGetSpectrumMode: - emit getScopeMode(); - break; - case cmdGetSpectrumSpan: - emit getScopeSpan(); - break; - case cmdSpecOn: - emit spectOutputEnable(); - break; - case cmdSpecOff: - emit spectOutputDisable(); - break; - case cmdGetRxGain: - emit getRfGain(); - break; - case cmdGetAfGain: - emit getAfGain(); - break; - case cmdGetSql: - emit getSql(); - break; - case cmdGetIFShift: - emit getIfShift(); - break; - case cmdGetTPBFInner: - emit getTPBFInner(); - break; - case cmdGetTPBFOuter: - emit getTPBFOuter(); - break; - case cmdGetTxPower: - emit getTxPower(); - break; - case cmdGetMicGain: - emit getMicGain(); - break; - case cmdGetSpectrumRefLevel: - emit getSpectrumRefLevel(); - break; - case cmdGetATUStatus: - if (rigCaps.hasATU) - emit getATUStatus(); - break; - case cmdStartATU: - if (rigCaps.hasATU) - emit startATU(); - break; - case cmdGetAttenuator: - emit getAttenuator(); - break; - case cmdGetPreamp: - emit getPreamp(); - break; - case cmdGetAntenna: - emit getAntenna(); - break; - case cmdScopeCenterMode: - emit setScopeMode(spectModeCenter); - break; - case cmdScopeFixedMode: - emit setScopeMode(spectModeFixed); - break; - case cmdGetPTT: - emit getPTT(); - break; - case cmdGetTxRxMeter: - if (amTransmitting) - emit getMeters(meterPower); - else - emit getMeters(meterS); - break; - case cmdGetSMeter: - if (!amTransmitting) - emit getMeters(meterS); - break; - case cmdGetCenterMeter: - if (!amTransmitting) - emit getMeters(meterCenter); - break; - case cmdGetPowerMeter: - if (amTransmitting) - emit getMeters(meterPower); - break; - case cmdGetSWRMeter: - if (amTransmitting) - emit getMeters(meterSWR); - break; - case cmdGetIdMeter: - emit getMeters(meterCurrent); - break; - case cmdGetVdMeter: - emit getMeters(meterVoltage); - break; - case cmdGetALCMeter: - if (amTransmitting) - emit getMeters(meterALC); - break; - case cmdGetCompMeter: - if (amTransmitting) - emit getMeters(meterComp); - break; - case cmdStartRegularPolling: - runPeriodicCommands = true; - break; - case cmdStopRegularPolling: - runPeriodicCommands = false; - break; - case cmdQueNormalSpeed: - if (usingLAN) - { - delayedCommand->setInterval(delayedCmdIntervalLAN_ms); - } - else { - delayedCommand->setInterval(delayedCmdIntervalSerial_ms); - } - break; - default: - qInfo(logSystem()) << __PRETTY_FUNCTION__ << "WARNING: Command fell through of type: " << (unsigned int)cmd; - break; - } -} - - - - -void servermain::sendRadioCommandLoop() -{ - // Called by the periodicPollingTimer, see setInitialTiming() - - if(!(loopTickCounter % 2)) - { - // if ther's a command waiting, run it. - if(!delayedCmdQue.empty()) - { - commandtype cmddata = delayedCmdQue.front(); - delayedCmdQue.pop_front(); - doCmd(cmddata); - } else if(!(loopTickCounter % 10)) - { - // pick from useful queries to make now and then - if(haveRigCaps && !slowPollCmdQueue.empty()) - { - - int nCmds = slowPollCmdQueue.size(); - cmds sCmd = slowPollCmdQueue[(slowCmdNum++)%nCmds]; - doCmd(sCmd); - } - } - } else { - // odd-number ticks: - // s-meter or other metering - if(haveRigCaps && !periodicCmdQueue.empty()) - { - int nCmds = periodicCmdQueue.size(); - cmds pcmd = periodicCmdQueue[ (pCmdNum++)%nCmds ]; - doCmd(pcmd); - } - } - loopTickCounter++; -} - -void servermain::issueDelayedCommand(cmds cmd) -{ - // Append to end of command queue - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueDelayedCommandPriority(cmds cmd) -{ - // Places the new command at the top of the queue - // Use only when needed. - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueDelayedCommandUnique(cmds cmd) -{ - // Use this function to insert commands where - // multiple (redundant) commands don't make sense. - - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = NULL; - - // The following is both expensive and not that great, - // since it does not check if the arguments are the same. - bool found = false; - for(unsigned int i=0; i < delayedCmdQue.size(); i++) - { - if(delayedCmdQue.at(i).cmd == cmd) - { - found = true; - break; - } - } - - if(!found) - { - delayedCmdQue.push_front(cmddata); - } - -// if( std::find(delayedCmdQue.begin(), delayedCmdQue.end(), cmddata ) == delayedCmdQue.end()) -// { -// delayedCmdQue.push_front(cmddata); -// } - -} - -void servermain::issueCmd(cmds cmd, mode_info m) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new mode_info(m)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, freqt f) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new freqt(f)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, timekind t) -{ - qDebug(logSystem()) << "Issuing timekind command with data: " << t.hours << " hours, " << t.minutes << " minutes, " << t.isMinus << " isMinus"; - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new timekind(t)); - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueCmd(cmds cmd, datekind d) -{ - qDebug(logSystem()) << "Issuing datekind command with data: " << d.day << " day, " << d.month << " month, " << d.year << " year."; - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new datekind(d)); - delayedCmdQue.push_front(cmddata); -} - -void servermain::issueCmd(cmds cmd, int i) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new int(i)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new char(c)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, bool b) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new bool(b)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmd(cmds cmd, unsigned char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new unsigned char(c)); - delayedCmdQue.push_back(cmddata); -} - -void servermain::issueCmdUniquePriority(cmds cmd, bool b) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new bool(b)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, unsigned char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new unsigned char(c)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, char c) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new char(c)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::issueCmdUniquePriority(cmds cmd, freqt f) -{ - commandtype cmddata; - cmddata.cmd = cmd; - cmddata.data = std::shared_ptr(new freqt(f)); - delayedCmdQue.push_front(cmddata); - removeSimilarCommand(cmd); -} - -void servermain::removeSimilarCommand(cmds cmd) -{ - // pop anything out that is of the same kind of command: - // pop anything out that is of the same kind of command: - // Start at 1 since we put one in at zero that we want to keep. - for(unsigned int i=1; i < delayedCmdQue.size(); i++) - { - if(delayedCmdQue.at(i).cmd == cmd) - { - //delayedCmdQue[i].cmd = cmdNone; - delayedCmdQue.erase(delayedCmdQue.begin()+i); - // i -= 1; - } - } -} - -void servermain::receiveRigID(rigCapabilities rigCaps) -{ - // Note: We intentionally request rigID several times - // because without rigID, we can't do anything with the waterfall. - if(haveRigCaps) - { - return; - } else { - - qDebug(logSystem()) << "Rig name: " << rigCaps.modelName; - qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan; - qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax; - qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax; - qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax; - qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum; - - this->rigCaps = rigCaps; - this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. - haveRigCaps = true; - // Added so that server receives rig capabilities. - emit sendRigCaps(rigCaps); - - } -} - -void servermain::initPeriodicCommands() -{ - // This function places periodic polling commands into a queue. - // The commands are run using a timer, - // and the timer is started by the delayed command cmdStartPeriodicTimer. - -} - -void servermain::insertPeriodicCommand(cmds cmd, unsigned char priority) -{ - // TODO: meaningful priority - // These commands get run at the fastest pace possible - // Typically just metering. - if(priority < 10) - { - periodicCmdQueue.push_front(cmd); - } else { - periodicCmdQueue.push_back(cmd); - } -} - -void servermain::insertPeriodicCommandUnique(cmds cmd) -{ - // Use this function to insert a non-duplicate command - // into the fast periodic polling queue, typically - // meter commands where high refresh rates are desirable. - - removePeriodicCommand(cmd); - periodicCmdQueue.push_front(cmd); -} - -void servermain::removePeriodicCommand(cmds cmd) -{ - while(true) - { - auto it = std::find(this->periodicCmdQueue.begin(), this->periodicCmdQueue.end(), cmd); - if(it != periodicCmdQueue.end()) - { - periodicCmdQueue.erase(it); - } else { - break; - } - } -} - - -void servermain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) -{ - // TODO: meaningful priority - // These commands are run every 20 "ticks" of the primary radio command loop - // Basically 20 times less often than the standard peridic command - if(priority < 10) - { - slowPollCmdQueue.push_front(cmd); - } else { - slowPollCmdQueue.push_back(cmd); - } -} - - void servermain::handlePttLimit() { - // transmission time exceeded! + // ptt time exceeded! std::cout << "Transmit timeout at 3 minutes. Sending PTT OFF command now.\n"; - issueCmdUniquePriority(cmdSetPTT, false); - issueDelayedCommand(cmdGetPTT); -} - -void servermain::calculateTimingParameters() -{ - // Function for calculating polling parameters. - // Requires that we know the "baud rate" of the actual - // radio connection. - - // baud on the serial port reflects the actual rig connection, - // even if a client-server connection is being used. - // Computed time for a 10 byte message, with a safety factor of 2. - - if (prefs.serialPortBaud == 0) - { - prefs.serialPortBaud = 9600; - qInfo(logSystem()) << "WARNING: baud rate received was zero. Assuming 9600 baud, performance may suffer."; - } - - unsigned int usPerByte = 9600*1000 / prefs.serialPortBaud; - unsigned int msMinTiming=usPerByte * 10*2/1000; - if(msMinTiming < 25) - msMinTiming = 25; - - if(haveRigCaps && rigCaps.hasFDcomms) - { - delayedCommand->setInterval( msMinTiming); // 20 byte message - } else { - delayedCommand->setInterval( msMinTiming * 3); // 20 byte message - } - - - qInfo(logSystem()) << "Delay command interval timing: " << delayedCommand->interval() << "ms"; - - // Normal: - delayedCmdIntervalLAN_ms = delayedCommand->interval(); - delayedCmdIntervalSerial_ms = delayedCommand->interval(); - - // startup initial state: - delayedCmdStartupInterval_ms = delayedCommand->interval() * 3; + emit setPTT(false); + emit getPTT(); } void servermain::receiveBaudRate(quint32 baud) { qInfo() << "Received serial port baud rate from remote server:" << baud; prefs.serialPortBaud = baud; - calculateTimingParameters(); } void servermain::powerRigOn() { emit sendPowerOn(); - - delayedCommand->setInterval(3000); // 3 seconds - issueDelayedCommand(cmdQueNormalSpeed); - issueDelayedCommand(cmdStartRegularPolling); // s-meter, etc - delayedCommand->start(); } void servermain::powerRigOff() { - delayedCommand->stop(); - delayedCmdQue.clear(); - emit sendPowerOff(); } void servermain::receiveStateInfo(rigstate* state) { - qInfo("Setting rig state for wfmain"); - rigState = state; -} - - -void servermain::handleCtrlC(int sig) { - if (sig == 2) { - QCoreApplication::quit(); - //exit(EXIT_FAILURE); - } + qInfo("Received rig state for wfmain"); + //rigState = state; } diff --git a/servermain.h b/servermain.h index 7a870ff..cea805c 100644 --- a/servermain.h +++ b/servermain.h @@ -168,30 +168,24 @@ private slots: void receiveCommReady(); void receivePTTstatus(bool pttOn); - void receiveRigID(rigCapabilities rigCaps); void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); - void sendRadioCommandLoop(); void receiveBaudRate(quint32 baudrate); void handlePttLimit(); - void receiveStatusUpdate(QString text); + void receiveStatusUpdate(networkStatus status); void receiveStateInfo(rigstate* state); private: QSettings *settings=Q_NULLPTR; void loadSettings(); - void getInitialRigState(); - void openRig(); void powerRigOff(); void powerRigOn(); QStringList portList; QString serialPortRig; - rigCommander * rig=Q_NULLPTR; - QThread* rigThread = Q_NULLPTR; QTimer * delayedCommand; QTimer * pttTimer; uint16_t loopTickCounter; @@ -199,7 +193,6 @@ private: QString lastMessage=""; void makeRig(); - void rigConnections(); void removeRig(); void findSerialPort(); @@ -235,119 +228,19 @@ private: unsigned char setModeVal=0; unsigned char setFilterVal=0; - enum cmds {cmdNone, cmdGetRigID, cmdGetRigCIV, cmdGetFreq, cmdSetFreq, cmdGetMode, cmdSetMode, - cmdGetDataMode, cmdSetModeFilter, cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue, - cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain, - cmdGetSql, cmdSetSql, cmdGetIFShift, cmdSetIFShift, cmdGetTPBFInner, cmdSetTPBFInner, - cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, - cmdSetATU, cmdStartATU, cmdGetSpectrumMode, - cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT, - cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel, - cmdGetSpectrumRefLevel, cmdGetDuplexMode, cmdGetModInput, cmdGetModDataInput, - cmdGetCurrentModLevel, cmdStartRegularPolling, cmdStopRegularPolling, cmdQueNormalSpeed, - cmdGetVdMeter, cmdGetIdMeter, cmdGetSMeter, cmdGetCenterMeter, cmdGetPowerMeter, - cmdGetSWRMeter, cmdGetALCMeter, cmdGetCompMeter, cmdGetTxRxMeter, - cmdGetTone, cmdGetTSQL, cmdGetDTCS, cmdGetRptAccessMode, cmdGetPreamp, cmdGetAttenuator, cmdGetAntenna, - cmdSetTime, cmdSetDate, cmdSetUTCOffset}; - - struct commandtype { - cmds cmd; - std::shared_ptr data; - }; - - std::deque delayedCmdQue; // rapid que for commands to the radio - std::deque periodicCmdQueue; // rapid que for metering - std::deque slowPollCmdQueue; // slow, regular checking for UI sync - void doCmd(cmds cmd); - void doCmd(commandtype cmddata); - - void issueCmd(cmds cmd, freqt f); - void issueCmd(cmds cmd, mode_info m); - void issueCmd(cmds cmd, timekind t); - void issueCmd(cmds cmd, datekind d); - void issueCmd(cmds cmd, int i); - void issueCmd(cmds cmd, unsigned char c); - void issueCmd(cmds cmd, char c); - void issueCmd(cmds cmd, bool b); - - // These commands pop_front and remove similar commands: - void issueCmdUniquePriority(cmds cmd, bool b); - void issueCmdUniquePriority(cmds cmd, unsigned char c); - void issueCmdUniquePriority(cmds cmd, char c); - void issueCmdUniquePriority(cmds cmd, freqt f); - - void removeSimilarCommand(cmds cmd); - - qint64 lastFreqCmdTime_ms; - - int pCmdNum = 0; - int delayedCmdIntervalLAN_ms = 100; - int delayedCmdIntervalSerial_ms = 100; - int delayedCmdStartupInterval_ms = 100; - bool runPeriodicCommands; bool usingLAN = false; - // Radio time sync: - QTimer *timeSync; - bool waitingToSetTimeDate; - void setRadioTimeDatePrep(); - timekind timesetpoint; - timekind utcsetting; - datekind datesetpoint; - - freqMemory mem; - struct colors { - QColor Dark_PlotBackground; - QColor Dark_PlotAxisPen; - QColor Dark_PlotLegendTextColor; - QColor Dark_PlotLegendBorderPen; - QColor Dark_PlotLegendBrush; - QColor Dark_PlotTickLabel; - QColor Dark_PlotBasePen; - QColor Dark_PlotTickPen; - QColor Dark_PeakPlotLine; - QColor Dark_TuningLine; - - QColor Light_PlotBackground; - QColor Light_PlotAxisPen; - QColor Light_PlotLegendTextColor; - QColor Light_PlotLegendBorderPen; - QColor Light_PlotLegendBrush; - QColor Light_PlotTickLabel; - QColor Light_PlotBasePen; - QColor Light_PlotTickPen; - QColor Light_PeakPlotLine; - QColor Light_TuningLine; - - } colorScheme; - struct preferences { - bool useFullScreen; - bool useDarkMode; - bool useSystemTheme; - bool drawPeaks; - bool wfAntiAlias; - bool wfInterpolate; - QString stylesheetPath; unsigned char radioCIVAddr; bool CIVisRadioModel; bool forceRTSasPTT; QString serialPortRadio; quint32 serialPortBaud; - bool enablePTT; - bool niceTS; - bool enableLAN; - bool enableRigCtlD; - quint16 rigCtlPort; - colors colorScheme; - QString virtualSerialPort; unsigned char localAFgain; - unsigned int wflength; - int wftheme; - bool confirmExit; - bool confirmPowerOff; - meterKind meter2Type; - // plot scheme + audioSetup rxAudio; + audioSetup txAudio; + rigCapabilities rigCaps; + bool haveRigCaps = false; } prefs; preferences defPrefs; @@ -361,35 +254,9 @@ private: audioSetup serverRxSetup; audioSetup serverTxSetup; - colors defaultColors; void setDefPrefs(); // populate default values to default prefs - void issueDelayedCommand(cmds cmd); - void issueDelayedCommandPriority(cmds cmd); - void issueDelayedCommandUnique(cmds cmd); - - // Fast command queue: - void initPeriodicCommands(); - void insertPeriodicCommand(cmds cmd, unsigned char priority); - void insertPeriodicCommandUnique(cmds cmd); - void removePeriodicCommand(cmds cmd); - - void insertSlowPeriodicCommand(cmds cmd, unsigned char priority); - void calculateTimingParameters(); - - - cmds meterKindToMeterCommand(meterKind m); - - int oldFreqDialVal; - - rigCapabilities rigCaps; - rigInput currentModSrc = inputUnknown; - rigInput currentModDataSrc = inputUnknown; - mode_kind currentMode = modeUSB; - mode_info currentModeInfo; - - bool haveRigCaps; bool amTransmitting; bool usingDataMode = false; @@ -405,33 +272,9 @@ private: rigCtlD* rigCtl = Q_NULLPTR; QThread* serverThread = Q_NULLPTR; - void bandStackBtnClick(); - bool waitingForBandStackRtn; - char bandStkBand; - char bandStkRegCode; - - bool freqLock; - - float tsPlus; - float tsPlusShift; - float tsPlusControl; - float tsPage; - float tsPageShift; - float tsWfScroll; - - unsigned int tsPlusHz; - unsigned int tsPlusShiftHz; - unsigned int tsPlusControlHz; - unsigned int tsPageHz; - unsigned int tsPageShiftHz; - unsigned int tsWfScrollHz; - unsigned int tsKnobHz; - rigstate* rigState = Q_NULLPTR; SERVERCONFIG serverConfig; - - static void handleCtrlC(int sig); }; Q_DECLARE_METATYPE(struct rigCapabilities) diff --git a/udpserver.cpp b/udpserver.cpp index d313249..11ddee5 100644 --- a/udpserver.cpp +++ b/udpserver.cpp @@ -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,6 +15,31 @@ udpServer::udpServer(SERVERCONFIG config, audioSetup outAudio, audioSetup inAudi void udpServer::init() { + 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()).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')) + ; + } srand(time(NULL)); // Generate random key timeStarted.start(); // Convoluted way to find the external IP address, there must be a better way???? @@ -112,7 +137,17 @@ udpServer::~udpServer() void udpServer::receiveRigCaps(rigCapabilities caps) { - this->rigCaps = caps; + 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=="") { + rig.rigName = caps.modelName; + } + } + } } void udpServer::controlReceived() @@ -164,8 +199,6 @@ void udpServer::controlReceived() connect(current->retransmitTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRetransmitRequest, this, current)); current->retransmitTimer->start(RETRANSMIT_PERIOD); - - current->commonCap = 0x8010; qInfo(logUdpServer()) << current->ipAddress.toString() << ": New Control connection created"; if (connMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) @@ -234,7 +267,9 @@ void udpServer::controlReceived() // Request for new token qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received create token request"; sendCapabilities(current); - sendConnectionInfo(current); + for (RIGCONFIG& radio : config.rigs) { + sendConnectionInfo(current, radio.guid); + } } else if (in->res == 0x01) { // Token disconnect @@ -245,7 +280,12 @@ void udpServer::controlReceived() // Disconnect audio/civ sendTokenResponse(current, in->res); current->isStreaming = false; - sendConnectionInfo(current); + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(radio.guid, current->guid, sizeof(radio.guid))) + { + sendConnectionInfo(current, radio.guid); + } + } } else { qInfo(logUdpServer()) << current->ipAddress.toString() << ": Received token request"; @@ -301,10 +341,10 @@ void udpServer::controlReceived() current->txBufferLen = qFromBigEndian(in->txbuffer); current->authInnerSeq = in->innerseq; - memcpy(current->macaddress, in->macaddress, 6); + memcpy(current->guid, in->guid, sizeof(in->guid)); sendStatus(current); current->authInnerSeq = 0x00; - sendConnectionInfo(current); + sendConnectionInfo(current,in->guid); qInfo(logUdpServer()) << current->ipAddress.toString() << ": rxCodec:" << current->rxCodec << " txCodec:" << current->txCodec << " rxSampleRate" << current->rxSampleRate << " txSampleRate" << current->txSampleRate << @@ -316,55 +356,62 @@ void udpServer::controlReceived() 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; - if (txaudio == Q_NULLPTR) - { - outAudio.codec = current->txCodec; - outAudio.samplerate = current->txSampleRate; - outAudio.isinput = false; - outAudio.latency = current->txBufferLen; - outAudio.isinput = false; + radio.txaudio = new audioHandler(); + radio.txAudioThread = new QThread(this); - txaudio = new audioHandler(); - txAudioThread = new QThread(this); + radio.txaudio->moveToThread(radio.txAudioThread); - txaudio->moveToThread(txAudioThread); + radio.txAudioThread->start(QThread::TimeCriticalPriority); - 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(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + 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)), txaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveAudioData(audioPacket)), radio.txaudio, SLOT(incomingAudio(audioPacket))); - } - if (rxaudio == Q_NULLPTR) - { - inAudio.codec = current->rxCodec; - inAudio.samplerate = current->rxSampleRate; - inAudio.latency = current->txBufferLen; - inAudio.isinput = true; + } + 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; - rxaudio = new audioHandler(); + radio.rxaudio = new audioHandler(); - rxAudioThread = new QThread(this); + radio.rxAudioThread = new QThread(this); - rxaudio->moveToThread(rxAudioThread); + radio.rxaudio->moveToThread(radio.rxAudioThread); - rxAudioThread->start(QThread::TimeCriticalPriority); + radio.rxAudioThread->start(QThread::TimeCriticalPriority); - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + //connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + connect(radio.rxAudioThread, SIGNAL(finished()), radio.rxaudio, SLOT(deleteLater())); - emit setupRxAudio(inAudio); + QMetaObject::invokeMethod(radio.rxaudio, [=]() { + radio.rxaudio->init(radio.rxAudioSetup); + }, Qt::QueuedConnection); - rxAudioTimer = new QTimer(); - rxAudioTimer->setTimerType(Qt::PreciseTimer); - connect(rxAudioTimer, &QTimer::timeout, this, std::bind(&udpServer::sendRxAudio, this)); - rxAudioTimer->start(TXAUDIO_PERIOD); + 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); + } } } @@ -420,6 +467,7 @@ void udpServer::civReceived() { current->controlClient = client; client->civClient = current; + memcpy(current->guid, client->guid, sizeof(current->guid)); } } } @@ -518,9 +566,18 @@ void udpServer::civReceived() qDebug(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; qInfo(logUdpServer()) << current->ipAddress.toString() << ": Detected different remote CI-V:" << hex << current->civId; } else if (r.length() > lastFE+2 && (quint8)r[lastFE+2] != 0xE1) { 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))) + { + // Only send to the rig that it belongs to! + QMetaObject::invokeMethod(radio.rig, [=]() { + radio.rig->dataFromServer(r.mid(0x15));; + }, Qt::DirectConnection); + } + } - emit haveDataFromServer(r.mid(0x15)); } else { qInfo(logUdpServer()) << current->ipAddress.toString() << ": Datalen mismatch " << quint16(in->datalen + 0x15) << ":" << (quint16)in->len; @@ -571,6 +628,7 @@ void udpServer::audioReceived() { current->controlClient = client; client->audioClient = current; + memcpy(current->guid, client->guid, sizeof(current->guid)); } } } @@ -743,7 +801,7 @@ void udpServer::commonReceived(QList* l, CLIENT* current, QByteArray r) } default: { - //break; + //break } } @@ -1036,13 +1094,8 @@ void udpServer::sendLoginResponse(CLIENT* c, bool allowed) void udpServer::sendCapabilities(CLIENT* c) { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending Capabilities :" << c->txSeq; - capabilities_packet p; - radio_cap_packet r; memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00! - memset(r.packet, 0x0, sizeof(r)); // We can't be sure it is initialized with 0x00! - p.len = sizeof(p); p.type = 0x00; p.seq = c->txSeq; p.sentid = c->myId; @@ -1050,79 +1103,86 @@ void udpServer::sendCapabilities(CLIENT* c) p.innerseq = c->authInnerSeq; p.tokrequest = c->tokenRx; p.token = c->tokenTx; - p.payloadsize = sizeof(p)-0x0f; p.res = 0x0202; - p.numradios = 0x01; - r.commoncap = c->commonCap; - - memcpy(r.macaddress, macAddress.toLocal8Bit(), 6); - // IRU seems to expect an "Icom" mac address so replace the first 3 octets of our Mac with one in their range! - memcpy(r.macaddress, QByteArrayLiteral("\x00\x90\xc7").constData(), 3); - memcpy(r.name, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); - memcpy(r.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); - - if (rigCaps.hasWiFi && !rigCaps.hasEthernet) { - r.conntype = 0x0707; // 0x0707 for wifi rig. - } - else { - r.conntype = 0x073f; // 0x073f for ethernet rig. - } - - r.civ = rigCaps.civ; - r.baudrate = (quint32)qToBigEndian(config.baudRate); - /* - 0x80 = 12K only - 0x40 = 44.1K only - 0x20 = 22.05K only - 0x10 = 11.025K only - 0x08 = 48K only - 0x04 = 32K only - 0x02 = 16K only - 0x01 = 8K only - */ - if (rxaudio == Q_NULLPTR) { - r.rxsample = 0x8b01; // all rx sample frequencies supported - } - else { - if (rxSampleRate == 48000) { - r.rxsample = 0x0800; // fixed rx sample frequency - } - else if (rxSampleRate == 32000) { - r.rxsample = 0x0400; - } - else if (rxSampleRate == 24000) { - r.rxsample = 0x0001; - } - else if (rxSampleRate == 16000) { - r.rxsample = 0x0200; - } - else if (rxSampleRate == 12000) { - r.rxsample = 0x8000; - } - } - - if (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"; - } - else { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Disable tx audio for client"; - r.txsample = 0; - } - - // I still don't know what these are? - r.enableb = 0x01; // 0x01 doesn't seem to do anything? - r.enablec = 0x01; // 0x01 doesn't seem to do anything? - r.capf = 0x5001; - r.capg = 0x0190; + p.numradios = config.rigs.count(); SEQBUFENTRY s; s.seqNum = p.seq; s.timeSent = QTime::currentTime(); s.retransmitCount = 0; - s.data = QByteArray::fromRawData((const char*)p.packet, sizeof(p)); - s.data.append(QByteArray::fromRawData((const char*)r.packet, sizeof(r))); + + 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.audio, QByteArrayLiteral("ICOM_VAUDIO").constData(), 11); + + if (rig.hasWiFi && !rig.hasEthernet) { + r.conntype = 0x0707; // 0x0707 for wifi rig. + } + else { + r.conntype = 0x073f; // 0x073f for ethernet rig. + } + + r.civ = rig.civAddr; + r.baudrate = (quint32)qToBigEndian(rig.baudRate); + /* + 0x80 = 12K only + 0x40 = 44.1K only + 0x20 = 22.05K only + 0x10 = 11.025K only + 0x08 = 48K only + 0x04 = 32K only + 0x02 = 16K only + 0x01 = 8K only + */ + if (rig.rxaudio == Q_NULLPTR) { + r.rxsample = 0x8b01; // all rx sample frequencies supported + } + else { + if (rxSampleRate == 48000) { + r.rxsample = 0x0800; // fixed rx sample frequency + } + else if (rxSampleRate == 32000) { + r.rxsample = 0x0400; + } + else if (rxSampleRate == 24000) { + r.rxsample = 0x0001; + } + else if (rxSampleRate == 16000) { + r.rxsample = 0x0200; + } + else if (rxSampleRate == 12000) { + r.rxsample = 0x8000; + } + } + + 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"; + } + else { + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Disable tx audio for client"; + r.txsample = 0; + } + + // I still don't know what these are? + r.enableb = 0x01; // 0x01 doesn't seem to do anything? + r.enablec = 0x01; // 0x01 doesn't seem to do anything? + r.capf = 0x5001; + r.capg = 0x0190; + s.data.append(QByteArray::fromRawData((const char*)r.packet, sizeof(r))); + } + + p.len = sizeof(p)+s.data.length(); + p.payloadsize = sizeof(p)+s.data.length() - 0x0f; + + s.data.insert(0,QByteArray::fromRawData((const char*)p.packet, sizeof(p))); + if (c->txMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) { if (c->txSeqBuf.size() > BUFSIZE) @@ -1155,68 +1215,79 @@ void udpServer::sendCapabilities(CLIENT* c) // When client has requested civ/audio connection, this will contain their details // Also used to display currently connected used information. -void udpServer::sendConnectionInfo(CLIENT* c) +void udpServer::sendConnectionInfo(CLIENT* c, quint8 guid[16]) { - qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; - conninfo_packet p; - memset(p.packet, 0x0, sizeof(p)); - p.len = sizeof(p); - p.type = 0x00; - p.seq = c->txSeq; - p.sentid = c->myId; - p.rcvdid = c->remoteId; - //p.innerseq = c->authInnerSeq; // Innerseq not used in user packet - p.tokrequest = c->tokenRx; - p.token = c->tokenTx; - p.code = 0x0380; - p.commoncap = c->commonCap; - memcpy(p.macaddress, c->macaddress, 6); - - // 0x1a-0x1f is authid (random number? - // memcpy(p + 0x40, QByteArrayLiteral("IC-7851").constData(), 7); - - memcpy(p.packet + 0x40, rigCaps.modelName.toLocal8Bit(), rigCaps.modelName.length()); - - // This is the current streaming client (should we support multiple clients?) - if (c->isStreaming) { - p.busy = 0x01; - memcpy(p.computer, c->clientName.constData(), c->clientName.length()); - p.ipaddress = qToBigEndian(c->ipAddress.toIPv4Address()); - memcpy(p.macaddress, c->macaddress, 6); - } - - - SEQBUFENTRY s; - s.seqNum = p.seq; - s.timeSent = QTime::currentTime(); - s.retransmitCount = 0; - 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) + for (RIGCONFIG& radio : config.rigs) { + if (!memcmp(guid, radio.guid, sizeof(guid))) { - c->txSeqBuf.remove(c->txSeqBuf.firstKey()); + qInfo(logUdpServer()) << c->ipAddress.toString() << "(" << c->type << "): Sending ConnectionInfo :" << c->txSeq; + conninfo_packet p; + memset(p.packet, 0x0, sizeof(p)); + p.len = sizeof(p); + p.type = 0x00; + p.seq = c->txSeq; + p.sentid = c->myId; + p.rcvdid = c->remoteId; + //p.innerseq = c->authInnerSeq; // Innerseq not used in user packet + 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)); + + if (radio.rigAvailable) { + if (c->isStreaming) { + p.busy = 0x01; + } + else { + p.busy = 0x00; + } + } + else + { + p.busy = 0x02; + } + + // This is the current streaming client (should we support multiple clients?) + if (c->isStreaming) { + memcpy(p.computer, c->clientName.constData(), c->clientName.length()); + p.ipaddress = qToBigEndian(c->ipAddress.toIPv4Address()); + + } + + + SEQBUFENTRY s; + s.seqNum = p.seq; + s.timeSent = QTime::currentTime(); + s.retransmitCount = 0; + 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(p.seq, s); + c->txSeq++; + c->txMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock txMutex()"; + } + + + if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); + udpMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock udpMutex()"; + } + } - c->txSeqBuf.insert(p.seq, s); - c->txSeq++; - c->txMutex.unlock(); } - else { - qInfo(logUdpServer()) << "Unable to lock txMutex()"; - } - - - if (udpMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - c->socket->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), c->ipAddress, c->port); - udpMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock udpMutex()"; - } - - if (c->idleTimer != Q_NULLPTR) c->idleTimer->start(100); @@ -1395,84 +1466,108 @@ void udpServer::sendStatus(CLIENT* c) void udpServer::dataForServer(QByteArray d) { - //qInfo(logUdpServer()) << "Server got:" << d; - foreach(CLIENT * client, civClients) + rigCommander* sender = qobject_cast(QObject::sender()); + + if (sender == Q_NULLPTR) { - int lastFE = d.lastIndexOf((quint8)0xfe); - if (client != Q_NULLPTR && 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) - { - 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()"; - } - - } else { - qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE+1] << ":" << hex << (quint8)d[lastFE+2]; - } + return; } + for (CLIENT* client : civClients) + { + if (client == Q_NULLPTR || !client->connected) + { + continue; + } + for (RIGCONFIG& radio : config.rigs) + { + + if (memcmp(radio.guid, client->guid, sizeof(radio.guid))) + { + 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) + { + 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()"; + } + } + else { + qInfo(logUdpServer()) << "Got data for different ID" << hex << (quint8)d[lastFE + 1] << ":" << hex << (quint8)d[lastFE + 2]; + } + } + } return; } void udpServer::sendRxAudio() { QByteArray audio; - if (rxaudio) { - if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) - { - audio.clear(); - rxaudio->getNextAudioChunk(audio); - int len = 0; - while (len < audio.length()) { - audioPacket partial; - partial.data = audio.mid(len, 1364); - receiveAudioData(partial); - len = len + partial.data.length(); + for (RIGCONFIG &rig : config.rigs) { + + if (rig.rxaudio != Q_NULLPTR) { + if (audioMutex.try_lock_for(std::chrono::milliseconds(LOCK_PERIOD))) + { + audio.clear(); + rig.rxaudio->getNextAudioChunk(audio); + int len = 0; + while (len < audio.length()) { + audioPacket partial; + partial.data = audio.mid(len, 1364); + receiveAudioData(partial); + len = len + partial.data.length(); + } + // Now we have the next audio chunk, we can release the mutex. + audioMutex.unlock(); + } + else { + qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; } - // Now we have the next audio chunk, we can release the mutex. - audioMutex.unlock(); - } - else { - qInfo(logUdpServer()) << "Unable to lock mutex for rxaudio"; } } } @@ -1721,6 +1816,10 @@ void udpServer::sendRetransmitRequest(CLIENT* c) void udpServer::deleteConnection(QList* l, CLIENT* c) { + quint8 guid[16]; + memcpy(guid, c->guid, sizeof(guid)); + int len = l->length(); + qInfo(logUdpServer()) << "Deleting" << c->type << "connection to: " << c->ipAddress.toString() << ":" << QString::number(c->port); if (c->idleTimer != Q_NULLPTR) { c->idleTimer->stop(); @@ -1774,6 +1873,7 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) if (client != Q_NULLPTR && client == c) { qInfo(logUdpServer()) << "Found" << client->type << "connection to: " << client->ipAddress.toString() << ":" << QString::number(client->port); it = l->erase(it); + len--; } else { ++it; @@ -1788,27 +1888,30 @@ void udpServer::deleteConnection(QList* l, CLIENT* c) qInfo(logUdpServer()) << "Unable to lock connMutex()"; } - if (l->length() == 0) { + if (len == 0) { + 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 (rxAudioTimer != Q_NULLPTR) { - rxAudioTimer->stop(); - delete rxAudioTimer; - rxAudioTimer = 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 (rxAudioThread != Q_NULLPTR) { - rxAudioThread->quit(); - rxAudioThread->wait(); - rxaudio = Q_NULLPTR; - rxAudioThread = Q_NULLPTR; - } - - if (txAudioThread != Q_NULLPTR) { - txAudioThread->quit(); - txAudioThread->wait(); - txaudio = Q_NULLPTR; - txAudioThread = Q_NULLPTR; - } - } } diff --git a/udpserver.h b/udpserver.h index 4d5730b..2269732 100644 --- a/udpserver.h +++ b/udpserver.h @@ -23,6 +23,7 @@ #include "rigidentities.h" #include "udphandler.h" #include "audiohandler.h" +#include "rigcommander.h" extern void passcode(QString in,QByteArray& out); extern QByteArray parseNullTerminatedString(QByteArray c, int s); @@ -41,6 +42,32 @@ struct SERVERUSER { quint8 userType; }; +struct RIGCONFIG { + QString serialPort; + quint32 baudRate; + unsigned char civAddr; + bool civIsRadioModel; + bool forceRTSasPTT; + bool hasWiFi = false; + bool hasEthernet=false; + audioSetup rxAudioSetup; + audioSetup txAudioSetup; + QString modelName; + QString rigName; + quint8 guid[16]; + bool rigAvailable=false; + rigCapabilities rigCaps; + rigCommander* rig = Q_NULLPTR; + QThread* rigThread = Q_NULLPTR; + audioHandler* rxaudio = Q_NULLPTR; + QThread* rxAudioThread = Q_NULLPTR; + audioHandler* txaudio = Q_NULLPTR; + QThread* txAudioThread = Q_NULLPTR; + QTimer* rxAudioTimer = Q_NULLPTR; + +}; + + struct SERVERCONFIG { bool enabled; bool lan; @@ -51,8 +78,8 @@ struct SERVERCONFIG { int audioInput; quint8 resampleQuality; quint32 baudRate; - QList users; + QList rigs; }; @@ -61,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: @@ -140,6 +167,7 @@ private: CLIENT* controlClient = Q_NULLPTR; CLIENT* civClient = Q_NULLPTR; CLIENT* audioClient = Q_NULLPTR; + quint8 guid[16]; }; void controlReceived(); @@ -151,7 +179,7 @@ private: void sendControl(CLIENT* c, quint8 type, quint16 seq); void sendLoginResponse(CLIENT* c, bool allowed); void sendCapabilities(CLIENT* c); - void sendConnectionInfo(CLIENT* c); + void sendConnectionInfo(CLIENT* c,quint8 guid[16]); void sendTokenResponse(CLIENT* c,quint8 type); void sendStatus(CLIENT* c); void sendRetransmitRequest(CLIENT* c); @@ -171,8 +199,6 @@ private: quint32 civId = 0; quint32 audioId = 0; - quint8 rigciv = 0xa2; - QMutex udpMutex; // Used for critical operations. QMutex connMutex; QMutex audioMutex; @@ -180,19 +206,13 @@ private: QList controlClients = QList(); QList civClients = QList(); QList audioClients = QList(); + QTime timeStarted; - rigCapabilities rigCaps; - audioHandler* rxaudio = Q_NULLPTR; - QThread* rxAudioThread = Q_NULLPTR; - - audioHandler* txaudio = Q_NULLPTR; - QThread* txAudioThread = Q_NULLPTR; audioSetup outAudio; audioSetup inAudio; - QTimer* rxAudioTimer=Q_NULLPTR; quint16 rxSampleRate = 0; quint16 txSampleRate = 0; quint8 rxCodec = 0;