Web API: /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/settings PUT, PATCH for NFM demod and mod

This commit is contained in:
f4exb 2017-12-11 18:18:47 +01:00
parent a513bd62b5
commit e4b65b52b4
18 changed files with 402 additions and 15 deletions

View File

@ -480,3 +480,40 @@ int NFMDemod::webapiSettingsGet(
response.getNfmDemodSettings()->setVolume(m_settings.m_volume);
return 200;
}
int NFMDemod::webapiSettingsPutPatch(
bool force,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage __attribute__((unused)))
{
NFMDemodSettings settings;
settings.m_afBandwidth = response.getNfmDemodSettings()->getAfBandwidth();
settings.m_audioMute = response.getNfmDemodSettings()->getAudioMute() != 0;
settings.m_audioSampleRate = response.getNfmDemodSettings()->getAudioSampleRate();
settings.m_copyAudioToUDP = response.getNfmDemodSettings()->getCopyAudioToUdp() != 0;
settings.m_ctcssIndex = response.getNfmDemodSettings()->getCtcssIndex();
settings.m_ctcssOn = response.getNfmDemodSettings()->getCtcssOn() != 0;
settings.m_deltaSquelch = response.getNfmDemodSettings()->getDeltaSquelch() != 0;
settings.m_fmDeviation = response.getNfmDemodSettings()->getFmDeviation();
settings.m_inputFrequencyOffset = response.getNfmDemodSettings()->getInputFrequencyOffset();
settings.m_inputSampleRate = response.getNfmDemodSettings()->getInputSampleRate();
settings.m_rfBandwidth = response.getNfmDemodSettings()->getRfBandwidth();
settings.m_rgbColor = response.getNfmDemodSettings()->getRgbColor();
settings.m_squelch = response.getNfmDemodSettings()->getSquelch();
settings.m_squelchGate = response.getNfmDemodSettings()->getSquelchGate();
settings.m_title = *response.getNfmDemodSettings()->getTitle();
settings.m_udpAddress = *response.getNfmDemodSettings()->getUdpAddress();
settings.m_udpPort = response.getNfmDemodSettings()->getUdpPort();
settings.m_volume = response.getNfmDemodSettings()->getVolume();
MsgConfigureNFMDemod *msg = MsgConfigureNFMDemod::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureNFMDemod *msgToGUI = MsgConfigureNFMDemod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
return 200;
}

View File

