Add SCPI command to read/write the preferences

This commit is contained in:
Jan Käberich 2024-06-20 14:40:06 +02:00
parent aeea13347e
commit 2ba954537b
12 changed files with 160 additions and 49 deletions

View file

@ -272,6 +272,21 @@ Important points when saving/loading setup files through SCPI commands:
206039903350,208939A23350 206039903350,208939A23350
\end{example} \end{example}
\subsubsection{DEVice:PREFerences}
This command provides read/write access to the preferences. The recommended way is usually to change the preferences manually in the GUI. But if for some reason that is not an option, this is also possible through the SCPI server. There is no complete documentation for all available preferences, refer to the source code.
\event{Set a preferences entry}{DEVice:PREFerences <name> <value>}{<name> Name of the preferences entry\\ <value> New value for the preferences entry}
\begin{example}
:DEV:PREF Startup.ConnectToFirstDevice false
\end{example}
Most settings take effect immediately but some (such as changing the port for the SCPI server) are only applied when the preferences are saved. Also see command~\ref{DEV:APPLYPREF}.
\query{Returns a preferences entry}{DEVice:PREFerences? <name>}{<name> Name of the preferences entry}{Current value of the preferences entry}
\subsubsection{DEVice:APPLYPREFerences}
\label{DEV:APPLYPREF}
\event{Permanently stores the preferences after a setting has been changed}{DEVice:APPLYPREFerences}{None}
\subsubsection{DEVice:MODE} \subsubsection{DEVice:MODE}
\event{Switches the device to the specified mode}{DEVice:MODE <mode>}{<mode>:\\ \hspace{1cm} VNA: set to vector analyzer\\ \hspace{1cm} GEN: set to signal generator\\ \hspace{1cm} SA: set to spectrum analyzer} \event{Switches the device to the specified mode}{DEVice:MODE <mode>}{<mode>:\\ \hspace{1cm} VNA: set to vector analyzer\\ \hspace{1cm} GEN: set to signal generator\\ \hspace{1cm} SA: set to spectrum analyzer}
\begin{example} \begin{example}

View file

@ -128,6 +128,7 @@ void Math::DFT::fromJSON(nlohmann::json j)
void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end) void Math::DFT::inputSamplesChanged(unsigned int begin, unsigned int end)
{ {
Q_UNUSED(begin);
Q_UNUSED(end); Q_UNUSED(end);
if(input->rData().size() < 2) { if(input->rData().size() < 2) {
// not enough input data // not enough input data

View file

@ -202,6 +202,7 @@ void TDR::setMode(Mode m)
void TDR::inputSamplesChanged(unsigned int begin, unsigned int end) void TDR::inputSamplesChanged(unsigned int begin, unsigned int end)
{ {
Q_UNUSED(begin);
Q_UNUSED(end); Q_UNUSED(end);
if(input->rData().size() >= 2) { if(input->rData().size() >= 2) {
// trigger calculation in thread // trigger calculation in thread

View file

@ -136,10 +136,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
} }
}; };
connect(ui->Xauto, &QCheckBox::toggled, [this, updateXenableState](bool checked) { connect(ui->Xauto, &QCheckBox::toggled, [this, updateXenableState](bool) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
}); });
connect(ui->XautoDivs, &QCheckBox::toggled, [this, updateXenableState](bool checked) { connect(ui->XautoDivs, &QCheckBox::toggled, [this, updateXenableState](bool) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
}); });
@ -155,7 +155,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
XAxisTypeChanged((int) plot->xAxis.getType()); XAxisTypeChanged((int) plot->xAxis.getType());
connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &XYplotAxisDialog::XAxisTypeChanged); connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &XYplotAxisDialog::XAxisTypeChanged);
connect(ui->Xlog, &QCheckBox::toggled, [=](bool checked){ connect(ui->Xlog, &QCheckBox::toggled, [=](bool){
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs);
}); });

View file

