From 3e004a257b122cde32b92c102068025733fd6186 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 16 Oct 2020 19:18:54 +0200 Subject: [PATCH 01/27] AFC plguin: REST API: sources --- swagger/sdrangel/api/swagger/include/AFC.yaml | 50 +++++++++++++++++++ .../api/swagger/include/FeatureActions.yaml | 2 + .../api/swagger/include/FeatureReport.yaml | 2 + .../api/swagger/include/FeatureSettings.yaml | 2 + 4 files changed, 56 insertions(+) create mode 100644 swagger/sdrangel/api/swagger/include/AFC.yaml diff --git a/swagger/sdrangel/api/swagger/include/AFC.yaml b/swagger/sdrangel/api/swagger/include/AFC.yaml new file mode 100644 index 000000000..4397ab890 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/AFC.yaml @@ -0,0 +1,50 @@ +AFCSettings: + description: "AFC settings" + properties: + title: + type: string + rgbColor: + type: integer + rxDeviceSetIndex: + description: index of the Rx device set to connect the Rx side to + type: integer + txDeviceSetIndex: + description: index of the Tx device set to connect the Tx side to + type: integer + rx2TxDelayMs: + description: Delay in milliseconds from Rx off to Tx on + type: integer + tx2RxDelayMs: + description: Delay in milliseconds from Tx off to Rx on + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + +AFCReport: + description: "AFC report" + properties: + ptt: + type: integer + description: > + AFC status + * 0 - released + * 1 - engaged + +AFCActions: + description: "AFC actions" + properties: + ptt: + type: integer + description: > + AFC action + * 0 - release + * 1 - engage diff --git a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml index 148f90b6d..5739ae33e 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureActions.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureActions.yaml @@ -13,5 +13,7 @@ FeatureActions: originatorFeatureIndex: description: Optional for reverse API. This is the feature index from where the message comes from. type: integer + AFCActions: + $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCActions" SimplePTTActions: $ref: "http://swgserver:8081/api/swagger/include/SimplePTT.yaml#/SimplePTTActions" diff --git a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml index 4cfc15ac5..de58bdda8 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureReport.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureReport.yaml @@ -7,5 +7,7 @@ FeatureReport: featureType: description: Feature type code type: string + AFCReport: + $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCReport" SimplePTTReport: $ref: "http://swgserver:8081/api/swagger/include/SimplePTT.yaml#/SimplePTTReport" diff --git a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml index d673f7e0d..c4bb5b710 100644 --- a/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/FeatureSettings.yaml @@ -13,6 +13,8 @@ FeatureSettings: originatorFeatureIndex: description: Optional for reverse API. This is the feature index from where the message comes from. type: integer + AFCSettings: + $ref: "http://swgserver:8081/api/swagger/include/AFC.yaml#/AFCSettings" SimplePTTSettings: $ref: "http://swgserver:8081/api/swagger/include/SimplePTT.yaml#/SimplePTTSettings" RigCtlServerSettings: From 0cd512ce4a79f03da62970b625fe1d882d218e58 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 16 Oct 2020 19:20:55 +0200 Subject: [PATCH 02/27] AFC plugin: brute force copy from Simple PTT plugin --- plugins/feature/CMakeLists.txt | 1 + plugins/feature/afc/CMakeLists.txt | 58 +++ plugins/feature/afc/afc.cpp | 427 +++++++++++++++++++++++ plugins/feature/afc/afc.h | 168 +++++++++ plugins/feature/afc/afcgui.cpp | 397 +++++++++++++++++++++ plugins/feature/afc/afcgui.h | 88 +++++ plugins/feature/afc/afcgui.ui | 324 +++++++++++++++++ plugins/feature/afc/afcplugin.cpp | 80 +++++ plugins/feature/afc/afcplugin.h | 48 +++ plugins/feature/afc/afcreport.cpp | 26 ++ plugins/feature/afc/afcreport.h | 55 +++ plugins/feature/afc/afcsettings.cpp | 109 ++++++ plugins/feature/afc/afcsettings.h | 46 +++ plugins/feature/afc/afcwebapiadapter.cpp | 50 +++ plugins/feature/afc/afcwebapiadapter.h | 49 +++ plugins/feature/afc/afcworker.cpp | 210 +++++++++++ plugins/feature/afc/afcworker.h | 106 ++++++ plugins/feature/afc/readme.md | 45 +++ 18 files changed, 2287 insertions(+) create mode 100644 plugins/feature/afc/CMakeLists.txt create mode 100644 plugins/feature/afc/afc.cpp create mode 100644 plugins/feature/afc/afc.h create mode 100644 plugins/feature/afc/afcgui.cpp create mode 100644 plugins/feature/afc/afcgui.h create mode 100644 plugins/feature/afc/afcgui.ui create mode 100644 plugins/feature/afc/afcplugin.cpp create mode 100644 plugins/feature/afc/afcplugin.h create mode 100644 plugins/feature/afc/afcreport.cpp create mode 100644 plugins/feature/afc/afcreport.h create mode 100644 plugins/feature/afc/afcsettings.cpp create mode 100644 plugins/feature/afc/afcsettings.h create mode 100644 plugins/feature/afc/afcwebapiadapter.cpp create mode 100644 plugins/feature/afc/afcwebapiadapter.h create mode 100644 plugins/feature/afc/afcworker.cpp create mode 100644 plugins/feature/afc/afcworker.h create mode 100644 plugins/feature/afc/readme.md diff --git a/plugins/feature/CMakeLists.txt b/plugins/feature/CMakeLists.txt index 58a75ed42..ebb46fb9c 100644 --- a/plugins/feature/CMakeLists.txt +++ b/plugins/feature/CMakeLists.txt @@ -1,4 +1,5 @@ project(feature) +add_subdirectory(afc) add_subdirectory(rigctlserver) add_subdirectory(simpleptt) diff --git a/plugins/feature/afc/CMakeLists.txt b/plugins/feature/afc/CMakeLists.txt new file mode 100644 index 000000000..2f240e401 --- /dev/null +++ b/plugins/feature/afc/CMakeLists.txt @@ -0,0 +1,58 @@ +project(afc) + +set(afc_SOURCES + afc.cpp + afcsettings.cpp + afcplugin.cpp + afcworker.cpp + afcreport.cpp + afcwebapiadapter.cpp +) + +set(afc_HEADERS + afc.h + afcsettings.h + afcplugin.h + afcworker.h + afcreport.h + afcwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(afc_SOURCES + ${afc_SOURCES} + afcgui.cpp + afcgui.ui + ) + set(afc_HEADERS + ${afc_HEADERS} + afcgui.h + ) + + set(TARGET_NAME featureafc) + set(TARGET_LIB "Qt5::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME featureafcsrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${afc_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt5::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/feature/afc/afc.cpp b/plugins/feature/afc/afc.cpp new file mode 100644 index 000000000..5f33e1a76 --- /dev/null +++ b/plugins/feature/afc/afc.cpp @@ -0,0 +1,427 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "SWGFeatureSettings.h" +#include "SWGFeatureReport.h" +#include "SWGFeatureActions.h" +#include "SWGAFCReport.h" +#include "SWGDeviceState.h" + +#include "dsp/dspengine.h" + +#include "afcworker.h" +#include "afc.h" + +MESSAGE_CLASS_DEFINITION(AFC::MsgConfigureAFC, Message) +MESSAGE_CLASS_DEFINITION(AFC::MsgPTT, Message) +MESSAGE_CLASS_DEFINITION(AFC::MsgStartStop, Message) + +const QString AFC::m_featureIdURI = "sdrangel.feature.afc"; +const QString AFC::m_featureId = "AFC"; + +AFC::AFC(WebAPIAdapterInterface *webAPIAdapterInterface) : + Feature(m_featureIdURI, webAPIAdapterInterface), + m_ptt(false) +{ + setObjectName(m_featureId); + m_worker = new AFCWorker(webAPIAdapterInterface); + m_state = StIdle; + m_errorMessage = "AFC error"; +} + +AFC::~AFC() +{ + if (m_worker->isRunning()) { + stop(); + } + + delete m_worker; +} + +void AFC::start() +{ + qDebug("AFC::start"); + + m_worker->reset(); + m_worker->setMessageQueueToGUI(getMessageQueueToGUI()); + bool ok = m_worker->startWork(); + m_state = ok ? StRunning : StError; + m_thread.start(); + + AFCWorker::MsgConfigureAFCWorker *msg = AFCWorker::MsgConfigureAFCWorker::create(m_settings, true); + m_worker->getInputMessageQueue()->push(msg); +} + +void AFC::stop() +{ + qDebug("AFC::stop"); + m_worker->stopWork(); + m_state = StIdle; + m_thread.quit(); + m_thread.wait(); +} + +bool AFC::handleMessage(const Message& cmd) +{ + if (MsgConfigureAFC::match(cmd)) + { + MsgConfigureAFC& cfg = (MsgConfigureAFC&) cmd; + qDebug() << "AFC::handleMessage: MsgConfigureAFC"; + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgPTT::match(cmd)) + { + MsgPTT& cfg = (MsgPTT&) cmd; + m_ptt = cfg.getTx(); + qDebug() << "AFC::handleMessage: MsgPTT: tx:" << m_ptt; + + AFCWorker::MsgPTT *msg = AFCWorker::MsgPTT::create(m_ptt); + m_worker->getInputMessageQueue()->push(msg); + + return true; + } + else if (MsgStartStop::match(cmd)) + { + MsgStartStop& cfg = (MsgStartStop&) cmd; + qDebug() << "AFC::handleMessage: MsgStartStop: start:" << cfg.getStartStop(); + + if (cfg.getStartStop()) { + start(); + } else { + stop(); + } + + return true; + } + else + { + return false; + } +} + +QByteArray AFC::serialize() const +{ + return m_settings.serialize(); +} + +bool AFC::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + MsgConfigureAFC *msg = MsgConfigureAFC::create(m_settings, true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureAFC *msg = MsgConfigureAFC::create(m_settings, true); + m_inputMessageQueue.push(msg); + return false; + } +} + +void AFC::applySettings(const AFCSettings& settings, bool force) +{ + qDebug() << "AFC::applySettings:" + << " m_title: " << settings.m_title + << " m_rgbColor: " << settings.m_rgbColor + << " m_rxDeviceSetIndex: " << settings.m_rxDeviceSetIndex + << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex + << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs + << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs + << " force: " << force; + + QList reverseAPIKeys; + + if ((m_settings.m_title != settings.m_title) || force) { + reverseAPIKeys.append("title"); + } + if ((m_settings.m_rgbColor != settings.m_rgbColor) || force) { + reverseAPIKeys.append("rgbColor"); + } + if ((m_settings.m_rxDeviceSetIndex != settings.m_rxDeviceSetIndex) || force) { + reverseAPIKeys.append("rxDeviceSetIndex"); + } + if ((m_settings.m_txDeviceSetIndex != settings.m_txDeviceSetIndex) || force) { + reverseAPIKeys.append("txDeviceSetIndex"); + } + if ((m_settings.m_rx2TxDelayMs != settings.m_rx2TxDelayMs) || force) { + reverseAPIKeys.append("rx2TxDelayMs"); + } + if ((m_settings.m_tx2RxDelayMs != settings.m_tx2RxDelayMs) || force) { + reverseAPIKeys.append("tx2RxDelayMs"); + } + + AFCWorker::MsgConfigureAFCWorker *msg = AFCWorker::MsgConfigureAFCWorker::create( + settings, force + ); + m_worker->getInputMessageQueue()->push(msg); + + if (settings.m_useReverseAPI) + { + bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || + (m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) || + (m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) || + (m_settings.m_reverseAPIFeatureSetIndex != settings.m_reverseAPIFeatureSetIndex) || + (m_settings.m_reverseAPIFeatureIndex != settings.m_reverseAPIFeatureIndex); + webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force); + } + + m_settings = settings; +} + +int AFC::webapiRun(bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage) +{ + getFeatureStateStr(*response.getState()); + MsgStartStop *msg = MsgStartStop::create(run); + getInputMessageQueue()->push(msg); + return 202; +} + +int AFC::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAfcSettings(new SWGSDRangel::SWGAFCSettings()); + response.getAfcSettings()->init(); + webapiFormatFeatureSettings(response, m_settings); + return 200; +} + +int AFC::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + AFCSettings settings = m_settings; + webapiUpdateFeatureSettings(settings, featureSettingsKeys, response); + + MsgConfigureAFC *msg = MsgConfigureAFC::create(settings, force); + m_inputMessageQueue.push(msg); + + qDebug("AFC::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureAFC *msgToGUI = MsgConfigureAFC::create(settings, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatFeatureSettings(response, settings); + + return 200; +} + +int AFC::webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAfcReport(new SWGSDRangel::SWGAFCReport()); + response.getAfcReport()->init(); + webapiFormatFeatureReport(response); + return 200; +} + +int AFC::webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage) +{ + SWGSDRangel::SWGAFCActions *swgAFCActions = query.getAfcActions(); + + if (swgAFCActions) + { + if (featureActionsKeys.contains("ptt")) + { + bool ptt = swgAFCActions->getPtt() != 0; + + MsgPTT *msg = MsgPTT::create(ptt); + getInputMessageQueue()->push(msg); + + if (getMessageQueueToGUI()) + { + MsgPTT *msgToGUI = MsgPTT::create(ptt); + getMessageQueueToGUI()->push(msgToGUI); + } + } + + return 202; + } + else + { + errorMessage = "Missing AFCActions in query"; + return 400; + } +} + +void AFC::webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const AFCSettings& settings) +{ + if (response.getAfcSettings()->getTitle()) { + *response.getAfcSettings()->getTitle() = settings.m_title; + } else { + response.getAfcSettings()->setTitle(new QString(settings.m_title)); + } + + response.getAfcSettings()->setRgbColor(settings.m_rgbColor); + response.getAfcSettings()->setRxDeviceSetIndex(settings.m_rxDeviceSetIndex); + response.getAfcSettings()->setTxDeviceSetIndex(settings.m_txDeviceSetIndex); + response.getAfcSettings()->setRx2TxDelayMs(settings.m_rx2TxDelayMs); + response.getAfcSettings()->setTx2RxDelayMs(settings.m_tx2RxDelayMs); + + response.getAfcSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getAfcSettings()->getReverseApiAddress()) { + *response.getAfcSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getAfcSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getAfcSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getAfcSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIFeatureSetIndex); + response.getAfcSettings()->setReverseApiChannelIndex(settings.m_reverseAPIFeatureIndex); +} + +void AFC::webapiUpdateFeatureSettings( + AFCSettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response) +{ + if (featureSettingsKeys.contains("title")) { + settings.m_title = *response.getAfcSettings()->getTitle(); + } + if (featureSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getAfcSettings()->getRgbColor(); + } + if (featureSettingsKeys.contains("rxDeviceSetIndex")) { + settings.m_rxDeviceSetIndex = response.getAfcSettings()->getRxDeviceSetIndex(); + } + if (featureSettingsKeys.contains("txDeviceSetIndex")) { + settings.m_txDeviceSetIndex = response.getAfcSettings()->getTxDeviceSetIndex(); + } + if (featureSettingsKeys.contains("rx2TxDelayMs")) { + settings.m_rx2TxDelayMs = response.getAfcSettings()->getRx2TxDelayMs(); + } + if (featureSettingsKeys.contains("tx2RxDelayMs")) { + settings.m_tx2RxDelayMs = response.getAfcSettings()->getTx2RxDelayMs(); + } + if (featureSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getAfcSettings()->getUseReverseApi() != 0; + } + if (featureSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getAfcSettings()->getReverseApiAddress(); + } + if (featureSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getAfcSettings()->getReverseApiPort(); + } + if (featureSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIFeatureSetIndex = response.getAfcSettings()->getReverseApiDeviceIndex(); + } + if (featureSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIFeatureIndex = response.getAfcSettings()->getReverseApiChannelIndex(); + } +} + +void AFC::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response) +{ + response.getAfcReport()->setPtt(m_ptt ? 1 : 0); +} + +void AFC::webapiReverseSendSettings(QList& channelSettingsKeys, const AFCSettings& settings, bool force) +{ + SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings(); + // swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet()); + // swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex()); + swgFeatureSettings->setFeatureType(new QString("AFC")); + swgFeatureSettings->setAfcSettings(new SWGSDRangel::SWGAFCSettings()); + SWGSDRangel::SWGAFCSettings *swgAFCSettings = swgFeatureSettings->getAfcSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("title") || force) { + swgAFCSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgAFCSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("rxDeviceSetIndex") || force) { + swgAFCSettings->setRxDeviceSetIndex(settings.m_rxDeviceSetIndex); + } + if (channelSettingsKeys.contains("txDeviceSetIndex") || force) { + swgAFCSettings->setTxDeviceSetIndex(settings.m_txDeviceSetIndex); + } + if (channelSettingsKeys.contains("rx2TxDelayMs") || force) { + swgAFCSettings->setRx2TxDelayMs(settings.m_rx2TxDelayMs); + } + if (channelSettingsKeys.contains("tx2RxDelayMs") || force) { + swgAFCSettings->setTx2RxDelayMs(settings.m_tx2RxDelayMs); + } + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIFeatureSetIndex) + .arg(settings.m_reverseAPIFeatureIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgFeatureSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgFeatureSettings; +} + +void AFC::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "AFC::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("AFC::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} diff --git a/plugins/feature/afc/afc.h b/plugins/feature/afc/afc.h new file mode 100644 index 000000000..9cdb1afb4 --- /dev/null +++ b/plugins/feature/afc/afc.h @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFC_H_ +#define INCLUDE_FEATURE_AFC_H_ + +#include +#include + +#include "feature/feature.h" +#include "util/message.h" + +#include "afcsettings.h" + +class WebAPIAdapterInterface; +class AFCWorker; +class QNetworkAccessManager; +class QNetworkReply; + +namespace SWGSDRangel { + class SWGDeviceState; +} + +class AFC : public Feature +{ + Q_OBJECT +public: + class MsgConfigureAFC : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const AFCSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureAFC* create(const AFCSettings& settings, bool force) { + return new MsgConfigureAFC(settings, force); + } + + private: + AFCSettings m_settings; + bool m_force; + + MsgConfigureAFC(const AFCSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgPTT : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getTx() const { return m_tx; } + + static MsgPTT* create(bool tx) { + return new MsgPTT(tx); + } + + private: + bool m_tx; + + MsgPTT(bool tx) : + Message(), + m_tx(tx) + { } + }; + + class MsgStartStop : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getStartStop() const { return m_startStop; } + + static MsgStartStop* create(bool startStop) { + return new MsgStartStop(startStop); + } + + protected: + bool m_startStop; + + MsgStartStop(bool startStop) : + Message(), + m_startStop(startStop) + { } + }; + + AFC(WebAPIAdapterInterface *webAPIAdapterInterface); + virtual ~AFC(); + virtual void destroy() { delete this; } + virtual bool handleMessage(const Message& cmd); + + virtual const QString& getURI() const { return m_featureIdURI; } + virtual void getIdentifier(QString& id) const { id = m_featureId; } + virtual void getTitle(QString& title) const { title = m_settings.m_title; } + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int webapiRun(bool run, + SWGSDRangel::SWGDeviceState& response, + QString& errorMessage); + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGFeatureReport& response, + QString& errorMessage); + + virtual int webapiActionsPost( + const QStringList& featureActionsKeys, + SWGSDRangel::SWGFeatureActions& query, + QString& errorMessage); + + static void webapiFormatFeatureSettings( + SWGSDRangel::SWGFeatureSettings& response, + const AFCSettings& settings); + + static void webapiUpdateFeatureSettings( + AFCSettings& settings, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response); + + static const QString m_featureIdURI; + static const QString m_featureId; + +private: + QThread m_thread; + AFCWorker *m_worker; + AFCSettings m_settings; + bool m_ptt; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + void start(); + void stop(); + void applySettings(const AFCSettings& settings, bool force = false); + void webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& response); + void webapiReverseSendSettings(QList& featureSettingsKeys, const AFCSettings& settings, bool force); + +private slots: + void networkManagerFinished(QNetworkReply *reply); +}; + +#endif // INCLUDE_FEATURE_AFC_H_ diff --git a/plugins/feature/afc/afcgui.cpp b/plugins/feature/afc/afcgui.cpp new file mode 100644 index 000000000..9d7ae8353 --- /dev/null +++ b/plugins/feature/afc/afcgui.cpp @@ -0,0 +1,397 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "feature/featureuiset.h" +#include "gui/basicfeaturesettingsdialog.h" +#include "device/deviceset.h" +#include "maincore.h" + +#include "ui_afcgui.h" +#include "afcreport.h" +#include "afc.h" +#include "afcgui.h" + +AFCGUI* AFCGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature) +{ + AFCGUI* gui = new AFCGUI(pluginAPI, featureUISet, feature); + return gui; +} + +void AFCGUI::destroy() +{ + delete this; +} + +void AFCGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applySettings(true); +} + +QByteArray AFCGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool AFCGUI::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + displaySettings(); + applySettings(true); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool AFCGUI::handleMessage(const Message& message) +{ + if (AFC::MsgConfigureAFC::match(message)) + { + qDebug("AFCGUI::handleMessage: AFC::MsgConfigureAFC"); + const AFC::MsgConfigureAFC& cfg = (AFC::MsgConfigureAFC&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + displaySettings(); + blockApplySettings(false); + + return true; + } + else if (AFCReport::MsgRadioState::match(message)) + { + qDebug("AFCGUI::handleMessage: AFCReport::MsgRadioState"); + const AFCReport::MsgRadioState& cfg = (AFCReport::MsgRadioState&) message; + AFCReport::RadioState state = cfg.getState(); + ui->statusIndicator->setStyleSheet("QLabel { background-color: " + + m_statusColors[(int) state] + "; border-radius: 12px; }"); + ui->statusIndicator->setToolTip(m_statusTooltips[(int) state]); + + return true; + } + else if (AFC::MsgPTT::match(message)) + { + qDebug("AFCGUI::handleMessage: AFC::MsgPTT"); + const AFC::MsgPTT& cfg = (AFC::MsgPTT&) message; + bool ptt = cfg.getTx(); + blockApplySettings(true); + ui->ptt->setChecked(ptt); + blockApplySettings(false); + + return true; + } + + return false; +} + +void AFCGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop())) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +void AFCGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; +} + +AFCGUI::AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) : + FeatureGUI(parent), + ui(new Ui::AFCGUI), + m_pluginAPI(pluginAPI), + m_featureUISet(featureUISet), + m_doApplySettings(true), + m_lastFeatureState(0) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + setChannelWidget(false); + connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + m_simplePTT = reinterpret_cast(feature); + m_simplePTT->setMessageQueueToGUI(&m_inputMessageQueue); + + m_featureUISet->addRollupWidget(this); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); + m_statusTimer.start(1000); + + m_statusTooltips.push_back("Idle"); // 0 - all off + m_statusTooltips.push_back("Rx on"); // 1 - Rx on + m_statusTooltips.push_back("Tx on"); // 2 - Tx on + + m_statusColors.push_back("gray"); // All off + m_statusColors.push_back("rgb(85, 232, 85)"); // Rx on (green) + m_statusColors.push_back("rgb(232, 85, 85)"); // Tx on (red) + + updateDeviceSetLists(); + displaySettings(); + applySettings(true); +} + +AFCGUI::~AFCGUI() +{ + delete ui; +} + +void AFCGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void AFCGUI::displaySettings() +{ + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_settings.m_title); + blockApplySettings(true); + ui->rxtxDelay->setValue(m_settings.m_rx2TxDelayMs); + ui->txrxDelay->setValue(m_settings.m_tx2RxDelayMs); + blockApplySettings(false); +} + +void AFCGUI::updateDeviceSetLists() +{ + MainCore *mainCore = MainCore::instance(); + std::vector& deviceSets = mainCore->getDeviceSets(); + std::vector::const_iterator it = deviceSets.begin(); + + ui->rxDevice->blockSignals(true); + ui->txDevice->blockSignals(true); + + ui->rxDevice->clear(); + ui->txDevice->clear(); + unsigned int deviceIndex = 0; + unsigned int rxIndex = 0; + unsigned int txIndex = 0; + + for (; it != deviceSets.end(); ++it, deviceIndex++) + { + DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; + DSPDeviceSinkEngine *deviceSinkEngine = (*it)->m_deviceSinkEngine; + + if (deviceSourceEngine) + { + ui->rxDevice->addItem(QString("R%1").arg(deviceIndex), deviceIndex); + rxIndex++; + } + else if (deviceSinkEngine) + { + ui->txDevice->addItem(QString("T%1").arg(deviceIndex), deviceIndex); + txIndex++; + } + } + + int rxDeviceIndex; + int txDeviceIndex; + + if (rxIndex > 0) + { + if (m_settings.m_rxDeviceSetIndex < 0) { + ui->rxDevice->setCurrentIndex(0); + } else { + ui->rxDevice->setCurrentIndex(m_settings.m_rxDeviceSetIndex); + } + + rxDeviceIndex = ui->rxDevice->currentData().toInt(); + } + else + { + rxDeviceIndex = -1; + } + + + if (txIndex > 0) + { + if (m_settings.m_txDeviceSetIndex < 0) { + ui->txDevice->setCurrentIndex(0); + } else { + ui->txDevice->setCurrentIndex(m_settings.m_txDeviceSetIndex); + } + + txDeviceIndex = ui->txDevice->currentData().toInt(); + } + else + { + txDeviceIndex = -1; + } + + if ((rxDeviceIndex != m_settings.m_rxDeviceSetIndex) || + (txDeviceIndex != m_settings.m_txDeviceSetIndex)) + { + qDebug("AFCGUI::updateDeviceSetLists: device index changed: %d:%d", rxDeviceIndex, txDeviceIndex); + m_settings.m_rxDeviceSetIndex = rxDeviceIndex; + m_settings.m_txDeviceSetIndex = txDeviceIndex; + applySettings(); + } + + ui->rxDevice->blockSignals(false); + ui->txDevice->blockSignals(false); +} + +void AFCGUI::leaveEvent(QEvent*) +{ +} + +void AFCGUI::enterEvent(QEvent*) +{ +} + +void AFCGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicFeatureSettingsDialog dialog(this); + dialog.setTitle(m_settings.m_title); + dialog.setColor(m_settings.m_rgbColor); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex); + dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex); + + dialog.move(p); + dialog.exec(); + + m_settings.m_rgbColor = dialog.getColor().rgb(); + m_settings.m_title = dialog.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex(); + m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex(); + + setWindowTitle(m_settings.m_title); + setTitleColor(m_settings.m_rgbColor); + + applySettings(); + } + + resetContextMenuType(); +} + +void AFCGUI::on_startStop_toggled(bool checked) +{ + if (m_doApplySettings) + { + AFC::MsgStartStop *message = AFC::MsgStartStop::create(checked); + m_simplePTT->getInputMessageQueue()->push(message); + } +} + +void AFCGUI::on_devicesRefresh_clicked() +{ + updateDeviceSetLists(); + displaySettings(); +} + +void AFCGUI::on_rxDevice_currentIndexChanged(int index) +{ + if (index >= 0) + { + m_settings.m_rxDeviceSetIndex = index; + applySettings(); + } +} + +void AFCGUI::on_txDevice_currentIndexChanged(int index) +{ + if (index >= 0) + { + m_settings.m_txDeviceSetIndex = index; + applySettings(); + } + +} + +void AFCGUI::on_rxtxDelay_valueChanged(int value) +{ + m_settings.m_rx2TxDelayMs = value; + applySettings(); +} + +void AFCGUI::on_txrxDelay_valueChanged(int value) +{ + m_settings.m_tx2RxDelayMs = value; + applySettings(); +} + +void AFCGUI::on_ptt_toggled(bool checked) +{ + applyPTT(checked); +} + +void AFCGUI::updateStatus() +{ + int state = m_simplePTT->getState(); + + if (m_lastFeatureState != state) + { + switch (state) + { + case Feature::StNotStarted: + ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + break; + case Feature::StIdle: + ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); + break; + case Feature::StRunning: + ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); + break; + case Feature::StError: + ui->startStop->setStyleSheet("QToolButton { background-color : red; }"); + QMessageBox::information(this, tr("Message"), m_simplePTT->getErrorMessage()); + break; + default: + break; + } + + m_lastFeatureState = state; + } +} + +void AFCGUI::applySettings(bool force) +{ + if (m_doApplySettings) + { + AFC::MsgConfigureAFC* message = AFC::MsgConfigureAFC::create( m_settings, force); + m_simplePTT->getInputMessageQueue()->push(message); + } +} + +void AFCGUI::applyPTT(bool tx) +{ + if (m_doApplySettings) + { + AFC::MsgPTT* message = AFC::MsgPTT::create(tx); + m_simplePTT->getInputMessageQueue()->push(message); + } +} diff --git a/plugins/feature/afc/afcgui.h b/plugins/feature/afc/afcgui.h new file mode 100644 index 000000000..0faa09d7d --- /dev/null +++ b/plugins/feature/afc/afcgui.h @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFCGUI_H_ +#define INCLUDE_FEATURE_AFCGUI_H_ + +#include + +#include "feature/featuregui.h" +#include "util/messagequeue.h" +#include "afcsettings.h" + +class PluginAPI; +class FeatureUISet; +class AFC; + +namespace Ui { + class AFCGUI; +} + +class AFCGUI : public FeatureGUI { + Q_OBJECT +public: + static AFCGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + +private: + Ui::AFCGUI* ui; + PluginAPI* m_pluginAPI; + FeatureUISet* m_featureUISet; + AFCSettings m_settings; + bool m_doApplySettings; + + AFC* m_simplePTT; + MessageQueue m_inputMessageQueue; + QTimer m_statusTimer; + int m_lastFeatureState; + std::vector m_statusColors; + std::vector m_statusTooltips; + + explicit AFCGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); + virtual ~AFCGUI(); + + void blockApplySettings(bool block); + void applySettings(bool force = false); + void applyPTT(bool tx); + void displaySettings(); + void updateDeviceSetLists(); + bool handleMessage(const Message& message); + + void leaveEvent(QEvent*); + void enterEvent(QEvent*); + +private slots: + void onMenuDialogCalled(const QPoint &p); + void onWidgetRolled(QWidget* widget, bool rollDown); + void handleInputMessages(); + void on_startStop_toggled(bool checked); + void on_devicesRefresh_clicked(); + void on_rxDevice_currentIndexChanged(int index); + void on_txDevice_currentIndexChanged(int index); + void on_rxtxDelay_valueChanged(int value); + void on_txrxDelay_valueChanged(int value); + void on_ptt_toggled(bool checked); + void updateStatus(); +}; + + +#endif // INCLUDE_FEATURE_AFCGUI_H_ diff --git a/plugins/feature/afc/afcgui.ui b/plugins/feature/afc/afcgui.ui new file mode 100644 index 000000000..d11eb6964 --- /dev/null +++ b/plugins/feature/afc/afcgui.ui @@ -0,0 +1,324 @@ + + + AFCGUI + + + + 0 + 0 + 320 + 181 + + + + + 0 + 0 + + + + + 320 + 100 + + + + + 320 + 16777215 + + + + + Liberation Sans + 9 + + + + AFC + + + + + 10 + 10 + 301 + 151 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + start/stop acquisition + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + 200 + 50 + + + + + 20 + 75 + true + + + + Push To Talk + + + PTT + + + + + + + + 0 + 0 + + + + + 24 + 24 + + + + Idle + + + QLabel { background-color: gray; border-radius: 12px; } + + + + + + + + + + + + + + + 24 + 16777215 + + + + Refresh indexes of available device sets + + + + + + + :/recycle.png:/recycle.png + + + + + + + Rx dev + + + + + + + + 55 + 0 + + + + + 50 + 16777215 + + + + Receiver deviceset index + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Tx dev + + + + + + + + 55 + 0 + + + + + 50 + 16777215 + + + + Transmitter deviceset index + + + + + + + + + + + Rx-Tx + + + + + + + Rx to Tx transition delay (ms) + + + 100 + + + 5000 + + + 100 + + + + + + + ms + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Tx-Rx + + + + + + + Tx to Rx transition delay (ms) + + + 100 + + + 5000 + + + 100 + + + + + + + ms + + + + + + + + + + + RollupWidget + QWidget +
gui/rollupwidget.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+
+ + + + +
diff --git a/plugins/feature/afc/afcplugin.cpp b/plugins/feature/afc/afcplugin.cpp new file mode 100644 index 000000000..89bb77f3c --- /dev/null +++ b/plugins/feature/afc/afcplugin.cpp @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "afcgui.h" +#endif +#include "afc.h" +#include "afcplugin.h" +#include "afcwebapiadapter.h" + +const PluginDescriptor AFCPlugin::m_pluginDescriptor = { + AFC::m_featureId, + QString("AFC"), + QString("4.21.0"), + QString("(c) Edouard Griffiths, F4EXB"), + QString("https://github.com/f4exb/sdrangel"), + true, + QString("https://github.com/f4exb/sdrangel") +}; + +AFCPlugin::AFCPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(nullptr) +{ +} + +const PluginDescriptor& AFCPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void AFCPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + // register AFC feature + m_pluginAPI->registerFeature(AFC::m_featureIdURI, AFC::m_featureId, this); +} + +#ifdef SERVER_MODE +FeatureGUI* AFCPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + (void) featureUISet; + (void) feature; + return nullptr; +} +#else +FeatureGUI* AFCPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const +{ + return AFCGUI::create(m_pluginAPI, featureUISet, feature); +} +#endif + +Feature* AFCPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const +{ + return new AFC(webAPIAdapterInterface); +} + +FeatureWebAPIAdapter* AFCPlugin::createFeatureWebAPIAdapter() const +{ + return new AFCWebAPIAdapter(); +} diff --git a/plugins/feature/afc/afcplugin.h b/plugins/feature/afc/afcplugin.h new file mode 100644 index 000000000..eea88635a --- /dev/null +++ b/plugins/feature/afc/afcplugin.h @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFCPLUGIN_H +#define INCLUDE_FEATURE_AFCPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class FeatureGUI; +class WebAPIAdapterInterface; + +class AFCPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.feature.afc") + +public: + explicit AFCPlugin(QObject* parent = nullptr); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const; + virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const; + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_FEATURE_AFCPLUGIN_H diff --git a/plugins/feature/afc/afcreport.cpp b/plugins/feature/afc/afcreport.cpp new file mode 100644 index 000000000..105de5d52 --- /dev/null +++ b/plugins/feature/afc/afcreport.cpp @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "afcreport.h" + +MESSAGE_CLASS_DEFINITION(AFCReport::MsgRadioState, Message) + +AFCReport::AFCReport() +{} + +AFCReport::~AFCReport() +{} diff --git a/plugins/feature/afc/afcreport.h b/plugins/feature/afc/afcreport.h new file mode 100644 index 000000000..97f94f99d --- /dev/null +++ b/plugins/feature/afc/afcreport.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFCREPORT_H_ +#define INCLUDE_FEATURE_AFCREPORT_H_ + +#include "util/message.h" + +class AFCReport +{ +public: + enum RadioState { + RadioIdle, + RadioRx, + RadioTx + }; + class MsgRadioState : public Message { + MESSAGE_CLASS_DECLARATION + + public: + RadioState getState() const { return m_state; } + + static MsgRadioState* create(RadioState state) + { + return new MsgRadioState(state); + } + + private: + RadioState m_state; + + MsgRadioState(RadioState state) : + Message(), + m_state(state) + { } + }; + + AFCReport(); + ~AFCReport(); +}; + +#endif // INCLUDE_FEATURE_AFCREPORT_H_ diff --git a/plugins/feature/afc/afcsettings.cpp b/plugins/feature/afc/afcsettings.cpp new file mode 100644 index 000000000..8ab695009 --- /dev/null +++ b/plugins/feature/afc/afcsettings.cpp @@ -0,0 +1,109 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" + +#include "afcsettings.h" + +AFCSettings::AFCSettings() +{ + resetToDefaults(); +} + +void AFCSettings::resetToDefaults() +{ + m_title = "AFC"; + m_rgbColor = QColor(255, 255, 0).rgb(); + m_rxDeviceSetIndex = -1; + m_txDeviceSetIndex = -1; + m_rx2TxDelayMs = 100; + m_tx2RxDelayMs = 100; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIFeatureSetIndex = 0; + m_reverseAPIFeatureIndex = 0; +} + +QByteArray AFCSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeString(1, m_title); + s.writeU32(2, m_rgbColor); + s.writeS32(3, m_rxDeviceSetIndex); + s.writeS32(4, m_txDeviceSetIndex); + s.writeU32(5, m_rx2TxDelayMs); + s.writeU32(6, m_tx2RxDelayMs); + s.writeBool(7, m_useReverseAPI); + s.writeString(8, m_reverseAPIAddress); + s.writeU32(9, m_reverseAPIPort); + s.writeU32(10, m_reverseAPIFeatureSetIndex); + s.writeU32(11, m_reverseAPIFeatureIndex); + + return s.final(); +} + +bool AFCSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + qint32 tmp; + uint32_t utmp; + QString strtmp; + + d.readString(1, &m_title, "AFC"); + d.readU32(2, &m_rgbColor, QColor(255, 255, 0).rgb()); + d.readS32(3, &m_rxDeviceSetIndex, -1); + d.readS32(4, &m_txDeviceSetIndex, -1); + d.readU32(5, &m_rx2TxDelayMs, 100); + d.readU32(6, &m_tx2RxDelayMs, 100); + d.readBool(7, &m_useReverseAPI, false); + d.readString(8, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(9, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(10, &utmp, 0); + m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp; + d.readU32(11, &utmp, 0); + m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/plugins/feature/afc/afcsettings.h b/plugins/feature/afc/afcsettings.h new file mode 100644 index 000000000..8dcf0c8b1 --- /dev/null +++ b/plugins/feature/afc/afcsettings.h @@ -0,0 +1,46 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFCSETTINGS_H_ +#define INCLUDE_FEATURE_AFCSETTINGS_H_ + +#include +#include + +class Serializable; + +struct AFCSettings +{ + QString m_title; + quint32 m_rgbColor; + int m_rxDeviceSetIndex; + int m_txDeviceSetIndex; + unsigned int m_rx2TxDelayMs; + unsigned int m_tx2RxDelayMs; + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIFeatureSetIndex; + uint16_t m_reverseAPIFeatureIndex; + + AFCSettings(); + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); +}; + +#endif // INCLUDE_FEATURE_AFCSETTINGS_H_ diff --git a/plugins/feature/afc/afcwebapiadapter.cpp b/plugins/feature/afc/afcwebapiadapter.cpp new file mode 100644 index 000000000..a34c36ed1 --- /dev/null +++ b/plugins/feature/afc/afcwebapiadapter.cpp @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGFeatureSettings.h" +#include "afc.h" +#include "afcwebapiadapter.h" + +AFCWebAPIAdapter::AFCWebAPIAdapter() +{} + +AFCWebAPIAdapter::~AFCWebAPIAdapter() +{} + +int AFCWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setAfcSettings(new SWGSDRangel::SWGAFCSettings()); + response.getAfcSettings()->init(); + AFC::webapiFormatFeatureSettings(response, m_settings); + + return 200; +} + +int AFCWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + AFC::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response); + + return 200; +} diff --git a/plugins/feature/afc/afcwebapiadapter.h b/plugins/feature/afc/afcwebapiadapter.h new file mode 100644 index 000000000..f53cc6c42 --- /dev/null +++ b/plugins/feature/afc/afcwebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_AFC_WEBAPIADAPTER_H +#define INCLUDE_AFC_WEBAPIADAPTER_H + +#include "feature/featurewebapiadapter.h" +#include "afcsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class AFCWebAPIAdapter : public FeatureWebAPIAdapter { +public: + AFCWebAPIAdapter(); + virtual ~AFCWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + +private: + AFCSettings m_settings; +}; + +#endif // INCLUDE_AFC_WEBAPIADAPTER_H diff --git a/plugins/feature/afc/afcworker.cpp b/plugins/feature/afc/afcworker.cpp new file mode 100644 index 000000000..1891b4a77 --- /dev/null +++ b/plugins/feature/afc/afcworker.cpp @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "SWGDeviceState.h" +#include "SWGSuccessResponse.h" +#include "SWGErrorResponse.h" + +#include "webapi/webapiadapterinterface.h" + +#include "afcreport.h" +#include "afcworker.h" + +MESSAGE_CLASS_DEFINITION(AFCWorker::MsgConfigureAFCWorker, Message) +MESSAGE_CLASS_DEFINITION(AFCWorker::MsgPTT, Message) + +AFCWorker::AFCWorker(WebAPIAdapterInterface *webAPIAdapterInterface) : + m_webAPIAdapterInterface(webAPIAdapterInterface), + m_msgQueueToGUI(nullptr), + m_running(false), + m_tx(false), + m_mutex(QMutex::Recursive) +{ + qDebug("AFCWorker::AFCWorker"); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware())); +} + +AFCWorker::~AFCWorker() +{ + m_inputMessageQueue.clear(); +} + +void AFCWorker::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_inputMessageQueue.clear(); +} + +bool AFCWorker::startWork() +{ + QMutexLocker mutexLocker(&m_mutex); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + m_running = true; + return m_running; +} + +void AFCWorker::stopWork() +{ + QMutexLocker mutexLocker(&m_mutex); + disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + m_running = false; +} + +void AFCWorker::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool AFCWorker::handleMessage(const Message& cmd) +{ + if (MsgConfigureAFCWorker::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureAFCWorker& cfg = (MsgConfigureAFCWorker&) cmd; + qDebug() << "AFCWorker::handleMessage: MsgConfigureAFCWorker"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgPTT::match(cmd)) + { + MsgPTT& cfg = (MsgPTT&) cmd; + qDebug() << "AFCWorker::handleMessage: MsgPTT"; + + sendPTT(cfg.getTx()); + + return true; + } + else + { + return false; + } +} + +void AFCWorker::applySettings(const AFCSettings& settings, bool force) +{ + qDebug() << "AFCWorker::applySettings:" + << " m_title: " << settings.m_title + << " m_rgbColor: " << settings.m_rgbColor + << " m_rxDeviceSetIndex: " << settings.m_rxDeviceSetIndex + << " m_txDeviceSetIndex: " << settings.m_txDeviceSetIndex + << " m_rx2TxDelayMs: " << settings.m_rx2TxDelayMs + << " m_tx2RxDelayMs: " << settings.m_tx2RxDelayMs + << " force: " << force; + m_settings = settings; +} + +void AFCWorker::sendPTT(bool tx) +{ + if (!m_updateTimer.isActive()) + { + bool switchedOff = false; + m_mutex.lock(); + + if (tx) + { + if (m_settings.m_rxDeviceSetIndex >= 0) + { + m_tx = false; + switchedOff = turnDevice(false); + } + + if (m_settings.m_txDeviceSetIndex >= 0) + { + m_tx = true; + m_updateTimer.start(m_settings.m_rx2TxDelayMs); + } + } + else + { + if (m_settings.m_txDeviceSetIndex >= 0) + { + m_tx = true; + switchedOff = turnDevice(false); + } + + if (m_settings.m_rxDeviceSetIndex >= 0) + { + m_tx = false; + m_updateTimer.start(m_settings.m_tx2RxDelayMs); + } + } + + if (switchedOff && (m_msgQueueToGUI)) + { + AFCReport::MsgRadioState *msg = AFCReport::MsgRadioState::create(AFCReport::RadioIdle); + m_msgQueueToGUI->push(msg); + } + } +} + +void AFCWorker::updateHardware() +{ + SWGSDRangel::SWGSuccessResponse response; + SWGSDRangel::SWGErrorResponse error; + m_updateTimer.stop(); + m_mutex.unlock(); + + if (turnDevice(true)) + { + m_webAPIAdapterInterface->devicesetFocusPatch( + m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); + + if (m_msgQueueToGUI) + { + AFCReport::MsgRadioState *msg = AFCReport::MsgRadioState::create( + m_tx ? AFCReport::RadioTx : AFCReport::RadioRx + ); + m_msgQueueToGUI->push(msg); + } + } +} + +bool AFCWorker::turnDevice(bool on) +{ + SWGSDRangel::SWGDeviceState response; + SWGSDRangel::SWGErrorResponse error; + int httpCode; + + if (on) { + httpCode = m_webAPIAdapterInterface->devicesetDeviceRunPost( + m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); + } else { + httpCode = m_webAPIAdapterInterface->devicesetDeviceRunDelete( + m_tx ? m_settings.m_txDeviceSetIndex : m_settings.m_rxDeviceSetIndex, response, error); + } + + if (httpCode/100 == 2) + { + return true; + } + else + { + qWarning("AFCWorker::turnDevice: error: %s", qPrintable(*error.getMessage())); + return false; + } +} diff --git a/plugins/feature/afc/afcworker.h b/plugins/feature/afc/afcworker.h new file mode 100644 index 000000000..86ef75b22 --- /dev/null +++ b/plugins/feature/afc/afcworker.h @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_AFCWORKER_H_ +#define INCLUDE_FEATURE_AFCWORKER_H_ + +#include +#include + +#include "util/message.h" +#include "util/messagequeue.h" + +#include "afcsettings.h" + +class WebAPIAdapterInterface; + +class AFCWorker : public QObject +{ + Q_OBJECT +public: + class MsgConfigureAFCWorker : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const AFCSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureAFCWorker* create(const AFCSettings& settings, bool force) + { + return new MsgConfigureAFCWorker(settings, force); + } + + private: + AFCSettings m_settings; + bool m_force; + + MsgConfigureAFCWorker(const AFCSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgPTT : public Message { + MESSAGE_CLASS_DECLARATION + + public: + bool getTx() const { return m_tx; } + + static MsgPTT* create(bool tx) { + return new MsgPTT(tx); + } + + private: + bool m_tx; + + MsgPTT(bool tx) : + Message(), + m_tx(tx) + { } + }; + + AFCWorker(WebAPIAdapterInterface *webAPIAdapterInterface); + ~AFCWorker(); + void reset(); + bool startWork(); + void stopWork(); + bool isRunning() const { return m_running; } + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; } + +private: + WebAPIAdapterInterface *m_webAPIAdapterInterface; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + MessageQueue *m_msgQueueToGUI; //!< Queue to report state to GUI + AFCSettings m_settings; + bool m_running; + bool m_tx; + QTimer m_updateTimer; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const AFCSettings& settings, bool force = false); + void sendPTT(bool tx); + bool turnDevice(bool on); + +private slots: + void handleInputMessages(); + void updateHardware(); +}; + +#endif // INCLUDE_FEATURE_SIMPLEPTTWORKER_H_ diff --git a/plugins/feature/afc/readme.md b/plugins/feature/afc/readme.md new file mode 100644 index 000000000..6d60f5b16 --- /dev/null +++ b/plugins/feature/afc/readme.md @@ -0,0 +1,45 @@ +