@ -125,6 +125,11 @@ public:
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
const Real *getCtcssToneSet(int& nbTones) const {
nbTones = m_ctcssDetector.getNTones();
return m_ctcssDetector.getToneSet();

View File

@ -72,7 +72,7 @@ bool NFMDemodGUI::deserialize(const QByteArray& data)
}
}
bool NFMDemodGUI::handleMessage(const Message& message __attribute__((unused)))
bool NFMDemodGUI::handleMessage(const Message& message)
{
if (NFMDemod::MsgReportCTCSSFreq::match(message))
{
@ -81,6 +81,15 @@ bool NFMDemodGUI::handleMessage(const Message& message __attribute__((unused)))
//qDebug("NFMDemodGUI::handleMessage: MsgReportCTCSSFreq: %f", report.getFrequency());
return true;
}
else if (NFMDemod::MsgConfigureNFMDemod::match(message))
{
const NFMDemod::MsgConfigureNFMDemod& cfg = (NFMDemod::MsgConfigureNFMDemod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
return false;
}

View File

@ -499,3 +499,52 @@ int NFMMod::webapiSettingsGet(
apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm);
return 200;
}
int NFMMod::webapiSettingsPutPatch(
bool force,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage __attribute__((unused)))
{
NFMModSettings settings;
settings.m_afBandwidth = response.getNfmModSettings()->getAfBandwidth();
settings.m_audioSampleRate = response.getNfmModSettings()->getAudioSampleRate();
settings.m_basebandSampleRate = response.getNfmModSettings()->getBasebandSampleRate();
settings.m_channelMute = response.getNfmModSettings()->getChannelMute() != 0;
settings.m_ctcssIndex = response.getNfmModSettings()->getCtcssIndex();
settings.m_ctcssOn = response.getNfmModSettings()->getCtcssOn() != 0;
settings.m_fmDeviation = response.getNfmModSettings()->getFmDeviation();
settings.m_inputFrequencyOffset = response.getNfmModSettings()->getInputFrequencyOffset();
settings.m_outputSampleRate = response.getNfmModSettings()->getOutputSampleRate();
settings.m_playLoop = response.getNfmModSettings()->getPlayLoop() != 0;
settings.m_rfBandwidth = response.getNfmModSettings()->getRfBandwidth();
settings.m_rgbColor = response.getNfmModSettings()->getRgbColor();
settings.m_title = *response.getNfmModSettings()->getTitle();
settings.m_toneFrequency = response.getNfmModSettings()->getToneFrequency();
settings.m_volumeFactor = response.getNfmModSettings()->getVolumeFactor();
SWGSDRangel::SWGCWKeyerSettings *apiCwKeyerSettings = response.getNfmModSettings()->getCwKeyer();
CWKeyerSettings cwKeyerSettings;
cwKeyerSettings.m_loop = apiCwKeyerSettings->getLoop() != 0;
cwKeyerSettings.m_mode = (CWKeyerSettings::CWMode) apiCwKeyerSettings->getMode();
cwKeyerSettings.m_sampleRate = apiCwKeyerSettings->getSampleRate();
cwKeyerSettings.m_text = *apiCwKeyerSettings->getText();
cwKeyerSettings.m_wpm = apiCwKeyerSettings->getWpm();
m_cwKeyer.setLoop(cwKeyerSettings.m_loop);
m_cwKeyer.setMode(cwKeyerSettings.m_mode);
m_cwKeyer.setSampleRate(cwKeyerSettings.m_sampleRate);
m_cwKeyer.setText(cwKeyerSettings.m_text);
m_cwKeyer.setWPM(cwKeyerSettings.m_wpm);
MsgConfigureNFMMod *msg = MsgConfigureNFMMod::create(settings, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
CWKeyer::MsgConfigureCWKeyer *msgCwKeyer = CWKeyer::MsgConfigureCWKeyer::create(cwKeyerSettings, force);
m_guiMessageQueue->push(msgCwKeyer);
MsgConfigureNFMMod *msgToGUI = MsgConfigureNFMMod::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
return 200;
}

View File

@ -245,6 +245,11 @@ public:
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
double getMagSq() const { return m_magsq; }
CWKeyer *getCWKeyer() { return &m_cwKeyer; }

View File

@ -103,6 +103,21 @@ bool NFMModGUI::handleMessage(const Message& message)
updateWithStreamTime();
return true;
}
else if (NFMMod::MsgConfigureNFMMod::match(message))
{
const NFMMod::MsgConfigureNFMMod& cfg = (NFMMod::MsgConfigureNFMMod&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (CWKeyer::MsgConfigureCWKeyer::match(message))
{
const CWKeyer::MsgConfigureCWKeyer& cfg = (CWKeyer::MsgConfigureCWKeyer&) message;
ui->cwKeyerGUI->displaySettings(cfg.getSettings());
return true;
}
else
{
return false;

View File

@ -22,6 +22,7 @@ CONFIG(MINGW64):LIBHACKRFSRC = "D:\softs\hackrf\host"
INCLUDEPATH += $$PWD
INCLUDEPATH += ../../../sdrbase
INCLUDEPATH += ../../../sdrgui
INCLUDEPATH += ../../../swagger/sdrangel/code/qt5/client
INCLUDEPATH += ../../../devices
!macx:INCLUDEPATH += $$LIBHACKRFSRC
macx:INCLUDEPATH += /opt/local/include
@ -45,6 +46,7 @@ FORMS += hackrfoutputgui.ui
LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui
LIBS += -L../../../swagger/$${build_subdir} -lswagger
!macx:LIBS += -L../../../libhackrf/$${build_subdir} -llibhackrf
macx:LIBS += -L/opt/local/lib -lhackrf
LIBS += -L../../../devices/$${build_subdir} -ldevices

View File

@ -43,6 +43,12 @@ public:
QString& errorMessage)
{ errorMessage = "Not implemented"; return 501; }
virtual int webapiSettingsPutPatch(
bool force __attribute__((unused)),
SWGSDRangel::SWGChannelSettings& response __attribute__((unused)),
QString& errorMessage)
{ errorMessage = "Not implemented"; return 501; }
int getIndexInDeviceSet() const { return m_indexInDeviceSet; }
void setIndexInDeviceSet(int indexInDeviceSet) { m_indexInDeviceSet = indexInDeviceSet; }
uint64_t getUID() const { return m_uid; }

View File

@ -43,6 +43,12 @@ public:
QString& errorMessage)
{ errorMessage = "Not implemented"; return 501; }
virtual int webapiSettingsPutPatch(
bool force __attribute__((unused)),
SWGSDRangel::SWGChannelSettings& response __attribute__((unused)),
QString& errorMessage)
{ errorMessage = "Not implemented"; return 501; }
int getIndexInDeviceSet() const { return m_indexInDeviceSet; }
void setIndexInDeviceSet(int indexInDeviceSet) { m_indexInDeviceSet = indexInDeviceSet; }
uint64_t getUID() const { return m_uid; }

View File

@ -21,6 +21,8 @@
#include "cwkeyer.h"
#include "util/stepfunctions.h"
MESSAGE_CLASS_DEFINITION(CWKeyer::MsgConfigureCWKeyer, Message)
/**
* 0: dot
* 1: dash

View File

@ -22,6 +22,7 @@
#include <QMutex>
#include "util/export.h"
#include "util/message.h"
#include "cwkeyersettings.h"
/**
@ -49,13 +50,28 @@ class SDRANGEL_API CWKeyer : public QObject {
Q_OBJECT
public:
// typedef enum
// {
// CWNone,
// CWText,
// CWDots,
// CWDashes
// } CWMode;
class MsgConfigureCWKeyer : public Message {
MESSAGE_CLASS_DECLARATION
public:
const CWKeyerSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureCWKeyer* create(const CWKeyerSettings& settings, bool force)
{
return new MsgConfigureCWKeyer(settings, force);
}
private:
CWKeyerSettings m_settings;
bool m_force;
MsgConfigureCWKeyer(const CWKeyerSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
typedef enum
{

View File

@ -321,6 +321,18 @@ public:
SWGSDRangel::SWGErrorResponse& error __attribute__((unused)))
{ return 501; }
/**
* Handler of /sdrangel/deviceset/{deviceSetIndex}/channel/{channelIndex}/settings (PUT, PATCH) swagger/sdrangel/code/html2/index.html#api-Default-instanceChannels
* returns the Http status code (default 501: not implemented)
*/
virtual int devicesetChannelSettingsPutPatch(
int deviceSetIndex __attribute__((unused)),
int channelIndex __attribute__((unused)),
bool force __attribute__((unused)),
SWGSDRangel::SWGChannelSettings& response __attribute__((unused)),
SWGSDRangel::SWGErrorResponse& error __attribute__((unused)))
{ return 501; }
static QString instanceSummaryURL;
static QString instanceDevicesURL;
static QString instanceChannelsURL;

View File

@ -891,6 +891,41 @@ void WebAPIRequestMapper::devicesetChannelSettingsService(
response.write(errorResponse.asJson().toUtf8());
}
}
else if ((request.getMethod() == "PUT") || (request.getMethod() == "PATCH"))
{
QString jsonStr = request.getBody();
QJsonObject jsonObject;
if (parseJsonBody(jsonStr, jsonObject, response))
{
SWGSDRangel::SWGChannelSettings normalResponse;
resetChannelSettings(normalResponse);
if (validateChannelSettings(normalResponse, jsonObject))
{
int status = m_adapter->devicesetChannelSettingsPutPatch(
deviceSetIndex,
channelIndex,
(request.getMethod() == "PUT"), // force settings on PUT
normalResponse,
errorResponse);
response.setStatus(status);
if (status == 200) {
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(405,"Invalid HTTP method");
@ -1037,6 +1072,50 @@ bool WebAPIRequestMapper::validateDeviceSettings(SWGSDRangel::SWGDeviceSettings&
}
}
bool WebAPIRequestMapper::validateChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings, QJsonObject& jsonObject)
{
if (jsonObject.contains("tx")) {
channelSettings.setTx(jsonObject["tx"].toInt());
} else {
channelSettings.setTx(0); // assume Rx
}
if (jsonObject.contains("channelType") && jsonObject["channelType"].isString()) {
channelSettings.setChannelType(new QString(jsonObject["channelType"].toString()));
} else {
return false;
}
QString *channelType = channelSettings.getChannelType();
if (*channelType == "NFMDemod")
{
if (channelSettings.getTx() == 0)
{
QJsonObject nfmDemodSettingsJsonObject = jsonObject["nfmDemodSettings"].toObject();
channelSettings.setNfmDemodSettings(new SWGSDRangel::SWGNFMDemodSettings());
channelSettings.getNfmDemodSettings()->fromJsonObject(nfmDemodSettingsJsonObject);
return true;
}
else {
return false;
}
}
else if (*channelType == "NFMMod")
{
if (channelSettings.getTx() != 0)
{
QJsonObject nfmModSettingsJsonObject = jsonObject["nfmModSettings"].toObject();
channelSettings.setNfmModSettings(new SWGSDRangel::SWGNFMModSettings());
channelSettings.getNfmModSettings()->fromJsonObject(nfmModSettingsJsonObject);
return true;
}
else {
return false;
}
}
}
void WebAPIRequestMapper::resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings)
{
deviceSettings.cleanup();

View File

@ -66,6 +66,7 @@ private:
bool validatePresetTransfer(SWGSDRangel::SWGPresetTransfer& presetTransfer);
bool validatePresetIdentifer(SWGSDRangel::SWGPresetIdentifier& presetIdentifier);
bool validateDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings, QJsonObject& jsonObject);
bool validateChannelSettings(SWGSDRangel::SWGChannelSettings& deviceSettings, QJsonObject& jsonObject);
bool parseJsonBody(QString& jsonStr, QJsonObject& jsonObject, qtwebapp::HttpResponse& response);

View File

@ -27,7 +27,8 @@ CWKeyerGUI::CWKeyerGUI(QWidget* parent) :
QWidget(parent),
ui(new Ui::CWKeyerGUI),
m_messageQueue(0),
m_cwKeyer(0)
m_cwKeyer(0),
m_doApplySettings(true)
{
ui->setupUi(this);
}
@ -100,13 +101,13 @@ void CWKeyerGUI::on_cwTextClear_clicked(bool checked __attribute__((unused)))
void CWKeyerGUI::on_cwTextEdit_editingFinished()
{
m_cwKeyer->setText(ui->cwTextEdit->text());
if (m_doApplySettings) { m_cwKeyer->setText(ui->cwTextEdit->text()); }
}
void CWKeyerGUI::on_cwSpeed_valueChanged(int value)
{
ui->cwSpeedText->setText(QString("%1").arg(value));
m_cwKeyer->setWPM(value);
if (m_doApplySettings) { m_cwKeyer->setWPM(value); }
}
void CWKeyerGUI::on_playDots_toggled(bool checked)
@ -115,7 +116,7 @@ void CWKeyerGUI::on_playDots_toggled(bool checked)
ui->playDashes->setEnabled(!checked);
ui->playText->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDots : CWKeyerSettings::CWNone);
if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDots : CWKeyerSettings::CWNone); }
}
void CWKeyerGUI::on_playDashes_toggled(bool checked)
@ -124,7 +125,7 @@ void CWKeyerGUI::on_playDashes_toggled(bool checked)
//ui->playDashes->setEnabled(!checked);
ui->playText->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDashes : CWKeyerSettings::CWNone);
if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWDashes : CWKeyerSettings::CWNone); }
}
void CWKeyerGUI::on_playText_toggled(bool checked)
@ -133,7 +134,7 @@ void CWKeyerGUI::on_playText_toggled(bool checked)
ui->playDashes->setEnabled(!checked);
//ui->playText->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyerSettings::CWText : CWKeyerSettings::CWNone);
if (m_doApplySettings) { m_cwKeyer->setMode(checked ? CWKeyerSettings::CWText : CWKeyerSettings::CWNone); }
if (checked) {
ui->playStop->setChecked(true);
@ -144,7 +145,7 @@ void CWKeyerGUI::on_playText_toggled(bool checked)
void CWKeyerGUI::on_playLoopCW_toggled(bool checked)
{
m_cwKeyer->setLoop(checked);
if (m_doApplySettings) { m_cwKeyer->setLoop(checked); }
}
void CWKeyerGUI::on_playStop_toggled(bool checked)
@ -171,3 +172,46 @@ void CWKeyerGUI::applySettings()
ui->cwSpeedText->setText(QString("%1").arg(value));
m_cwKeyer->setWPM(value);
}
void CWKeyerGUI::displaySettings(const CWKeyerSettings& settings)
{
blockApplySettings(true);
ui->playLoopCW->setChecked(settings.m_loop);
switch (settings.m_mode)
{
case CWKeyerSettings::CWDashes:
ui->playDots->setEnabled(false);
ui->playDashes->setEnabled(true);
ui->playText->setEnabled(false);
break;
case CWKeyerSettings::CWDots:
ui->playDots->setEnabled(true);
ui->playDashes->setEnabled(false);
ui->playText->setEnabled(false);
break;
case CWKeyerSettings::CWText:
ui->playDots->setEnabled(false);
ui->playDashes->setEnabled(false);
ui->playText->setEnabled(true);
break;
case CWKeyerSettings::CWNone:
default:
ui->playDots->setEnabled(false);
ui->playDashes->setEnabled(false);
ui->playText->setEnabled(false);
break;
}
ui->cwTextEdit->setText(settings.m_text);
ui->cwSpeed->setValue(settings.m_wpm);
ui->cwSpeedText->setText(QString("%1").arg(settings.m_wpm));
blockApplySettings(false);
}
void CWKeyerGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}

