From 411cb36a6fb78524d344b14e24f98cf988ef5af7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 18 Aug 2018 15:51:46 +0200 Subject: [PATCH] Created SDRdaemon library --- CMakeLists.txt | 1 + sdrdaemon/CMakeLists.txt | 55 ++ sdrdaemon/sdrdaemonmain.cpp | 162 ++++++ sdrdaemon/sdrdaemonmain.h | 76 +++ sdrdaemon/sdrdaemonpreferences.cpp | 95 +++ sdrdaemon/sdrdaemonpreferences.h | 53 ++ sdrdaemon/sdrdaemonsettings.cpp | 51 ++ sdrdaemon/sdrdaemonsettings.h | 54 ++ sdrdaemon/webapi/webapiadapterdaemon.cpp | 164 ++++++ sdrdaemon/webapi/webapiadapterdaemon.h | 98 ++++ sdrdaemon/webapi/webapirequestmapper.cpp | 706 +++++++++++++++++++++++ sdrdaemon/webapi/webapirequestmapper.h | 76 +++ sdrdaemon/webapi/webapiserver.cpp | 67 +++ sdrdaemon/webapi/webapiserver.h | 56 ++ 14 files changed, 1714 insertions(+) create mode 100644 sdrdaemon/CMakeLists.txt create mode 100644 sdrdaemon/sdrdaemonmain.cpp create mode 100644 sdrdaemon/sdrdaemonmain.h create mode 100644 sdrdaemon/sdrdaemonpreferences.cpp create mode 100644 sdrdaemon/sdrdaemonpreferences.h create mode 100644 sdrdaemon/sdrdaemonsettings.cpp create mode 100644 sdrdaemon/sdrdaemonsettings.h create mode 100644 sdrdaemon/webapi/webapiadapterdaemon.cpp create mode 100644 sdrdaemon/webapi/webapiadapterdaemon.h create mode 100644 sdrdaemon/webapi/webapirequestmapper.cpp create mode 100644 sdrdaemon/webapi/webapirequestmapper.h create mode 100644 sdrdaemon/webapi/webapiserver.cpp create mode 100644 sdrdaemon/webapi/webapiserver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 133ef301d..1d39b60f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,7 @@ endif() add_subdirectory(sdrbase) add_subdirectory(sdrgui) add_subdirectory(sdrsrv) +add_subdirectory(sdrdaemon) add_subdirectory(sdrbench) add_subdirectory(httpserver) add_subdirectory(logging) diff --git a/sdrdaemon/CMakeLists.txt b/sdrdaemon/CMakeLists.txt new file mode 100644 index 000000000..e5c707520 --- /dev/null +++ b/sdrdaemon/CMakeLists.txt @@ -0,0 +1,55 @@ +project (sdrdaemon) + +set(sdrdaemon_SOURCES + sdrdaemonmain.cpp + sdrdaemonpreferences.cpp + sdrdaemonsettings.cpp + webapi/webapiadapterdaemon.cpp + webapi/webapirequestmapper.cpp + webapi/webapiserver.cpp +) + +set(sdrdaemon_HEADERS + sdrdaemonmain.h + sdrdaemonpreferences.h + sdrdaemonsettings.h + webapi/webapiadapterdaemon.h + webapi/webapirequestmapper.h + webapi/webapiserver.h +) + +set(sdrdaemon_SOURCES + ${sdrdaemon_SOURCES} + ${sdrdaemon_HEADERS} +) + +add_definitions(${QT_DEFINITIONS}) +add_definitions(-DQT_SHARED) + +add_library(sdrdaemon SHARED + ${sdrdaemon_SOURCES} + ${sdrdaemon_HEADERS_MOC} +) + +include_directories( + . + ${CMAKE_SOURCE_DIR}/exports + ${CMAKE_SOURCE_DIR}/sdrbase + ${CMAKE_SOURCE_DIR}/logging + ${CMAKE_SOURCE_DIR}/httpserver + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_link_libraries(sdrdaemon + ${QT_LIBRARIES} + sdrbase + logging +) + +target_compile_features(sdrdaemon PRIVATE cxx_generalized_initializers) # cmake >= 3.1.0 + +target_link_libraries(sdrdaemon Qt5::Core Qt5::Multimedia) + +install(TARGETS sdrdaemon DESTINATION lib) + diff --git a/sdrdaemon/sdrdaemonmain.cpp b/sdrdaemon/sdrdaemonmain.cpp new file mode 100644 index 000000000..db2567772 --- /dev/null +++ b/sdrdaemon/sdrdaemonmain.cpp @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 "dsp/dspengine.h" +#include "dsp/dspdevicesourceengine.h" +#include "plugin/pluginmanager.h" +#include "util/message.h" +#include "loggerwithfile.h" +#include "sdrdaemonmain.h" + +#include "webapi/webapiadapterdaemon.h" +#include "webapi/webapirequestmapper.h" +#include "webapi/webapiserver.h" + +SDRDaemonMain *SDRDaemonMain::m_instance = 0; + +SDRDaemonMain::SDRDaemonMain(qtwebapp::LoggerWithFile *logger, const MainParser& parser, QObject *parent) : + QObject(parent), + m_logger(logger), + m_settings(), + m_dspEngine(DSPEngine::instance()), + m_lastEngineState(DSPDeviceSourceEngine::StNotStarted) +{ + qDebug() << "SDRdaemon::SDRdaemon: start"; + + m_instance = this; + + m_pluginManager = new PluginManager(this); + m_pluginManager->loadPlugins(QString("pluginssrv")); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()), Qt::QueuedConnection); + m_masterTimer.start(50); + + loadSettings(); + + QString applicationDirPath = QCoreApplication::instance()->applicationDirPath(); + + if (QResource::registerResource(applicationDirPath + "/sdrbase.rcc")) { + qDebug("MainCore::MainCore: registered resource file %s/%s", qPrintable(applicationDirPath), "sdrbase.rcc"); + } else { + qWarning("MainCore::MainCore: could not register resource file %s/%s", qPrintable(applicationDirPath), "sdrbase.rcc"); + } + + m_apiAdapter = new WebAPIAdapterDaemon(*this); + m_requestMapper = new SDRDaemon::WebAPIRequestMapper(this); + m_requestMapper->setAdapter(m_apiAdapter); + m_apiServer = new SDRDaemon::WebAPIServer(parser.getServerAddress(), parser.getServerPort(), m_requestMapper); + m_apiServer->start(); + + qDebug() << "SDRdaemon::SDRdaemon: end"; +} + +SDRDaemonMain::~SDRDaemonMain() +{ + m_apiServer->stop(); + m_settings.save(); + delete m_apiServer; + delete m_requestMapper; + delete m_apiAdapter; + + delete m_pluginManager; + + qDebug() << "SDRdaemon::~SDRdaemon: end"; + delete m_logger; +} + +void SDRDaemonMain::loadSettings() +{ + qDebug() << "SDRdaemon::loadSettings"; + + m_settings.load(); + setLoggingOptions(); +} + +void SDRDaemonMain::setLoggingOptions() +{ + m_logger->setConsoleMinMessageLevel(m_settings.getConsoleMinLogLevel()); + + if (m_settings.getUseLogFile()) + { + qtwebapp::FileLoggerSettings fileLoggerSettings; // default values + + if (m_logger->hasFileLogger()) { + fileLoggerSettings = m_logger->getFileLoggerSettings(); // values from file logger if it exists + } + + fileLoggerSettings.fileName = m_settings.getLogFileName(); // put new values + m_logger->createOrSetFileLogger(fileLoggerSettings, 2000); // create file logger if it does not exist and apply settings in any case + } + + if (m_logger->hasFileLogger()) { + m_logger->setFileMinMessageLevel(m_settings.getFileMinLogLevel()); + } + + m_logger->setUseFileLogger(m_settings.getUseLogFile()); + + if (m_settings.getUseLogFile()) + { +#if QT_VERSION >= 0x050400 + QString appInfoStr(tr("%1 %2 Qt %3 %4b %5 %6 DSP Rx:%7b Tx:%8b PID %9") + .arg(QCoreApplication::applicationName()) + .arg(QCoreApplication::applicationVersion()) + .arg(QT_VERSION_STR) + .arg(QT_POINTER_SIZE*8) + .arg(QSysInfo::currentCpuArchitecture()) + .arg(QSysInfo::prettyProductName()) + .arg(SDR_RX_SAMP_SZ) + .arg(SDR_TX_SAMP_SZ) + .arg(QCoreApplication::applicationPid())); +#else + QString appInfoStr(tr("%1 %2 Qt %3 %4b DSP Rx:%5b Tx:%6b PID %7") + .arg(QCoreApplication::applicationName()) + .arg(QCoreApplication::applicationVersion()) + .arg(QT_VERSION_STR) + .arg(QT_POINTER_SIZE*8) + .arg(SDR_RX_SAMP_SZ) + .arg(SDR_RX_SAMP_SZ) + .arg(QCoreApplication::applicationPid()); + #endif + m_logger->logToFile(QtInfoMsg, appInfoStr); + } +} + +bool SDRDaemonMain::handleMessage(const Message& cmd __attribute__((unused))) +{ + return false; +} + +void SDRDaemonMain::handleMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != 0) + { + qDebug("SDRdaemon::handleMessages: message: %s", message->getIdentifier()); + handleMessage(*message); + delete message; + } +} diff --git a/sdrdaemon/sdrdaemonmain.h b/sdrdaemon/sdrdaemonmain.h new file mode 100644 index 000000000..1d9fe0862 --- /dev/null +++ b/sdrdaemon/sdrdaemonmain.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 SDRDAEMON_SDRDAEMONMAIN_H_ +#define SDRDAEMON_SDRDAEMONMAIN_H_ + +#include +#include + +#include "mainparser.h" +#include "sdrdaemonsettings.h" +#include "util/messagequeue.h" + +namespace SDRDaemon { + class WebAPIRequestMapper; + class WebAPIServer; +} + +namespace qtwebapp { + class LoggerWithFile; +} + +class DSPEngine; +class PluginManager; +class Message; +class WebAPIAdapterDaemon; + +class SDRDaemonMain : public QObject { + Q_OBJECT +public: + explicit SDRDaemonMain(qtwebapp::LoggerWithFile *logger, const MainParser& parser, QObject *parent = 0); + ~SDRDaemonMain(); + static SDRDaemonMain *getInstance() { return m_instance; } // Main Core is de facto a singleton so this just returns its reference + +private: + static SDRDaemonMain *m_instance; + qtwebapp::LoggerWithFile *m_logger; + SDRDaemonSettings m_settings; + DSPEngine* m_dspEngine; + int m_lastEngineState; + PluginManager* m_pluginManager; + MessageQueue m_inputMessageQueue; + QTimer m_masterTimer; + + SDRDaemon::WebAPIRequestMapper *m_requestMapper; + SDRDaemon::WebAPIServer *m_apiServer; + WebAPIAdapterDaemon *m_apiAdapter; + + void loadSettings(); + void setLoggingOptions(); + bool handleMessage(const Message& cmd); + +private slots: + void handleMessages(); +}; + +#endif /* SDRDAEMON_SDRDAEMONMAIN_H_ */ diff --git a/sdrdaemon/sdrdaemonpreferences.cpp b/sdrdaemon/sdrdaemonpreferences.cpp new file mode 100644 index 000000000..a194cc46f --- /dev/null +++ b/sdrdaemon/sdrdaemonpreferences.cpp @@ -0,0 +1,95 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 "util/simpleserializer.h" +#include "sdrdaemonpreferences.h" + +SDRDaemonPreferences::SDRDaemonPreferences() +{ + resetToDefaults(); +} + +void SDRDaemonPreferences::resetToDefaults() +{ + m_useLogFile = false; + m_logFileName = "sdrangel.log"; + m_consoleMinLogLevel = QtDebugMsg; + m_fileMinLogLevel = QtDebugMsg; +} + +QByteArray SDRDaemonPreferences::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, (int) m_consoleMinLogLevel); + s.writeBool(2, m_useLogFile); + s.writeString(3, m_logFileName); + s.writeS32(4, (int) m_fileMinLogLevel); + return s.final(); +} + +bool SDRDaemonPreferences::deserialize(const QByteArray& data) +{ + int tmpInt; + + SimpleDeserializer d(data); + + if(!d.isValid()) { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + d.readS32(1, &tmpInt, (int) QtDebugMsg); + + if ((tmpInt == (int) QtDebugMsg) || + (tmpInt == (int) QtInfoMsg) || + (tmpInt == (int) QtWarningMsg) || + (tmpInt == (int) QtCriticalMsg) || + (tmpInt == (int) QtFatalMsg)) { + m_consoleMinLogLevel = (QtMsgType) tmpInt; + } else { + m_consoleMinLogLevel = QtDebugMsg; + } + + d.readBool(2, &m_useLogFile, false); + d.readString(3, &m_logFileName, "sdrangel.log"); + + d.readS32(4, &tmpInt, (int) QtDebugMsg); + + if ((tmpInt == (int) QtDebugMsg) || + (tmpInt == (int) QtInfoMsg) || + (tmpInt == (int) QtWarningMsg) || + (tmpInt == (int) QtCriticalMsg) || + (tmpInt == (int) QtFatalMsg)) { + m_fileMinLogLevel = (QtMsgType) tmpInt; + } else { + m_fileMinLogLevel = QtDebugMsg; + } + + return true; + } else + { + resetToDefaults(); + return false; + } +} diff --git a/sdrdaemon/sdrdaemonpreferences.h b/sdrdaemon/sdrdaemonpreferences.h new file mode 100644 index 000000000..c3ea5c1f2 --- /dev/null +++ b/sdrdaemon/sdrdaemonpreferences.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 SDRDAEMON_SDRDAEMONPREFERENCES_H_ +#define SDRDAEMON_SDRDAEMONPREFERENCES_H_ + +#include + +class SDRDaemonPreferences +{ +public: + SDRDaemonPreferences(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_consoleMinLogLevel = minLogLevel; } + void setFileMinLogLevel(const QtMsgType& minLogLevel) { m_fileMinLogLevel = minLogLevel; } + void setUseLogFile(bool useLogFile) { m_useLogFile = useLogFile; } + void setLogFileName(const QString& value) { m_logFileName = value; } + QtMsgType getConsoleMinLogLevel() const { return m_consoleMinLogLevel; } + QtMsgType getFileMinLogLevel() const { return m_fileMinLogLevel; } + bool getUseLogFile() const { return m_useLogFile; } + const QString& getLogFileName() const { return m_logFileName; } + +private: + QtMsgType m_consoleMinLogLevel; + QtMsgType m_fileMinLogLevel; + bool m_useLogFile; + QString m_logFileName; +}; + +#endif /* SDRDAEMON_SDRDAEMONPREFERENCES_H_ */ diff --git a/sdrdaemon/sdrdaemonsettings.cpp b/sdrdaemon/sdrdaemonsettings.cpp new file mode 100644 index 000000000..2fc177701 --- /dev/null +++ b/sdrdaemon/sdrdaemonsettings.cpp @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 "sdrdaemonpreferences.h" +#include "sdrdaemonsettings.h" + +SDRDaemonSettings::SDRDaemonSettings() +{ + resetToDefaults(); +} + +SDRDaemonSettings::~SDRDaemonSettings() +{} + +void SDRDaemonSettings::load() +{ + QSettings s; + m_preferences.deserialize(qUncompress(QByteArray::fromBase64(s.value("preferences").toByteArray()))); +} + +void SDRDaemonSettings::save() const +{ + QSettings s; + s.setValue("preferences", qCompress(m_preferences.serialize()).toBase64()); +} + +void SDRDaemonSettings::resetToDefaults() +{ + m_preferences.resetToDefaults(); +} diff --git a/sdrdaemon/sdrdaemonsettings.h b/sdrdaemon/sdrdaemonsettings.h new file mode 100644 index 000000000..ef909a8fe --- /dev/null +++ b/sdrdaemon/sdrdaemonsettings.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon instance // +// // +// SDRdaemon is a detached SDR front end that handles the interface with a // +// physical device and sends or receives the I/Q samples stream to or from a // +// SDRangel instance via UDP. It is controlled via a Web REST API. // +// // +// 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 // +// // +// 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 SDRDAEMON_SDRDAEMONSETTINGS_H_ +#define SDRDAEMON_SDRDAEMONSETTINGS_H_ + +#include "sdrdaemonpreferences.h" + +class SDRDaemonSettings +{ +public: + SDRDaemonSettings(); + ~SDRDaemonSettings(); + + void load(); + void save() const; + + void resetToDefaults(); + + void setConsoleMinLogLevel(const QtMsgType& minLogLevel) { m_preferences.setConsoleMinLogLevel(minLogLevel); } + void setFileMinLogLevel(const QtMsgType& minLogLevel) { m_preferences.setFileMinLogLevel(minLogLevel); } + void setUseLogFile(bool useLogFile) { m_preferences.setUseLogFile(useLogFile); } + void setLogFileName(const QString& value) { m_preferences.setLogFileName(value); } + QtMsgType getConsoleMinLogLevel() const { return m_preferences.getConsoleMinLogLevel(); } + QtMsgType getFileMinLogLevel() const { return m_preferences.getFileMinLogLevel(); } + bool getUseLogFile() const { return m_preferences.getUseLogFile(); } + const QString& getLogFileName() const { return m_preferences.getLogFileName(); } + +private: + SDRDaemonPreferences m_preferences; +}; + + + +#endif /* SDRDAEMON_SDRDAEMONSETTINGS_H_ */ diff --git a/sdrdaemon/webapi/webapiadapterdaemon.cpp b/sdrdaemon/webapi/webapiadapterdaemon.cpp new file mode 100644 index 000000000..5703a3736 --- /dev/null +++ b/sdrdaemon/webapi/webapiadapterdaemon.cpp @@ -0,0 +1,164 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRDaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 "SWGDaemonSummaryResponse.h" +#include "SWGDeviceSettings.h" +#include "SWGDeviceState.h" +#include "SWGDeviceReport.h" +#include "SWGErrorResponse.h" + +#include "webapiadapterdaemon.h" + +QString WebAPIAdapterDaemon::daemonInstanceSummaryURL = "/sdrdaemon"; +QString WebAPIAdapterDaemon::daemonInstanceLoggingURL = "/sdrdaemon/logging"; +QString WebAPIAdapterDaemon::daemonSettingsURL = "/sdrdaemon/settings"; +QString WebAPIAdapterDaemon::daemonReportURL = "/sdrdaemon/report"; +QString WebAPIAdapterDaemon::daemonRunURL = "/sdrdaemon/run"; + +WebAPIAdapterDaemon::WebAPIAdapterDaemon(SDRDaemonMain& sdrDaemonMain) : + m_sdrDaemonMain(sdrDaemonMain) +{ +} + +WebAPIAdapterDaemon::~WebAPIAdapterDaemon() +{ +} + +int WebAPIAdapterDaemon::daemonInstanceSummary( + SWGSDRangel::SWGDaemonSummaryResponse& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonInstanceLoggingGet( + SWGSDRangel::SWGLoggingInfo& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonInstanceLoggingPut( + SWGSDRangel::SWGLoggingInfo& query __attribute__((unused)), + SWGSDRangel::SWGLoggingInfo& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonSettingsGet( + SWGSDRangel::SWGDeviceSettings& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonSettingsPutPatch( + bool force __attribute__((unused)), + const QStringList& deviceSettingsKeys __attribute__((unused)), + SWGSDRangel::SWGDeviceSettings& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonRunGet( + SWGSDRangel::SWGDeviceState& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonRunPost( + SWGSDRangel::SWGDeviceState& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonRunDelete( + SWGSDRangel::SWGDeviceState& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +int WebAPIAdapterDaemon::daemonReportGet( + SWGSDRangel::SWGDeviceReport& response __attribute__((unused)), + SWGSDRangel::SWGErrorResponse& error) +{ + error.init(); + *error.getMessage() = "Not implemented"; + return 501; +} + +// TODO: put in library in common with SDRangel. Can be static. +QtMsgType WebAPIAdapterDaemon::getMsgTypeFromString(const QString& msgTypeString) +{ + if (msgTypeString == "debug") { + return QtDebugMsg; + } else if (msgTypeString == "info") { + return QtInfoMsg; + } else if (msgTypeString == "warning") { + return QtWarningMsg; + } else if (msgTypeString == "error") { + return QtCriticalMsg; + } else { + return QtDebugMsg; + } +} + +// TODO: put in library in common with SDRangel. Can be static. +void WebAPIAdapterDaemon::getMsgTypeString(const QtMsgType& msgType, QString& levelStr) +{ + switch (msgType) + { + case QtDebugMsg: + levelStr = "debug"; + break; + case QtInfoMsg: + levelStr = "info"; + break; + case QtWarningMsg: + levelStr = "warning"; + break; + case QtCriticalMsg: + case QtFatalMsg: + levelStr = "error"; + break; + default: + levelStr = "debug"; + break; + } +} diff --git a/sdrdaemon/webapi/webapiadapterdaemon.h b/sdrdaemon/webapi/webapiadapterdaemon.h new file mode 100644 index 000000000..8cd5af801 --- /dev/null +++ b/sdrdaemon/webapi/webapiadapterdaemon.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRDaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 SDRDAEMON_WEBAPI_WEBAPIADAPTERDAEMON_H_ +#define SDRDAEMON_WEBAPI_WEBAPIADAPTERDAEMON_H_ + +#include +#include + +namespace SWGSDRangel +{ + class SWGDaemonSummaryResponse; + class SWGDeviceSet; + class SWGDeviceListItem; + class SWGDeviceSettings; + class SWGDeviceState; + class SWGDeviceReport; + class SWGSuccessResponse; + class SWGErrorResponse; + class SWGLoggingInfo; +} + +class SDRDaemonMain; + +class WebAPIAdapterDaemon +{ +public: + WebAPIAdapterDaemon(SDRDaemonMain& sdrDaemonMain); + ~WebAPIAdapterDaemon(); + + int daemonInstanceSummary( + SWGSDRangel::SWGDaemonSummaryResponse& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonInstanceLoggingGet( + SWGSDRangel::SWGLoggingInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonInstanceLoggingPut( + SWGSDRangel::SWGLoggingInfo& query, + SWGSDRangel::SWGLoggingInfo& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonSettingsGet( + SWGSDRangel::SWGDeviceSettings& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonSettingsPutPatch( + bool force, + const QStringList& deviceSettingsKeys, + SWGSDRangel::SWGDeviceSettings& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonRunGet( + SWGSDRangel::SWGDeviceState& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonRunPost( + SWGSDRangel::SWGDeviceState& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonRunDelete( + SWGSDRangel::SWGDeviceState& response, + SWGSDRangel::SWGErrorResponse& error); + + int daemonReportGet( + SWGSDRangel::SWGDeviceReport& response, + SWGSDRangel::SWGErrorResponse& error); + + static QString daemonInstanceSummaryURL; + static QString daemonInstanceLoggingURL; + static QString daemonSettingsURL; + static QString daemonReportURL; + static QString daemonRunURL; + +private: + SDRDaemonMain& m_sdrDaemonMain; + + static QtMsgType getMsgTypeFromString(const QString& msgTypeString); + static void getMsgTypeString(const QtMsgType& msgType, QString& level); +}; + +#endif /* SDRDAEMON_WEBAPI_WEBAPIADAPTERDAEMON_H_ */ diff --git a/sdrdaemon/webapi/webapirequestmapper.cpp b/sdrdaemon/webapi/webapirequestmapper.cpp new file mode 100644 index 000000000..3b2a6eb7f --- /dev/null +++ b/sdrdaemon/webapi/webapirequestmapper.cpp @@ -0,0 +1,706 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRDaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 "httpdocrootsettings.h" +#include "webapirequestmapper.h" +#include "SWGDaemonSummaryResponse.h" +#include "SWGInstanceDevicesResponse.h" +#include "SWGDeviceSettings.h" +#include "SWGDeviceState.h" +#include "SWGDeviceReport.h" +#include "SWGSuccessResponse.h" +#include "SWGErrorResponse.h" +#include "SWGLoggingInfo.h" + +#include "webapirequestmapper.h" +#include "webapiadapterdaemon.h" + +namespace SDRDaemon +{ + +WebAPIRequestMapper::WebAPIRequestMapper(QObject* parent) : + HttpRequestHandler(parent), + m_adapter(0) +{ + qtwebapp::HttpDocrootSettings docrootSettings; + docrootSettings.path = ":/webapi"; + m_staticFileController = new qtwebapp::StaticFileController(docrootSettings, parent); +} + +WebAPIRequestMapper::~WebAPIRequestMapper() +{ + delete m_staticFileController; +} + +void WebAPIRequestMapper::service(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + if (m_adapter == 0) // format service unavailable if adapter is null + { + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setStatus(500,"Service not available"); + + errorResponse.init(); + *errorResponse.getMessage() = "Service not available"; + response.write(errorResponse.asJson().toUtf8()); + } + else // normal processing + { + QByteArray path=request.getPath(); + + // Handle pre-flight requests + if (request.getMethod() == "OPTIONS") + { + qDebug("WebAPIRequestMapper::service: method OPTIONS: assume pre-flight"); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Headers", "*"); + response.setHeader("Access-Control-Allow-Methods", "*"); + response.setStatus(200, "OK"); + return; + } + + if (path.startsWith("/sdrangel")) + { + SWGSDRangel::SWGErrorResponse errorResponse; + response.setStatus(501,"Not implemented"); + errorResponse.init(); + *errorResponse.getMessage() = "Not implemented"; + response.write(errorResponse.asJson().toUtf8()); + return; + } + + if (path == WebAPIAdapterDaemon::daemonInstanceSummaryURL) { + daemonInstanceSummaryService(request, response); + } else if (path == WebAPIAdapterDaemon::daemonInstanceLoggingURL) { + daemonInstanceLoggingService(request, response); + } else if (path == WebAPIAdapterDaemon::daemonSettingsURL) { + daemonSettingsService(request, response); + } else if (path == WebAPIAdapterDaemon::daemonReportURL) { + daemonReportService(request, response); + } else if (path == WebAPIAdapterDaemon::daemonRunURL) { + daemonRunService(request, response); + } else { + m_staticFileController->service(request, response); // serve static pages + } + } +} + +void WebAPIRequestMapper::daemonInstanceSummaryService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGDaemonSummaryResponse normalResponse; + + int status = m_adapter->daemonInstanceSummary(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + +void WebAPIRequestMapper::daemonInstanceLoggingService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGLoggingInfo query; + SWGSDRangel::SWGLoggingInfo normalResponse; + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if (request.getMethod() == "GET") + { + int status = m_adapter->daemonInstanceLoggingGet(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "PUT") + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + query.fromJson(jsonStr); + int status = m_adapter->daemonInstanceLoggingPut(query, normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + +void WebAPIRequestMapper::daemonSettingsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if ((request.getMethod() == "PUT") || (request.getMethod() == "PATCH")) + { + QString jsonStr = request.getBody(); + QJsonObject jsonObject; + + if (parseJsonBody(jsonStr, jsonObject, response)) + { + SWGSDRangel::SWGDeviceSettings normalResponse; + resetDeviceSettings(normalResponse); + QStringList deviceSettingsKeys; + + if (validateDeviceSettings(normalResponse, jsonObject, deviceSettingsKeys)) + { + int status = m_adapter->daemonSettingsPutPatch( + (request.getMethod() == "PUT"), // force settings on PUT + deviceSettingsKeys, + normalResponse, + errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON request"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON request"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(400,"Invalid JSON format"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid JSON format"; + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "GET") + { + SWGSDRangel::SWGDeviceSettings normalResponse; + resetDeviceSettings(normalResponse); + int status = m_adapter->daemonSettingsGet(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + +void WebAPIRequestMapper::daemonReportService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGDeviceReport normalResponse; + resetDeviceReport(normalResponse); + int status = m_adapter->daemonReportGet(normalResponse, errorResponse); + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + +void WebAPIRequestMapper::daemonRunService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + response.setHeader("Content-Type", "application/json"); + response.setHeader("Access-Control-Allow-Origin", "*"); + + if (request.getMethod() == "GET") + { + SWGSDRangel::SWGDeviceState normalResponse; + int status = m_adapter->daemonRunGet(normalResponse, errorResponse); + + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "POST") + { + SWGSDRangel::SWGDeviceState normalResponse; + int status = m_adapter->daemonRunPost(normalResponse, errorResponse); + + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else if (request.getMethod() == "DELETE") + { + SWGSDRangel::SWGDeviceState normalResponse; + int status = m_adapter->daemonRunDelete(normalResponse, errorResponse); + + response.setStatus(status); + + if (status/100 == 2) { + response.write(normalResponse.asJson().toUtf8()); + } else { + response.write(errorResponse.asJson().toUtf8()); + } + } + else + { + response.setStatus(405,"Invalid HTTP method"); + errorResponse.init(); + *errorResponse.getMessage() = "Invalid HTTP method"; + response.write(errorResponse.asJson().toUtf8()); + } +} + +// TODO: put in library in common with SDRangel. Can be static. +bool WebAPIRequestMapper::validateDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings, QJsonObject& jsonObject, QStringList& deviceSettingsKeys) +{ + if (jsonObject.contains("tx")) { + deviceSettings.setTx(jsonObject["tx"].toInt()); + } else { + deviceSettings.setTx(0); // assume Rx + } + + if (jsonObject.contains("deviceHwType") && jsonObject["deviceHwType"].isString()) { + deviceSettings.setDeviceHwType(new QString(jsonObject["deviceHwType"].toString())); + } else { + return false; + } + + QString *deviceHwType = deviceSettings.getDeviceHwType(); + + if ((*deviceHwType == "Airspy") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("airspySettings") && jsonObject["airspySettings"].isObject()) + { + QJsonObject airspySettingsJsonObject = jsonObject["airspySettings"].toObject(); + deviceSettingsKeys = airspySettingsJsonObject.keys(); + deviceSettings.setAirspySettings(new SWGSDRangel::SWGAirspySettings()); + deviceSettings.getAirspySettings()->fromJsonObject(airspySettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "AirspyHF") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("airspyHFSettings") && jsonObject["airspyHFSettings"].isObject()) + { + QJsonObject airspyHFSettingsJsonObject = jsonObject["airspyHFSettings"].toObject(); + deviceSettingsKeys = airspyHFSettingsJsonObject.keys(); + deviceSettings.setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings()); + deviceSettings.getAirspyHfSettings()->fromJsonObject(airspyHFSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "BladeRF") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("bladeRFInputSettings") && jsonObject["bladeRFInputSettings"].isObject()) + { + QJsonObject bladeRFInputSettingsJsonObject = jsonObject["bladeRFInputSettings"].toObject(); + deviceSettingsKeys = bladeRFInputSettingsJsonObject.keys(); + deviceSettings.setBladeRfInputSettings(new SWGSDRangel::SWGBladeRFInputSettings()); + deviceSettings.getBladeRfInputSettings()->fromJsonObject(bladeRFInputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "BladeRF") && (deviceSettings.getTx() != 0)) + { + if (jsonObject.contains("bladeRFOutputSettings") && jsonObject["bladeRFOutputSettings"].isObject()) + { + QJsonObject bladeRFOutputSettingsJsonObject = jsonObject["bladeRFOutputSettings"].toObject(); + deviceSettingsKeys = bladeRFOutputSettingsJsonObject.keys(); + deviceSettings.setBladeRfOutputSettings(new SWGSDRangel::SWGBladeRFOutputSettings()); + deviceSettings.getBladeRfOutputSettings()->fromJsonObject(bladeRFOutputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "FCDPro") + { + if (jsonObject.contains("fcdProSettings") && jsonObject["fcdProSettings"].isObject()) + { + QJsonObject fcdProSettingsJsonObject = jsonObject["fcdProSettings"].toObject(); + deviceSettingsKeys = fcdProSettingsJsonObject.keys(); + deviceSettings.setFcdProSettings(new SWGSDRangel::SWGFCDProSettings()); + deviceSettings.getFcdProSettings()->fromJsonObject(fcdProSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "FCDProPlus") + { + if (jsonObject.contains("fcdProPlusSettings") && jsonObject["fcdProPlusSettings"].isObject()) + { + QJsonObject fcdProPlusSettingsJsonObject = jsonObject["fcdProPlusSettings"].toObject(); + deviceSettingsKeys = fcdProPlusSettingsJsonObject.keys(); + deviceSettings.setFcdProPlusSettings(new SWGSDRangel::SWGFCDProPlusSettings()); + deviceSettings.getFcdProPlusSettings()->fromJsonObject(fcdProPlusSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "FileSource") + { + if (jsonObject.contains("fileSourceSettings") && jsonObject["fileSourceSettings"].isObject()) + { + QJsonObject fileSourceSettingsJsonObject = jsonObject["fileSourceSettings"].toObject(); + deviceSettingsKeys = fileSourceSettingsJsonObject.keys(); + deviceSettings.setFileSourceSettings(new SWGSDRangel::SWGFileSourceSettings()); + deviceSettings.getFileSourceSettings()->fromJsonObject(fileSourceSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "HackRF") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("hackRFInputSettings") && jsonObject["hackRFInputSettings"].isObject()) + { + QJsonObject hackRFInputSettingsJsonObject = jsonObject["hackRFInputSettings"].toObject(); + deviceSettingsKeys = hackRFInputSettingsJsonObject.keys(); + deviceSettings.setHackRfInputSettings(new SWGSDRangel::SWGHackRFInputSettings()); + deviceSettings.getHackRfInputSettings()->fromJsonObject(hackRFInputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "HackRF") && (deviceSettings.getTx() != 0)) + { + if (jsonObject.contains("hackRFOutputSettings") && jsonObject["hackRFOutputSettings"].isObject()) + { + QJsonObject hackRFOutputSettingsJsonObject = jsonObject["hackRFOutputSettings"].toObject(); + deviceSettingsKeys = hackRFOutputSettingsJsonObject.keys(); + deviceSettings.setHackRfOutputSettings(new SWGSDRangel::SWGHackRFOutputSettings()); + deviceSettings.getHackRfOutputSettings()->fromJsonObject(hackRFOutputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "LimeSDR") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("limeSdrInputSettings") && jsonObject["limeSdrInputSettings"].isObject()) + { + QJsonObject limeSdrInputSettingsJsonObject = jsonObject["limeSdrInputSettings"].toObject(); + deviceSettingsKeys = limeSdrInputSettingsJsonObject.keys(); + deviceSettings.setLimeSdrInputSettings(new SWGSDRangel::SWGLimeSdrInputSettings()); + deviceSettings.getLimeSdrInputSettings()->fromJsonObject(limeSdrInputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "LimeSDR") && (deviceSettings.getTx() != 0)) + { + if (jsonObject.contains("limeSdrOutputSettings") && jsonObject["limeSdrOutputSettings"].isObject()) + { + QJsonObject limeSdrOutputSettingsJsonObject = jsonObject["limeSdrOutputSettings"].toObject(); + deviceSettingsKeys = limeSdrOutputSettingsJsonObject.keys(); + deviceSettings.setLimeSdrOutputSettings(new SWGSDRangel::SWGLimeSdrOutputSettings()); + deviceSettings.getLimeSdrOutputSettings()->fromJsonObject(limeSdrOutputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "Perseus") + { + if (jsonObject.contains("perseusSettings") && jsonObject["perseusSettings"].isObject()) + { + QJsonObject perseusSettingsJsonObject = jsonObject["perseusSettings"].toObject(); + deviceSettingsKeys = perseusSettingsJsonObject.keys(); + deviceSettings.setPerseusSettings(new SWGSDRangel::SWGPerseusSettings()); + deviceSettings.getPerseusSettings()->fromJsonObject(perseusSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "PlutoSDR") && (deviceSettings.getTx() == 0)) + { + if (jsonObject.contains("plutoSdrInputSettings") && jsonObject["plutoSdrInputSettings"].isObject()) + { + QJsonObject plutoSdrInputSettingsJsonObject = jsonObject["plutoSdrInputSettings"].toObject(); + deviceSettingsKeys = plutoSdrInputSettingsJsonObject.keys(); + deviceSettings.setPlutoSdrInputSettings(new SWGSDRangel::SWGPlutoSdrInputSettings()); + deviceSettings.getPlutoSdrInputSettings()->fromJsonObject(plutoSdrInputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if ((*deviceHwType == "PlutoSDR") && (deviceSettings.getTx() != 0)) + { + if (jsonObject.contains("plutoSdrOutputSettings") && jsonObject["plutoSdrOutputSettings"].isObject()) + { + QJsonObject plutoSdrOutputSettingsJsonObject = jsonObject["plutoSdrOutputSettings"].toObject(); + deviceSettingsKeys = plutoSdrOutputSettingsJsonObject.keys(); + deviceSettings.setPlutoSdrOutputSettings(new SWGSDRangel::SWGPlutoSdrOutputSettings()); + deviceSettings.getPlutoSdrOutputSettings()->fromJsonObject(plutoSdrOutputSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "RTLSDR") + { + if (jsonObject.contains("rtlSdrSettings") && jsonObject["rtlSdrSettings"].isObject()) + { + QJsonObject rtlSdrSettingsJsonObject = jsonObject["rtlSdrSettings"].toObject(); + deviceSettingsKeys = rtlSdrSettingsJsonObject.keys(); + deviceSettings.setRtlSdrSettings(new SWGSDRangel::SWGRtlSdrSettings()); + deviceSettings.getRtlSdrSettings()->fromJsonObject(rtlSdrSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else if (*deviceHwType == "TestSource") + { + if (jsonObject.contains("testSourceSettings") && jsonObject["testSourceSettings"].isObject()) + { + QJsonObject testSourceSettingsJsonObject = jsonObject["testSourceSettings"].toObject(); + deviceSettingsKeys = testSourceSettingsJsonObject.keys(); + deviceSettings.setTestSourceSettings(new SWGSDRangel::SWGTestSourceSettings()); + deviceSettings.getTestSourceSettings()->fromJsonObject(testSourceSettingsJsonObject); + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +// TODO: put in library in common with SDRangel. Can be static. +void WebAPIRequestMapper::appendSettingsSubKeys( + const QJsonObject& parentSettingsJsonObject, + QJsonObject& childSettingsJsonObject, + const QString& parentKey, + QStringList& keyList) +{ + childSettingsJsonObject = parentSettingsJsonObject[parentKey].toObject(); + QStringList childSettingsKeys = childSettingsJsonObject.keys(); + + for (int i = 0; i < childSettingsKeys.size(); i++) { + keyList.append(parentKey + QString(".") + childSettingsKeys.at(i)); + } +} + +// TODO: put in library in common with SDRangel. Can be static. +bool WebAPIRequestMapper::parseJsonBody(QString& jsonStr, QJsonObject& jsonObject, qtwebapp::HttpResponse& response) +{ + SWGSDRangel::SWGErrorResponse errorResponse; + + try + { + QByteArray jsonBytes(jsonStr.toStdString().c_str()); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(jsonBytes, &error); + + if (error.error == QJsonParseError::NoError) + { + jsonObject = doc.object(); + } + else + { + QString errorMsg = QString("Input JSON error: ") + error.errorString() + QString(" at offset ") + QString::number(error.offset); + errorResponse.init(); + *errorResponse.getMessage() = errorMsg; + response.setStatus(400, errorMsg.toUtf8()); + response.write(errorResponse.asJson().toUtf8()); + } + + return (error.error == QJsonParseError::NoError); + } + catch (const std::exception& ex) + { + QString errorMsg = QString("Error parsing request: ") + ex.what(); + errorResponse.init(); + *errorResponse.getMessage() = errorMsg; + response.setStatus(500, errorMsg.toUtf8()); + response.write(errorResponse.asJson().toUtf8()); + + return false; + } +} + +// TODO: put in library in common with SDRangel. Can be static. +void WebAPIRequestMapper::resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings) +{ + deviceSettings.cleanup(); + deviceSettings.setDeviceHwType(0); + deviceSettings.setAirspySettings(0); + deviceSettings.setAirspyHfSettings(0); + deviceSettings.setBladeRfInputSettings(0); + deviceSettings.setBladeRfOutputSettings(0); + deviceSettings.setFcdProPlusSettings(0); + deviceSettings.setFcdProSettings(0); + deviceSettings.setFileSourceSettings(0); + deviceSettings.setHackRfInputSettings(0); + deviceSettings.setHackRfOutputSettings(0); + deviceSettings.setLimeSdrInputSettings(0); + deviceSettings.setLimeSdrOutputSettings(0); + deviceSettings.setPerseusSettings(0); + deviceSettings.setPlutoSdrInputSettings(0); + deviceSettings.setPlutoSdrOutputSettings(0); + deviceSettings.setRtlSdrSettings(0); + deviceSettings.setSdrDaemonSinkSettings(0); + deviceSettings.setSdrDaemonSourceSettings(0); + deviceSettings.setSdrPlaySettings(0); + deviceSettings.setTestSourceSettings(0); +} + +// TODO: put in library in common with SDRangel. Can be static. +void WebAPIRequestMapper::resetDeviceReport(SWGSDRangel::SWGDeviceReport& deviceReport) +{ + deviceReport.cleanup(); + deviceReport.setDeviceHwType(0); + deviceReport.setAirspyHfReport(0); + deviceReport.setAirspyReport(0); + deviceReport.setFileSourceReport(0); + deviceReport.setLimeSdrInputReport(0); + deviceReport.setLimeSdrOutputReport(0); + deviceReport.setPerseusReport(0); + deviceReport.setPlutoSdrInputReport(0); + deviceReport.setPlutoSdrOutputReport(0); + deviceReport.setRtlSdrReport(0); + deviceReport.setSdrDaemonSinkReport(0); + deviceReport.setSdrDaemonSourceReport(0); + deviceReport.setSdrPlayReport(0); +} + +} // namespace SDRDaemon + + diff --git a/sdrdaemon/webapi/webapirequestmapper.h b/sdrdaemon/webapi/webapirequestmapper.h new file mode 100644 index 000000000..14cc361c8 --- /dev/null +++ b/sdrdaemon/webapi/webapirequestmapper.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB. // +// // +// SDRDaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 SDRDAEMON_WEBAPI_WEBAPIREQUESTMAPPER_H_ +#define SDRDAEMON_WEBAPI_WEBAPIREQUESTMAPPER_H_ + +#include + +#include "httprequesthandler.h" +#include "httprequest.h" +#include "httpresponse.h" +#include "staticfilecontroller.h" + +#include "export.h" + +namespace SWGSDRangel +{ + class SWGDeviceSettings; + class SWGDeviceReport; +} + +class WebAPIAdapterDaemon; + +namespace SDRDaemon +{ + +class SDRBASE_API WebAPIRequestMapper : public qtwebapp::HttpRequestHandler { + Q_OBJECT +public: + WebAPIRequestMapper(QObject* parent=0); + ~WebAPIRequestMapper(); + void service(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void setAdapter(WebAPIAdapterDaemon *adapter) { m_adapter = adapter; } + +private: + WebAPIAdapterDaemon *m_adapter; + qtwebapp::StaticFileController *m_staticFileController; + + void daemonInstanceSummaryService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void daemonInstanceLoggingService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void daemonSettingsService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void daemonRunService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + void daemonReportService(qtwebapp::HttpRequest& request, qtwebapp::HttpResponse& response); + + bool validateDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings, QJsonObject& jsonObject, QStringList& deviceSettingsKeys); + + void appendSettingsSubKeys( + const QJsonObject& parentSettingsJsonObject, + QJsonObject& childSettingsJsonObject, + const QString& parentKey, + QStringList& keyList); + + bool parseJsonBody(QString& jsonStr, QJsonObject& jsonObject, qtwebapp::HttpResponse& response); + + void resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings); + void resetDeviceReport(SWGSDRangel::SWGDeviceReport& deviceReport); +}; + +} // namespace SDRdaemon + +#endif /* SDRDAEMON_WEBAPI_WEBAPIREQUESTMAPPER_H_ */ diff --git a/sdrdaemon/webapi/webapiserver.cpp b/sdrdaemon/webapi/webapiserver.cpp new file mode 100644 index 000000000..b76993e61 --- /dev/null +++ b/sdrdaemon/webapi/webapiserver.cpp @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 "httplistener.h" +#include "webapirequestmapper.h" +#include "webapiserver.h" + +namespace SDRDaemon { + +WebAPIServer::WebAPIServer(const QString& host, uint16_t port, WebAPIRequestMapper *requestMapper) : + m_requestMapper(requestMapper), + m_listener(0) +{ + m_settings.host = host; + m_settings.port = port; +} + +WebAPIServer::~WebAPIServer() +{ + if (m_listener) { delete m_listener; } +} + +void WebAPIServer::start() +{ + if (!m_listener) + { + m_listener = new qtwebapp::HttpListener(m_settings, m_requestMapper, qApp); + qInfo("WebAPIServer::start: starting web API server at http://%s:%d", qPrintable(m_settings.host), m_settings.port); + } +} + +void WebAPIServer::stop() +{ + if (m_listener) + { + delete m_listener; + m_listener = 0; + qInfo("WebAPIServer::stop: stopped web API server at http://%s:%d", qPrintable(m_settings.host), m_settings.port); + } +} + +void WebAPIServer::setHostAndPort(const QString& host, uint16_t port) +{ + stop(); + m_settings.host = host; + m_settings.port = port; + m_listener = new qtwebapp::HttpListener(m_settings, m_requestMapper, qApp); +} + +} // namespace SDRdaemon diff --git a/sdrdaemon/webapi/webapiserver.h b/sdrdaemon/webapi/webapiserver.h new file mode 100644 index 000000000..a337412e6 --- /dev/null +++ b/sdrdaemon/webapi/webapiserver.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB. // +// // +// SDRdaemon Swagger server adapter interface // +// // +// 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 // +// // +// 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 SDRDAEMON_WEBAPI_WEBAPISERVER_H_ +#define SDRDAEMON_WEBAPI_WEBAPISERVER_H_ + +#include +#include "export.h" + +namespace qtwebapp +{ + class HttpListener; + class HttpListenerSettings; +} + +namespace SDRDaemon { + +class WebAPIRequestMapper; + +class SDRBASE_API WebAPIServer +{ +public: + WebAPIServer(const QString& host, uint16_t port, WebAPIRequestMapper *requestMapper); + ~WebAPIServer(); + + void start(); + void stop(); + + void setHostAndPort(const QString& host, uint16_t port); + const QString& getHost() const { return m_settings.host; } + int getPort() const { return m_settings.port; } + +private: + WebAPIRequestMapper *m_requestMapper; + qtwebapp::HttpListener *m_listener; + qtwebapp::HttpListenerSettings m_settings; +}; + +} // namespace SDRdaemon + +#endif /* SDRDAEMON_WEBAPI_WEBAPISERVER_H_ */