New SCPI command: load/save setup files

This commit is contained in:
Jan Käberich 2023-07-21 13:43:38 +02:00
parent a697f65de6
commit 94482fe151
6 changed files with 54 additions and 10 deletions

View File

@ -251,6 +251,22 @@ This section contains general device commands, available regardless of the curre
VNA
\end{example}
\subsubsection{DEVice:SETUP:SAVE}
\event{Saves the GUI setup to a file}{DEVice:SETUP:SAVE}{<filename>}
Important points when saving/loading setup files through SCPI commands:
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item If the LibreVNA-GUI (and thus also the SCPI server) is running on a different machine than the SCPI client, the setup files will be saved/loaded from the machine that runs the GUI.
\item If no (or a wrong) file ending is specified, ``.setup'' is automatically added to the filename.
\end{itemize}
\subsubsection{DEVice:SETUP:LOAD}
\query{Loads a setup file}{DEVice:SETUP:LOAD?}{<filename>}{TRUE or FALSE}
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item The filename must include the file ending ``.setup''.
\end{itemize}
\subsubsection{DEVice:REFerence:OUT}
\event{Sets the reference output frequency}{DEVice:REFerence:OUT <freq>}{<freq> in MHz, either 0 (disabled), 10 or 100}
\query{Queries the reference output frequency}{DEVice:REFerence:OUT?}{None}{Output frequency in MHz}
@ -563,7 +579,6 @@ Any number of measurements can be specified (by their number). These measurement
Important points when saving/loading calibration files through SCPI commands:
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item SCPI parsing implicitly capitalizes all commands, the file will be saved using only uppercase letters. Similarly, it is not possible to load a file whose filename contains lowercase characters.
\item If the LibreVNA-GUI (and thus also the SCPI server) is running on a different machine than the SCPI client, the calibration files will be saved/loaded from the machine that runs the GUI.
\end{itemize}

View File

@ -521,6 +521,27 @@ void AppWindow::SetupSCPI()
ret.chop(1);
return ret;
}));
auto scpi_setup = new SCPINode("SETUP");
scpi_dev->add(scpi_setup);
scpi_setup->add(new SCPICommand("SAVE", [=](QStringList params) -> QString {
if(params.size() != 1) {
// no filename given
return SCPI::getResultName(SCPI::Result::Error);
}
SaveSetup(params[0]);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr, false));
scpi_setup->add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString {
if(params.size() != 1) {
// no filename given
return SCPI::getResultName(SCPI::Result::False);
}
if(!LoadSetup(params[0])) {
// some error when loading the setup file
return SCPI::getResultName(SCPI::Result::False);
}
return SCPI::getResultName(SCPI::Result::True);
}, false));
auto scpi_ref = new SCPINode("REFerence");
scpi_dev->add(scpi_ref);
scpi_ref->add(new SCPICommand("OUT", [=](QStringList params) -> QString {
@ -1185,13 +1206,13 @@ nlohmann::json AppWindow::SaveSetup()
return j;
}
void AppWindow::LoadSetup(QString filename)
bool AppWindow::LoadSetup(QString filename)
{
ifstream file;
file.open(filename.toStdString());
if(!file.is_open()) {
qWarning() << "Unable to open file:" << filename;
return;
return false;
}
nlohmann::json j;
try {
@ -1200,12 +1221,13 @@ void AppWindow::LoadSetup(QString filename)
InformationBox::ShowError("Error", "Failed to parse the setup file (" + QString(e.what()) + ")");
qWarning() << "Parsing of setup file failed: " << e.what();
file.close();
return;
return false;
}
file.close();
LoadSetup(j);
QFileInfo fi(filename);
lSetupName.setText("Setup: "+fi.fileName());
return true;
}
void AppWindow::LoadSetup(nlohmann::json j)

View File

@ -68,7 +68,7 @@ private slots:
void DeviceFlagsUpdated();
void DeviceInfoUpdated();
void SaveSetup(QString filename);
void LoadSetup(QString filename);
bool LoadSetup(QString filename);
private:
nlohmann::json SaveSetup();
void LoadSetup(nlohmann::json j);

View File

@ -105,7 +105,6 @@ void SCPI::input(QString line)
if(cmd[0] == ':') {
cmd.remove(0, 1);
}
cmd = cmd.toUpper();
auto response = lastNode->parse(cmd, lastNode);
emit output(response);
}
@ -274,7 +273,7 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
// have not reached a leaf, find next subnode
auto subnode = cmd.left(splitPos);
for(auto n : subnodes) {
if(SCPI::match(n->name, subnode)) {
if(SCPI::match(n->name, subnode.toUpper())) {
// pass on to next level
return n->parse(cmd.right(cmd.size() - splitPos - 1), lastNode);
}
@ -292,9 +291,14 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
cmd.chop(1);
}
for(auto c : commands) {
if(SCPI::match(c->name(), cmd)) {
if(SCPI::match(c->name(), cmd.toUpper())) {
// save current node in case of non-root for the next command
lastNode = this;
if(c->convertToUppercase()) {
for(auto &p : params) {
p = p.toUpper();
}
}
if(isQuery) {
return c->query(params);
} else {

View File

@ -8,20 +8,23 @@
class SCPICommand {
public:
SCPICommand(QString name, std::function<QString(QStringList)> cmd, std::function<QString(QStringList)> query) :
SCPICommand(QString name, std::function<QString(QStringList)> cmd, std::function<QString(QStringList)> query, bool convertToUppercase = true) :
_name(name),
fn_cmd(cmd),
fn_query(query){}
fn_query(query),
argAlwaysUppercase(convertToUppercase){}
QString execute(QStringList params);
QString query(QStringList params);
QString name() {return _name;}
bool queryable() { return fn_query != nullptr;}
bool executable() { return fn_cmd != nullptr;}
bool convertToUppercase() { return argAlwaysUppercase;}
private:
const QString _name;
std::function<QString(QStringList)> fn_cmd;
std::function<QString(QStringList)> fn_query;
bool argAlwaysUppercase;
};
class SCPINode {