From 11042dc8898276b7466c4d4ab2dd8902538e33ad Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 5 Sep 2023 16:38:15 +0100 Subject: [PATCH 1/2] Add command line options to start Remote TCP Sink on a specified device --- app/main.cpp | 22 +++- appsrv/main.cpp | 50 +++++--- sdrbase/CMakeLists.txt | 2 + sdrbase/mainparser.cpp | 66 ++++++++-- sdrbase/mainparser.h | 18 +++ sdrbase/remotetcpsinkstarter.cpp | 200 +++++++++++++++++++++++++++++++ sdrbase/remotetcpsinkstarter.h | 32 +++++ 7 files changed, 365 insertions(+), 25 deletions(-) create mode 100644 sdrbase/remotetcpsinkstarter.cpp create mode 100644 sdrbase/remotetcpsinkstarter.h diff --git a/app/main.cpp b/app/main.cpp index f8211904a..cb6a2f310 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -36,6 +36,7 @@ #include "loggerwithfile.h" #include "mainwindow.h" +#include "remotetcpsinkstarter.h" #include "dsp/dsptypes.h" static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *logger) @@ -74,7 +75,6 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo if (settings.contains(uiScaleFactor)) { QString scaleFactor = settings.value(uiScaleFactor).toString(); - qDebug() << "Setting QT_SCALE_FACTOR to" << scaleFactor; qputenv("QT_SCALE_FACTOR", scaleFactor.toLatin1()); } @@ -178,7 +178,27 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo applicationPid); #endif + if (parser.getListDevices()) + { + // Disable log on console, so we can more easily see device list + logger->setConsoleMinMessageLevel(QtFatalMsg); + // Don't pass logger to MainWindow, otherwise it can reenable log output + logger = nullptr; + } + MainWindow w(logger, parser); + + if (parser.getListDevices()) + { + // List available physical devices and exit + RemoteTCPSinkStarter::listAvailableDevices(); + exit (EXIT_SUCCESS); + } + + if (parser.getRemoteTCPSink()) { + RemoteTCPSinkStarter::start(parser); + } + w.show(); return a.exec(); diff --git a/appsrv/main.cpp b/appsrv/main.cpp index d94df1c3f..46ce93378 100644 --- a/appsrv/main.cpp +++ b/appsrv/main.cpp @@ -1,8 +1,6 @@ /////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2017 Edouard Griffiths, F4EXB. // // // -// 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 // @@ -26,6 +24,7 @@ #include "loggerwithfile.h" #include "mainparser.h" #include "mainserver.h" +#include "remotetcpsinkstarter.h" #include "dsp/dsptypes.h" void handler(int sig) { @@ -92,20 +91,39 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo QCoreApplication::applicationPid()); #endif - MainServer m(logger, parser, &a); + if (parser.getListDevices()) + { + // Disable log on console, so we can more easily see device list + logger->setConsoleMinMessageLevel(QtFatalMsg); + // Don't pass logger to MainWindow, otherwise it can reenable log output + logger = nullptr; + } - // This will cause the application to exit when the main core is finished - QObject::connect(&m, SIGNAL(finished()), &a, SLOT(quit())); + MainServer m(logger, parser, &a); - return a.exec(); - } + // This will cause the application to exit when the main core is finished + QObject::connect(&m, SIGNAL(finished()), &a, SLOT(quit())); - int main(int argc, char* argv[]) - { - qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp); - logger->installMsgHandler(); - int res = runQtApplication(argc, argv, logger); - delete logger; - qWarning("SDRangel quit."); - return res; - } + if (parser.getListDevices()) + { + // List available physical devices and exit + RemoteTCPSinkStarter::listAvailableDevices(); + exit (EXIT_SUCCESS); + } + + if (parser.getRemoteTCPSink()) { + RemoteTCPSinkStarter::start(parser); + } + + return a.exec(); +} + +int main(int argc, char* argv[]) +{ + qtwebapp::LoggerWithFile *logger = new qtwebapp::LoggerWithFile(qApp); + logger->installMsgHandler(); + int res = runQtApplication(argc, argv, logger); + delete logger; + qWarning("SDRangel quit."); + return res; +} diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 5b640f7bb..63f0ed392 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -285,6 +285,7 @@ set(sdrbase_SOURCES mainparser.cpp maincore.cpp + remotetcpsinkstarter.cpp resources/webapi.qrc ) @@ -519,6 +520,7 @@ set(sdrbase_HEADERS mainparser.h maincore.h + remotetcpsinkstarter.h ) include_directories( diff --git a/sdrbase/mainparser.cpp b/sdrbase/mainparser.cpp index 6ab22f2ce..dfb0116bd 100644 --- a/sdrbase/mainparser.cpp +++ b/sdrbase/mainparser.cpp @@ -36,7 +36,13 @@ MainParser::MainParser() : "file", ""), m_scratchOption("scratch", "Start from scratch (no current config)."), - m_soapyOption("soapy", "Activate Soapy SDR support.") + m_soapyOption("soapy", "Activate Soapy SDR support."), + m_remoteTCPSinkOption("remote-tcp", "Start Remote TCP Sink"), + m_remoteTCPSinkAddressOption("remote-tcp-address", "Remote TCP Sink interface IP address (Default 127.0.0.1).", "address", "127.0.0.1"), + m_remoteTCPSinkPortOption("remote-tcp-port", "Remote TCP Sink port (Default 1234).", "port", "1234"), + m_remoteTCPSinkHWTypeOption("remote-tcp-hwtype", "Remote TCP Sink device hardware type (Optional. E.g. RTLSDR/SDRplayV3/AirspyHF).", "hwtype"), + m_remoteTCPSinkSerialOption("remote-tcp-serial", "Remote TCP Sink device serial (Optional).", "serial"), + m_listDevicesOption("list-devices", "List available physical devices.") { m_serverAddress = ""; // Bind to any address @@ -44,6 +50,12 @@ MainParser::MainParser() : m_scratch = false; m_soapy = false; m_fftwfWindowFileName = ""; + m_remoteTCPSink = false; + m_remoteTCPSinkAddress = "127.0.0.1"; + m_remoteTCPSinkPort = 1234; + m_remoteTCPSinkHWType = ""; + m_remoteTCPSinkSerial = ""; + m_listDevices = false; m_parser.setApplicationDescription("Software Defined Radio application"); m_parser.addHelpOption(); @@ -54,6 +66,12 @@ MainParser::MainParser() : m_parser.addOption(m_fftwfWisdomOption); m_parser.addOption(m_scratchOption); m_parser.addOption(m_soapyOption); + m_parser.addOption(m_remoteTCPSinkOption); + m_parser.addOption(m_remoteTCPSinkAddressOption); + m_parser.addOption(m_remoteTCPSinkPortOption); + m_parser.addOption(m_remoteTCPSinkHWTypeOption); + m_parser.addOption(m_remoteTCPSinkSerialOption); + m_parser.addOption(m_listDevicesOption); } MainParser::~MainParser() @@ -65,19 +83,18 @@ void MainParser::parse(const QCoreApplication& app) int pos; bool ok; + QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"; + QRegularExpression ipRegex ("^" + ipRange + + "\\." + ipRange + + "\\." + ipRange + + "\\." + ipRange + "$"); + QRegularExpressionValidator ipValidator(ipRegex); // server address QString serverAddress = m_parser.value(m_serverAddressOption); if (!serverAddress.isEmpty()) { - QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"; - QRegularExpression ipRegex ("^" + ipRange - + "\\." + ipRange - + "\\." + ipRange - + "\\." + ipRange + "$"); - QRegularExpressionValidator ipValidator(ipRegex); - if (ipValidator.validate(serverAddress, pos) == QValidator::Acceptable) { m_serverAddress = serverAddress; } else { @@ -104,4 +121,37 @@ void MainParser::parse(const QCoreApplication& app) // Soapy SDR support m_soapy = m_parser.isSet(m_soapyOption); + + // Remote TCP Sink options + + m_remoteTCPSink = m_parser.isSet(m_remoteTCPSinkOption); + + QString remoteTCPSinkAddress = m_parser.value(m_remoteTCPSinkAddressOption); + if (!remoteTCPSinkAddress.isEmpty()) + { + if (ipValidator.validate(remoteTCPSinkAddress, pos) == QValidator::Acceptable) { + m_remoteTCPSinkAddress = remoteTCPSinkAddress; + } else { + qWarning() << "MainParser::parse: remote TCP Sink address invalid. Defaulting to " << m_remoteTCPSinkAddress; + } + } + + QString remoteTCPSinkPortStr = m_parser.value(m_remoteTCPSinkPortOption); + int remoteTCPSinkPort = remoteTCPSinkPortStr.toInt(&ok); + + if (ok && (remoteTCPSinkPort > 1023) && (remoteTCPSinkPort < 65536)) { + m_remoteTCPSinkPort = remoteTCPSinkPort; + } else { + qWarning() << "MainParser::parse: remote TCP Sink port invalid. Defaulting to " << m_serverPort; + } + + m_remoteTCPSinkHWType = m_parser.value(m_remoteTCPSinkHWTypeOption); + m_remoteTCPSinkSerial = m_parser.value(m_remoteTCPSinkSerialOption); + m_listDevices = m_parser.isSet(m_listDevicesOption); + + if (m_remoteTCPSink && m_remoteTCPSinkHWType.isEmpty() && m_remoteTCPSinkSerial.isEmpty()) + { + qCritical() << "You must specify a device with either -remote-tcp-hwtype or -remote-tcp-serial"; + exit (EXIT_FAILURE); + } } diff --git a/sdrbase/mainparser.h b/sdrbase/mainparser.h index f472d2733..fb3705434 100644 --- a/sdrbase/mainparser.h +++ b/sdrbase/mainparser.h @@ -37,6 +37,12 @@ public: bool getScratch() const { return m_scratch; } bool getSoapy() const { return m_soapy; } const QString& getFFTWFWisdomFileName() const { return m_fftwfWindowFileName; } + bool getRemoteTCPSink() const { return m_remoteTCPSink; } + const QString& getRemoteTCPSinkAddressOption() const { return m_remoteTCPSinkAddress; } + const int getRemoteTCPSinkPortOption() const { return m_remoteTCPSinkPort; } + const QString& getRemoteTCPSinkHWType() const { return m_remoteTCPSinkHWType; } + const QString& getRemoteTCPSinkSerial() const { return m_remoteTCPSinkSerial; } + const bool getListDevices() const { return m_listDevices; } private: QString m_serverAddress; @@ -44,6 +50,12 @@ private: QString m_fftwfWindowFileName; bool m_scratch; bool m_soapy; + bool m_remoteTCPSink; + QString m_remoteTCPSinkAddress; + int m_remoteTCPSinkPort; + QString m_remoteTCPSinkHWType; + QString m_remoteTCPSinkSerial; + bool m_listDevices; QCommandLineParser m_parser; QCommandLineOption m_serverAddressOption; @@ -51,6 +63,12 @@ private: QCommandLineOption m_fftwfWisdomOption; QCommandLineOption m_scratchOption; QCommandLineOption m_soapyOption; + QCommandLineOption m_remoteTCPSinkOption; + QCommandLineOption m_remoteTCPSinkAddressOption; + QCommandLineOption m_remoteTCPSinkPortOption; + QCommandLineOption m_remoteTCPSinkHWTypeOption; + QCommandLineOption m_remoteTCPSinkSerialOption; + QCommandLineOption m_listDevicesOption; }; diff --git a/sdrbase/remotetcpsinkstarter.cpp b/sdrbase/remotetcpsinkstarter.cpp new file mode 100644 index 000000000..0dd99e1b8 --- /dev/null +++ b/sdrbase/remotetcpsinkstarter.cpp @@ -0,0 +1,200 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 "remotetcpsinkstarter.h" + +#include "maincore.h" +#include "device/deviceset.h" +#include "device/deviceapi.h" +#include "device/deviceenumerator.h" +#include "dsp/devicesamplesource.h" +#include "channel/channelapi.h" +#include "SWGChannelSettings.h" +#include "SWGRemoteTCPSinkSettings.h" +#include "SWGDeviceState.h" + +// Lists available physical devices to stdout +void RemoteTCPSinkStarter::listAvailableDevices() +{ + int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); + + printf("Available devices:\n"); + for (int i = 0; i < nbSamplingDevices; i++) + { + const PluginInterface::SamplingDevice *samplingDevice; + + samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); + if (samplingDevice->type == PluginInterface::SamplingDevice::PhysicalDevice) + { + printf(" HWType: %s", qPrintable(samplingDevice->hardwareId)); + if (!samplingDevice->serial.isEmpty()) { + printf(" Serial: %s", qPrintable(samplingDevice->serial)); + } + printf("\n"); + } + } +} + +// Instantiate specified sampling source device and create a RemoteTCPSink channel +// on the specified address and port and start the device +static void startRemoteTCPSink(const QString& address, int port, const QString& hwType, const QString& serial) +{ + MainCore *mainCore = MainCore::instance(); + + // Delete any existing device sets, in case requested device is already in use + int initialDeviceSets = mainCore->getDeviceSets().size(); + for (int i = 0; i < initialDeviceSets; i++) + { + MainCore::MsgRemoveLastDeviceSet *msg = MainCore::MsgRemoveLastDeviceSet::create(); + mainCore->getMainMessageQueue()->push(msg); + } + + // Wait until they've been deleted + if (initialDeviceSets > 0) + { + do + { + QTime dieTime = QTime::currentTime().addMSecs(100); + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } + } + while (mainCore->getDeviceSets().size() > 0); + } + + // Create DeviceSet + int deviceSetIndex = mainCore->getDeviceSets().size(); + MainCore::MsgAddDeviceSet *msg = MainCore::MsgAddDeviceSet::create(0); + mainCore->getMainMessageQueue()->push(msg); + + // Switch to requested device type + int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); + bool found = false; + for (int i = 0; i < nbSamplingDevices; i++) + { + const PluginInterface::SamplingDevice *samplingDevice; + + samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); + + if (!hwType.isEmpty() && (hwType != samplingDevice->hardwareId)) { + continue; + } + if (!serial.isEmpty() && (serial != samplingDevice->serial)) { + continue; + } + + int direction = 0; + MainCore::MsgSetDevice *msg = MainCore::MsgSetDevice::create(deviceSetIndex, i, direction); + mainCore->getMainMessageQueue()->push(msg); + found = true; + break; + } + if (!found) + { + qCritical() << "startRemoteTCPSink: Failed to find device"; + return; + } + + // Add RemoteTCPSink channel + PluginAPI::ChannelRegistrations *channelRegistrations = mainCore->getPluginManager()->getRxChannelRegistrations(); + int nbRegistrations = channelRegistrations->size(); + int index = 0; + for (; index < nbRegistrations; index++) + { + if (channelRegistrations->at(index).m_channelId == "RemoteTCPSink") { + break; + } + } + + if (index < nbRegistrations) + { + MainCore::MsgAddChannel *msg = MainCore::MsgAddChannel::create(deviceSetIndex, index, 0); + mainCore->getMainMessageQueue()->push(msg); + } + else + { + qCritical() << "startRemoteTCPSink: RemoteTCPSink is not available"; + return; + } + int channelIndex = 0; + + // Wait until device & channel are created - is there a better way? + DeviceSet *deviceSet = nullptr; + ChannelAPI *channelAPI = nullptr; + do + { + QTime dieTime = QTime::currentTime().addMSecs(100); + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } + + if (mainCore->getDeviceSets().size() > deviceSetIndex) + { + deviceSet = mainCore->getDeviceSets()[deviceSetIndex]; + if (deviceSet) { + channelAPI = deviceSet->m_deviceAPI->getChanelSinkAPIAt(channelIndex); + } + } + } + while (channelAPI == nullptr); + + // Set TCP settings + QStringList channelSettingsKeys = {"dataAddress", "dataPort"}; + SWGSDRangel::SWGChannelSettings response; + response.init(); + SWGSDRangel::SWGRemoteTCPSinkSettings *sinkSettings = response.getRemoteTcpSinkSettings(); + sinkSettings->setDataAddress(new QString(address)); + sinkSettings->setDataPort(port); + QString errorMessage; + channelAPI->webapiSettingsPutPatch(false, channelSettingsKeys, response, errorMessage); + + // Wait some time for settings to be applied + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + + // Start the device (use WebAPI so GUI is updated) + DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource(); + QStringList deviceActionsKeys; + SWGSDRangel::SWGDeviceState state; + state.init(); + int res = source->webapiRun(true, state, errorMessage); + if (res != 200) { + qCritical() << "startRemoteTCPSink: Failed to start device: " << res; + } else { + qInfo().nospace().noquote() << "Remote TCP Sink started on " << address << ":" << port; + } +} + +// Start Remote TCP Sink on specified device, with specified address and port +void RemoteTCPSinkStarter::start(const MainParser& parser) +{ + QString remoteTCPSinkAddress = parser.getRemoteTCPSinkAddressOption(); + int remoteTCPSinkPort = parser.getRemoteTCPSinkPortOption(); + QString remoteTCPSinkHWType = parser.getRemoteTCPSinkHWType(); + QString remoteTCPSinkSerial = parser.getRemoteTCPSinkSerial(); + + QTimer::singleShot(250, [=] { + startRemoteTCPSink( + remoteTCPSinkAddress, + remoteTCPSinkPort, + remoteTCPSinkHWType, + remoteTCPSinkSerial); + }); +} diff --git a/sdrbase/remotetcpsinkstarter.h b/sdrbase/remotetcpsinkstarter.h new file mode 100644 index 000000000..8b723b6ea --- /dev/null +++ b/sdrbase/remotetcpsinkstarter.h @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 REMOTETCPSINKSTARTER_H +#define REMOTETCPSINKSTARTER_H + +#include "mainparser.h" +#include "export.h" + +class SDRBASE_API RemoteTCPSinkStarter { + +public: + static void listAvailableDevices(); + static void start(const MainParser& parser); + +}; + +#endif /* REMOTETCPSINKSTARTER_H */ From 60c55970d382857ff0a3b51b1c339e25ab156210 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Tue, 5 Sep 2023 17:14:09 +0100 Subject: [PATCH 2/2] Fix warnings and formatting --- appsrv/main.cpp | 2 +- sdrbase/mainparser.h | 4 +- sdrbase/remotetcpsinkstarter.cpp | 400 +++++++++++++++---------------- sdrbase/remotetcpsinkstarter.h | 64 ++--- 4 files changed, 235 insertions(+), 235 deletions(-) diff --git a/appsrv/main.cpp b/appsrv/main.cpp index 46ce93378..4f3068484 100644 --- a/appsrv/main.cpp +++ b/appsrv/main.cpp @@ -95,7 +95,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo { // Disable log on console, so we can more easily see device list logger->setConsoleMinMessageLevel(QtFatalMsg); - // Don't pass logger to MainWindow, otherwise it can reenable log output + // Don't pass logger to MainServer, otherwise it can reenable log output logger = nullptr; } diff --git a/sdrbase/mainparser.h b/sdrbase/mainparser.h index fb3705434..73daa59b7 100644 --- a/sdrbase/mainparser.h +++ b/sdrbase/mainparser.h @@ -39,10 +39,10 @@ public: const QString& getFFTWFWisdomFileName() const { return m_fftwfWindowFileName; } bool getRemoteTCPSink() const { return m_remoteTCPSink; } const QString& getRemoteTCPSinkAddressOption() const { return m_remoteTCPSinkAddress; } - const int getRemoteTCPSinkPortOption() const { return m_remoteTCPSinkPort; } + int getRemoteTCPSinkPortOption() const { return m_remoteTCPSinkPort; } const QString& getRemoteTCPSinkHWType() const { return m_remoteTCPSinkHWType; } const QString& getRemoteTCPSinkSerial() const { return m_remoteTCPSinkSerial; } - const bool getListDevices() const { return m_listDevices; } + bool getListDevices() const { return m_listDevices; } private: QString m_serverAddress; diff --git a/sdrbase/remotetcpsinkstarter.cpp b/sdrbase/remotetcpsinkstarter.cpp index 0dd99e1b8..bcc8b75d8 100644 --- a/sdrbase/remotetcpsinkstarter.cpp +++ b/sdrbase/remotetcpsinkstarter.cpp @@ -1,200 +1,200 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2023 Jon Beniston, M7RCE // -// // -// 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 "remotetcpsinkstarter.h" - -#include "maincore.h" -#include "device/deviceset.h" -#include "device/deviceapi.h" -#include "device/deviceenumerator.h" -#include "dsp/devicesamplesource.h" -#include "channel/channelapi.h" -#include "SWGChannelSettings.h" -#include "SWGRemoteTCPSinkSettings.h" -#include "SWGDeviceState.h" - -// Lists available physical devices to stdout -void RemoteTCPSinkStarter::listAvailableDevices() -{ - int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); - - printf("Available devices:\n"); - for (int i = 0; i < nbSamplingDevices; i++) - { - const PluginInterface::SamplingDevice *samplingDevice; - - samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); - if (samplingDevice->type == PluginInterface::SamplingDevice::PhysicalDevice) - { - printf(" HWType: %s", qPrintable(samplingDevice->hardwareId)); - if (!samplingDevice->serial.isEmpty()) { - printf(" Serial: %s", qPrintable(samplingDevice->serial)); - } - printf("\n"); - } - } -} - -// Instantiate specified sampling source device and create a RemoteTCPSink channel -// on the specified address and port and start the device -static void startRemoteTCPSink(const QString& address, int port, const QString& hwType, const QString& serial) -{ - MainCore *mainCore = MainCore::instance(); - - // Delete any existing device sets, in case requested device is already in use - int initialDeviceSets = mainCore->getDeviceSets().size(); - for (int i = 0; i < initialDeviceSets; i++) - { - MainCore::MsgRemoveLastDeviceSet *msg = MainCore::MsgRemoveLastDeviceSet::create(); - mainCore->getMainMessageQueue()->push(msg); - } - - // Wait until they've been deleted - if (initialDeviceSets > 0) - { - do - { - QTime dieTime = QTime::currentTime().addMSecs(100); - while (QTime::currentTime() < dieTime) { - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - } - } - while (mainCore->getDeviceSets().size() > 0); - } - - // Create DeviceSet - int deviceSetIndex = mainCore->getDeviceSets().size(); - MainCore::MsgAddDeviceSet *msg = MainCore::MsgAddDeviceSet::create(0); - mainCore->getMainMessageQueue()->push(msg); - - // Switch to requested device type - int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); - bool found = false; - for (int i = 0; i < nbSamplingDevices; i++) - { - const PluginInterface::SamplingDevice *samplingDevice; - - samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); - - if (!hwType.isEmpty() && (hwType != samplingDevice->hardwareId)) { - continue; - } - if (!serial.isEmpty() && (serial != samplingDevice->serial)) { - continue; - } - - int direction = 0; - MainCore::MsgSetDevice *msg = MainCore::MsgSetDevice::create(deviceSetIndex, i, direction); - mainCore->getMainMessageQueue()->push(msg); - found = true; - break; - } - if (!found) - { - qCritical() << "startRemoteTCPSink: Failed to find device"; - return; - } - - // Add RemoteTCPSink channel - PluginAPI::ChannelRegistrations *channelRegistrations = mainCore->getPluginManager()->getRxChannelRegistrations(); - int nbRegistrations = channelRegistrations->size(); - int index = 0; - for (; index < nbRegistrations; index++) - { - if (channelRegistrations->at(index).m_channelId == "RemoteTCPSink") { - break; - } - } - - if (index < nbRegistrations) - { - MainCore::MsgAddChannel *msg = MainCore::MsgAddChannel::create(deviceSetIndex, index, 0); - mainCore->getMainMessageQueue()->push(msg); - } - else - { - qCritical() << "startRemoteTCPSink: RemoteTCPSink is not available"; - return; - } - int channelIndex = 0; - - // Wait until device & channel are created - is there a better way? - DeviceSet *deviceSet = nullptr; - ChannelAPI *channelAPI = nullptr; - do - { - QTime dieTime = QTime::currentTime().addMSecs(100); - while (QTime::currentTime() < dieTime) { - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - } - - if (mainCore->getDeviceSets().size() > deviceSetIndex) - { - deviceSet = mainCore->getDeviceSets()[deviceSetIndex]; - if (deviceSet) { - channelAPI = deviceSet->m_deviceAPI->getChanelSinkAPIAt(channelIndex); - } - } - } - while (channelAPI == nullptr); - - // Set TCP settings - QStringList channelSettingsKeys = {"dataAddress", "dataPort"}; - SWGSDRangel::SWGChannelSettings response; - response.init(); - SWGSDRangel::SWGRemoteTCPSinkSettings *sinkSettings = response.getRemoteTcpSinkSettings(); - sinkSettings->setDataAddress(new QString(address)); - sinkSettings->setDataPort(port); - QString errorMessage; - channelAPI->webapiSettingsPutPatch(false, channelSettingsKeys, response, errorMessage); - - // Wait some time for settings to be applied - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - - // Start the device (use WebAPI so GUI is updated) - DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource(); - QStringList deviceActionsKeys; - SWGSDRangel::SWGDeviceState state; - state.init(); - int res = source->webapiRun(true, state, errorMessage); - if (res != 200) { - qCritical() << "startRemoteTCPSink: Failed to start device: " << res; - } else { - qInfo().nospace().noquote() << "Remote TCP Sink started on " << address << ":" << port; - } -} - -// Start Remote TCP Sink on specified device, with specified address and port -void RemoteTCPSinkStarter::start(const MainParser& parser) -{ - QString remoteTCPSinkAddress = parser.getRemoteTCPSinkAddressOption(); - int remoteTCPSinkPort = parser.getRemoteTCPSinkPortOption(); - QString remoteTCPSinkHWType = parser.getRemoteTCPSinkHWType(); - QString remoteTCPSinkSerial = parser.getRemoteTCPSinkSerial(); - - QTimer::singleShot(250, [=] { - startRemoteTCPSink( - remoteTCPSinkAddress, - remoteTCPSinkPort, - remoteTCPSinkHWType, - remoteTCPSinkSerial); - }); -} +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 "remotetcpsinkstarter.h" + +#include "maincore.h" +#include "device/deviceset.h" +#include "device/deviceapi.h" +#include "device/deviceenumerator.h" +#include "dsp/devicesamplesource.h" +#include "channel/channelapi.h" +#include "SWGChannelSettings.h" +#include "SWGRemoteTCPSinkSettings.h" +#include "SWGDeviceState.h" + +// Lists available physical devices to stdout +void RemoteTCPSinkStarter::listAvailableDevices() +{ + int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); + + printf("Available devices:\n"); + for (int i = 0; i < nbSamplingDevices; i++) + { + const PluginInterface::SamplingDevice *samplingDevice; + + samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); + if (samplingDevice->type == PluginInterface::SamplingDevice::PhysicalDevice) + { + printf(" HWType: %s", qPrintable(samplingDevice->hardwareId)); + if (!samplingDevice->serial.isEmpty()) { + printf(" Serial: %s", qPrintable(samplingDevice->serial)); + } + printf("\n"); + } + } +} + +// Instantiate specified sampling source device and create a RemoteTCPSink channel +// on the specified address and port and start the device +static void startRemoteTCPSink(const QString& address, int port, const QString& hwType, const QString& serial) +{ + MainCore *mainCore = MainCore::instance(); + + // Delete any existing device sets, in case requested device is already in use + int initialDeviceSets = mainCore->getDeviceSets().size(); + for (int i = 0; i < initialDeviceSets; i++) + { + MainCore::MsgRemoveLastDeviceSet *msg = MainCore::MsgRemoveLastDeviceSet::create(); + mainCore->getMainMessageQueue()->push(msg); + } + + // Wait until they've been deleted + if (initialDeviceSets > 0) + { + do + { + QTime dieTime = QTime::currentTime().addMSecs(100); + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } + } + while (mainCore->getDeviceSets().size() > 0); + } + + // Create DeviceSet + unsigned int deviceSetIndex = mainCore->getDeviceSets().size(); + MainCore::MsgAddDeviceSet *msg = MainCore::MsgAddDeviceSet::create(0); + mainCore->getMainMessageQueue()->push(msg); + + // Switch to requested device type + int nbSamplingDevices = DeviceEnumerator::instance()->getNbRxSamplingDevices(); + bool found = false; + for (int i = 0; i < nbSamplingDevices; i++) + { + const PluginInterface::SamplingDevice *samplingDevice; + + samplingDevice = DeviceEnumerator::instance()->getRxSamplingDevice(i); + + if (!hwType.isEmpty() && (hwType != samplingDevice->hardwareId)) { + continue; + } + if (!serial.isEmpty() && (serial != samplingDevice->serial)) { + continue; + } + + int direction = 0; + MainCore::MsgSetDevice *msg = MainCore::MsgSetDevice::create(deviceSetIndex, i, direction); + mainCore->getMainMessageQueue()->push(msg); + found = true; + break; + } + if (!found) + { + qCritical() << "startRemoteTCPSink: Failed to find device"; + return; + } + + // Add RemoteTCPSink channel + PluginAPI::ChannelRegistrations *channelRegistrations = mainCore->getPluginManager()->getRxChannelRegistrations(); + int nbRegistrations = channelRegistrations->size(); + int index = 0; + for (; index < nbRegistrations; index++) + { + if (channelRegistrations->at(index).m_channelId == "RemoteTCPSink") { + break; + } + } + + if (index < nbRegistrations) + { + MainCore::MsgAddChannel *msg = MainCore::MsgAddChannel::create(deviceSetIndex, index, 0); + mainCore->getMainMessageQueue()->push(msg); + } + else + { + qCritical() << "startRemoteTCPSink: RemoteTCPSink is not available"; + return; + } + int channelIndex = 0; + + // Wait until device & channel are created - is there a better way? + DeviceSet *deviceSet = nullptr; + ChannelAPI *channelAPI = nullptr; + do + { + QTime dieTime = QTime::currentTime().addMSecs(100); + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } + + if (mainCore->getDeviceSets().size() > deviceSetIndex) + { + deviceSet = mainCore->getDeviceSets()[deviceSetIndex]; + if (deviceSet) { + channelAPI = deviceSet->m_deviceAPI->getChanelSinkAPIAt(channelIndex); + } + } + } + while (channelAPI == nullptr); + + // Set TCP settings + QStringList channelSettingsKeys = {"dataAddress", "dataPort"}; + SWGSDRangel::SWGChannelSettings response; + response.init(); + SWGSDRangel::SWGRemoteTCPSinkSettings *sinkSettings = response.getRemoteTcpSinkSettings(); + sinkSettings->setDataAddress(new QString(address)); + sinkSettings->setDataPort(port); + QString errorMessage; + channelAPI->webapiSettingsPutPatch(false, channelSettingsKeys, response, errorMessage); + + // Wait some time for settings to be applied + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + + // Start the device (use WebAPI so GUI is updated) + DeviceSampleSource *source = deviceSet->m_deviceAPI->getSampleSource(); + QStringList deviceActionsKeys; + SWGSDRangel::SWGDeviceState state; + state.init(); + int res = source->webapiRun(true, state, errorMessage); + if (res != 200) { + qCritical() << "startRemoteTCPSink: Failed to start device: " << res; + } else { + qInfo().nospace().noquote() << "Remote TCP Sink started on " << address << ":" << port; + } +} + +// Start Remote TCP Sink on specified device, with specified address and port +void RemoteTCPSinkStarter::start(const MainParser& parser) +{ + QString remoteTCPSinkAddress = parser.getRemoteTCPSinkAddressOption(); + int remoteTCPSinkPort = parser.getRemoteTCPSinkPortOption(); + QString remoteTCPSinkHWType = parser.getRemoteTCPSinkHWType(); + QString remoteTCPSinkSerial = parser.getRemoteTCPSinkSerial(); + + QTimer::singleShot(250, [=] { + startRemoteTCPSink( + remoteTCPSinkAddress, + remoteTCPSinkPort, + remoteTCPSinkHWType, + remoteTCPSinkSerial); + }); +} diff --git a/sdrbase/remotetcpsinkstarter.h b/sdrbase/remotetcpsinkstarter.h index 8b723b6ea..b57d64f2e 100644 --- a/sdrbase/remotetcpsinkstarter.h +++ b/sdrbase/remotetcpsinkstarter.h @@ -1,32 +1,32 @@ -/////////////////////////////////////////////////////////////////////////////////// -// Copyright (C) 2023 Jon Beniston, M7RCE // -// // -// 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 REMOTETCPSINKSTARTER_H -#define REMOTETCPSINKSTARTER_H - -#include "mainparser.h" -#include "export.h" - -class SDRBASE_API RemoteTCPSinkStarter { - -public: - static void listAvailableDevices(); - static void start(const MainParser& parser); - -}; - -#endif /* REMOTETCPSINKSTARTER_H */ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 REMOTETCPSINKSTARTER_H +#define REMOTETCPSINKSTARTER_H + +#include "mainparser.h" +#include "export.h" + +class SDRBASE_API RemoteTCPSinkStarter { + +public: + static void listAvailableDevices(); + static void start(const MainParser& parser); + +}; + +#endif /* REMOTETCPSINKSTARTER_H */