AFC plugin

+ +

Introduction

+ +This plugin controls switchover between a Rx (Device source) and Tx (Device sink). It has no other controls than an adjustable delay from Rx to Tx and back to Rx. Because of its simplicity it can also serve as a model to build other feature plugins. + +

Interface

+ +![File source channel plugin GUI](../../../doc/img/AFC_plugin.png) + +

1: Start/Stop plugin

+ +This button starts or stops the plugin + +

2: PTT button

+ +Click to switch from Rx to Tx and again to switch back to Rx. When in Tx mode the button is lit. + +

3: Status indicator

+ +This LED type display shows the current PTT status: + + - **Green**: Rx runs + - **Red**: Tx runs + - **Grey**: None active (transient) + +

4: Refresh list of devices

+ +Use this button to refresh the list of devices (5) and (6) + +

5: Select Rx device set

+ +Use this combo to select which Rx device is controlled + +

6: Select Tx device set

+ +Use this combo to select which Tx device is controlled + +

7: Transistion delay from Rx to Tx

+ +Value in milliseconds between Rx stop and Tx start + +

8: Transistion delay from Tx to Rx

+ +Value in milliseconds between Tx stop and Rx start From ffdc6f69f0de1776d677416f40cade507c8a02b4 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 17 Oct 2020 23:41:54 +0200 Subject: [PATCH 03/27] AFC: settings fix REST API sources --- swagger/sdrangel/api/swagger/include/AFC.yaml | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/swagger/sdrangel/api/swagger/include/AFC.yaml b/swagger/sdrangel/api/swagger/include/AFC.yaml index 4397ab890..4781ce38b 100644 --- a/swagger/sdrangel/api/swagger/include/AFC.yaml +++ b/swagger/sdrangel/api/swagger/include/AFC.yaml @@ -5,18 +5,32 @@ AFCSettings: type: string rgbColor: type: integer - rxDeviceSetIndex: - description: index of the Rx device set to connect the Rx side to + trackerDeviceSetIndex: + description: index of the device set of frequency tracker being used type: integer - txDeviceSetIndex: - description: index of the Tx device set to connect the Tx side to + trackedDeviceSetIndex: + description: index of the device set being tracked (channels and possibly device) type: integer - rx2TxDelayMs: - description: Delay in milliseconds from Rx off to Tx on + hasTargetFrequency: type: integer - tx2RxDelayMs: - description: Delay in milliseconds from Tx off to Rx on + description: > + Adjust device frequency to match tracker frequency + * 0 - disabled + * 1 - enabled + transverterTarget: type: integer + description: > + Use transverter or device frequency for tracker frequency adjustment + * 0 - device + * 1 - transverter + targetFrequency: + description: Target frequency for the tracker + type: integer + format: int64 + freqTolerance: + descritpion: Frequency shift tolerance before tracker frequency is (re)adjusted + type: integer + format: int64 useReverseAPI: description: Synchronize with reverse API (1 for yes, 0 for no) type: integer From 4ac114a924a2f98db040ee36ee0c20cb7dbea796 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 27 Oct 2020 06:01:57 +0100 Subject: [PATCH 04/27] AFC: settings fix REST API sources: generated code --- sdrbase/resources/webapi/doc/html2/index.html | 81 +++- .../webapi/doc/swagger/include/AFC.yaml | 64 +++ .../doc/swagger/include/FeatureActions.yaml | 2 + .../doc/swagger/include/FeatureReport.yaml | 2 + .../doc/swagger/include/FeatureSettings.yaml | 2 + swagger/sdrangel/code/html2/index.html | 81 +++- .../code/qt5/client/SWGAFCActions.cpp | 108 +++++ .../sdrangel/code/qt5/client/SWGAFCActions.h | 58 +++ .../sdrangel/code/qt5/client/SWGAFCReport.cpp | 108 +++++ .../sdrangel/code/qt5/client/SWGAFCReport.h | 58 +++ .../code/qt5/client/SWGAFCSettings.cpp | 388 ++++++++++++++++++ .../sdrangel/code/qt5/client/SWGAFCSettings.h | 131 ++++++ .../code/qt5/client/SWGFeatureActions.cpp | 25 ++ .../code/qt5/client/SWGFeatureActions.h | 7 + .../code/qt5/client/SWGFeatureReport.cpp | 25 ++ .../code/qt5/client/SWGFeatureReport.h | 7 + .../code/qt5/client/SWGFeatureSettings.cpp | 25 ++ .../code/qt5/client/SWGFeatureSettings.h | 7 + .../code/qt5/client/SWGModelFactory.h | 12 + 19 files changed, 1189 insertions(+), 2 deletions(-) create mode 100644 sdrbase/resources/webapi/doc/swagger/include/AFC.yaml create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCActions.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCActions.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCReport.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCSettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGAFCSettings.h diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 2dd81cdeb..977025a24 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -696,6 +696,76 @@ margin-bottom: 20px;