View File

@ -29,6 +29,7 @@ namespace Ui {
class MessageQueue;
class CWKeyer;
class CWKeyerSettings;
class SDRANGEL_API CWKeyerGUI : public QWidget, public Serializable {
Q_OBJECT
@ -43,13 +44,17 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void displaySettings(const CWKeyerSettings& settings);
private:
Ui::CWKeyerGUI* ui;
MessageQueue* m_messageQueue;
CWKeyer* m_cwKeyer;
bool m_doApplySettings;
void applySettings();
void blockApplySettings(bool block);
private slots:
void on_cwTextClear_clicked(bool checked);

View File

@ -1077,6 +1077,93 @@ int WebAPIAdapterGUI::devicesetChannelSettingsGet(
}
}
int WebAPIAdapterGUI::devicesetChannelSettingsPutPatch(
int deviceSetIndex,
int channelIndex,
bool force,
SWGSDRangel::SWGChannelSettings& response,
SWGSDRangel::SWGErrorResponse& error)
{
if ((deviceSetIndex >= 0) && (deviceSetIndex < (int) m_mainWindow.m_deviceUIs.size()))
{
DeviceUISet *deviceSet = m_mainWindow.m_deviceUIs[deviceSetIndex];
if (deviceSet->m_deviceSourceEngine) // Rx
{
ChannelSinkAPI *channelAPI = deviceSet->m_deviceSourceAPI->getChanelAPIAt(channelIndex);
if (channelAPI == 0)
{
error.init();
*error.getMessage() = QString("There is no channel with index %1").arg(channelIndex);
return 404;
}
else
{
QString channelType;
channelAPI->getIdentifier(channelType);
if (channelType == *response.getChannelType())
{
return channelAPI->webapiSettingsPutPatch(force, response, *error.getMessage());
}
else
{
error.init();
*error.getMessage() = QString("There is no channel type %1 at index %2. Found %3.")
.arg(*response.getChannelType())
.arg(channelIndex)
.arg(channelType);
return 404;
}
}
}
else if (deviceSet->m_deviceSinkEngine) // Tx
{
ChannelSourceAPI *channelAPI = deviceSet->m_deviceSinkAPI->getChanelAPIAt(channelIndex);
if (channelAPI == 0)
{
error.init();
*error.getMessage() = QString("There is no channel with index %1").arg(channelIndex);
return 404;
}
else
{
QString channelType;
channelAPI->getIdentifier(channelType);
if (channelType == *response.getChannelType())
{
return channelAPI->webapiSettingsPutPatch(force, response, *error.getMessage());
}
else
{
error.init();
*error.getMessage() = QString("There is no channel type %1 at index %2. Found %3.")
.arg(*response.getChannelType())
.arg(channelIndex)
.arg(channelType);
return 404;
}
}
}
else
{
*error.getMessage() = QString("DeviceSet error");
return 500;
}
}
else
{
error.init();
*error.getMessage() = QString("There is no device set with index %1").arg(deviceSetIndex);
return 404;
}
}
void WebAPIAdapterGUI::getDeviceSetList(SWGSDRangel::SWGDeviceSetList* deviceSetList)
{
deviceSetList->init();

View File

@ -157,6 +157,13 @@ public:
SWGSDRangel::SWGChannelSettings& response,
SWGSDRangel::SWGErrorResponse& error);
virtual int devicesetChannelSettingsPutPatch(
int deviceSetIndex,
int channelIndex,
bool force,
SWGSDRangel::SWGChannelSettings& response,
SWGSDRangel::SWGErrorResponse& error);
private:
MainWindow& m_mainWindow;