@ -18,7 +18,7 @@ public:
QMetaType mt(destType); QMetaType mt(destType);
mt.construct(ptr, variant.constData()); mt.construct(ptr, variant.constData());
} }
QVariant value() { QVariant value() const {
return QVariant(variant.metaType(), ptr); return QVariant(variant.metaType(), ptr);
} }
void* getPtr(){return ptr;} void* getPtr(){return ptr;}

View file

@ -231,50 +231,8 @@ void AppWindow::SetupMenu()
connect(ui->actionPreferences, &QAction::triggered, [=](){ connect(ui->actionPreferences, &QAction::triggered, [=](){
// save previous SCPI settings in case they change // save previous SCPI settings in case they change
auto &p = Preferences::getInstance(); auto &p = Preferences::getInstance();
auto SCPIenabled = p.SCPIServer.enabled;
auto SCPIport = p.SCPIServer.port;
p.edit(); p.edit();
// store the updated settings preferencesChanged();
p.store();
if(SCPIenabled != p.SCPIServer.enabled || SCPIport != p.SCPIServer.port) {
StopTCPServer();
if(p.SCPIServer.enabled) {
StartTCPServer(p.SCPIServer.port);
}
}
// averaging mode may have changed, update for all relevant modes
for (auto m : modeHandler->getModes())
{
switch (m->getType())
{
case Mode::Type::VNA:
case Mode::Type::SA:
if(p.Acquisition.useMedianAveraging) {
m->setAveragingMode(Averaging::Mode::Median);
}
else {
m->setAveragingMode(Averaging::Mode::Mean);
}
break;
case Mode::Type::SG:
case Mode::Type::Last:
default:
break;
}
}
// acquisition frequencies may have changed, update
// UpdateAcquisitionFrequencies();
auto active = modeHandler->getActiveMode();
if (active)
{
active->updateGraphColors();
if(device) {
active->initializeDevice();
}
}
}); });
connect(ui->actionAbout, &QAction::triggered, [=](){ connect(ui->actionAbout, &QAction::triggered, [=](){
@ -564,6 +522,32 @@ void AppWindow::SetupSCPI()
ret.chop(1); ret.chop(1);
return ret; return ret;
})); }));
scpi_dev->add(new SCPICommand("PREFerences", [=](QStringList params) -> QString {
if(params.size() != 2) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto &p = Preferences::getInstance();
if(p.set(params[0], QVariant(params[1]))) {
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return SCPI::getResultName(SCPI::Result::Error);
}
}, [=](QStringList params) -> QString {
if(params.size() != 1) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto value = Preferences::getInstance().get(params[0]).toString();
if(value.isEmpty()) {
// failed to get setting
return SCPI::getResultName(SCPI::Result::Error);
} else {
return value;
}
}, false));
scpi_dev->add(new SCPICommand("APPLYPREFerences", [=](QStringList) -> QString {
preferencesChanged();
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
auto scpi_setup = new SCPINode("SETUP"); auto scpi_setup = new SCPINode("SETUP");
scpi_dev->add(scpi_setup); scpi_dev->add(scpi_setup);
scpi_setup->add(new SCPICommand("SAVE", [=](QStringList params) -> QString { scpi_setup->add(new SCPICommand("SAVE", [=](QStringList params) -> QString {
@ -1022,6 +1006,50 @@ void AppWindow::StopTCPServer()
server = nullptr; server = nullptr;
} }
void AppWindow::preferencesChanged()
{
auto &p = Preferences::getInstance();
p.store();
if(p.SCPIServer.enabled && !server) {
StartTCPServer(p.SCPIServer.port);
} else if(!p.SCPIServer.enabled && server) {
StopTCPServer();
} else if(server && server->getPort() != p.SCPIServer.port) {
// still enabled but the port changed -> needs to restart the SCPI server
StopTCPServer();
StartTCPServer(p.SCPIServer.port);
}
// averaging mode may have changed, update for all relevant modes
for (auto m : modeHandler->getModes())
{
switch (m->getType())
{
case Mode::Type::VNA:
case Mode::Type::SA:
if(p.Acquisition.useMedianAveraging) {
m->setAveragingMode(Averaging::Mode::Median);
}
else {
m->setAveragingMode(Averaging::Mode::Mean);
}
break;
case Mode::Type::SG:
case Mode::Type::Last:
default:
break;
}
}
auto active = modeHandler->getActiveMode();
if (active)
{
active->updateGraphColors();
if(device) {
active->initializeDevice();
}
}
}
SCPI* AppWindow::getSCPI() SCPI* AppWindow::getSCPI()
{ {
return &scpi; return &scpi;

View file

@ -91,6 +91,9 @@ private:
void StartTCPServer(int port); void StartTCPServer(int port);
void StopTCPServer(); void StopTCPServer();
// Call whenever the preferences have changed. It stores the updated preferences and applies the changes which do not take effect immediately
void preferencesChanged();
QStackedWidget *central; QStackedWidget *central;
struct { struct {

View file

@ -531,6 +531,61 @@ nlohmann::json Preferences::toJSON()
return j; return j;
} }
bool Preferences::set(QString name, QVariant value)
{
QPointerVariant *ptr = nullptr;
for(auto s : descr) {
if(s.name == name) {
ptr = &s.var;
break;
}
}
if(!ptr) {
// check the driver settings
for(auto driver : DeviceDriver::getDrivers()) {
for(auto s : driver->driverSpecificSettings()) {
if(s.name == name) {
ptr = &s.var;
break;
}
}
if(ptr) {
break;
}
}
}
if(ptr) {
try {
ptr->setValue(value);
return true;
} catch (const std::runtime_error&) {
// failed to set variable, likely wrong format for the QVariant
return false;
}
} else {
// not found
return false;
}
}
QVariant Preferences::get(QString name)
{
for(auto &s : descr) {
if(s.name == name) {
return s.var.value();
}
}
for(auto driver : DeviceDriver::getDrivers()) {
for(auto &s : driver->driverSpecificSettings()) {
if(s.name == name) {
return s.var.value();
}
}
}
// not found
return QVariant();
}
void Preferences::nonTrivialParsing() void Preferences::nonTrivialParsing()
{ {

View file

@ -44,6 +44,7 @@ Q_DECLARE_METATYPE(MarkerSymbolStyle);
class Preferences : public Savable { class Preferences : public Savable {
friend class PreferencesDialog;
public: public:
static Preferences& getInstance() { static Preferences& getInstance() {
return instance; return instance;
@ -172,12 +173,16 @@ public:
void fromJSON(nlohmann::json j) override; void fromJSON(nlohmann::json j) override;
nlohmann::json toJSON() override; nlohmann::json toJSON() override;
void nonTrivialParsing(); bool set(QString name, QVariant value);
void nonTrivialWriting(); QVariant get(QString name);
private: private:
Preferences() : Preferences() :
TCPoverride(false) {} TCPoverride(false) {}
void nonTrivialParsing();
void nonTrivialWriting();
static Preferences instance; static Preferences instance;
// TODO remove settings that have been moved to LibreVNADriver // TODO remove settings that have been moved to LibreVNADriver

View file

@ -4,6 +4,7 @@
TCPServer::TCPServer(int port) TCPServer::TCPServer(int port)
{ {
this->port = port;
qInfo() << "Listening on port" << port; qInfo() << "Listening on port" << port;
socket = nullptr; socket = nullptr;
server.listen(QHostAddress::Any, port); server.listen(QHostAddress::Any, port);

View file

@ -11,12 +11,14 @@ class TCPServer : public QObject
public: public:
TCPServer(int port); TCPServer(int port);
int getPort() {return port;}
public slots: public slots:
bool send(QString line); bool send(QString line);
signals: signals:
void received(QString line); void received(QString line);
private: private:
int port;
QTcpServer server; QTcpServer server;
QTcpSocket *socket; QTcpSocket *socket;
}; };