Add start/stop all devices button in toolbar. Add device stateChanged signal. Use in RTL SDR GUI for updating device state

pull/1394/head
Jon Beniston 2022-08-27 10:18:17 +01:00
rodzic d1a4fca49b
commit d404e9f943
15 zmienionych plików z 276 dodań i 56 usunięć

Wyświetl plik

@ -39,8 +39,7 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) :
m_forceSettings(true),
m_settings(),
m_sampleRateMode(true),
m_sampleSource(0),
m_lastEngineState(DeviceAPI::StNotStarted)
m_sampleSource(0)
{
m_deviceUISet = deviceUISet;
setAttribute(Qt::WA_DeleteOnClose, true);
@ -60,8 +59,8 @@ RTLSDRGui::RTLSDRGui(DeviceUISet *deviceUISet, QWidget* parent) :
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
connect(deviceUISet->m_deviceAPI, &DeviceAPI::stateChanged, this, &RTLSDRGui::updateStatus);
updateStatus();
displaySettings();
makeUIConnections();
@ -426,28 +425,23 @@ void RTLSDRGui::updateStatus()
{
int state = m_deviceUISet->m_deviceAPI->state();
if(m_lastEngineState != state)
switch(state)
{
switch(state)
{
case DeviceAPI::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
case DeviceAPI::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
break;
default:
break;
}
}

Wyświetl plik

@ -58,12 +58,10 @@ private:
RTLSDRSettings m_settings;
bool m_sampleRateMode; //!< true: device, false: base band sample rate update mode
QTimer m_updateTimer;
QTimer m_statusTimer;
std::vector<int> m_gains;
RTLSDRInput* m_sampleSource;
int m_sampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_lastEngineState;
MessageQueue m_inputMessageQueue;
void displayGains();

Wyświetl plik

@ -51,6 +51,15 @@ DeviceAPI::DeviceAPI(
m_deviceSinkEngine(deviceSinkEngine),
m_deviceMIMOEngine(deviceMIMOEngine)
{
if (m_deviceSourceEngine) {
QObject::connect(m_deviceSourceEngine, &DSPDeviceSourceEngine::stateChanged, this, &DeviceAPI::engineStateChanged);
}
if (m_deviceSinkEngine) {
QObject::connect(m_deviceSinkEngine, &DSPDeviceSinkEngine::stateChanged, this, &DeviceAPI::engineStateChanged);
}
if (m_deviceMIMOEngine) {
QObject::connect(m_deviceMIMOEngine, &DSPDeviceMIMOEngine::stateChanged, this, &DeviceAPI::engineStateChanged);
}
}
DeviceAPI::~DeviceAPI()
@ -826,3 +835,8 @@ void DeviceAPI::setDeviceSetIndex(int deviceSetIndex)
m_deviceTabIndex = deviceSetIndex;
renumerateChannels();
}
void DeviceAPI::engineStateChanged()
{
emit stateChanged(this);
}

Wyświetl plik

@ -212,5 +212,11 @@ protected:
private:
void renumerateChannels();
private slots:
void engineStateChanged();
signals:
void stateChanged(DeviceAPI *deviceAPI);
};
#endif // SDRBASE_DEVICE_DEVICEAPI_H_

Wyświetl plik

@ -61,11 +61,29 @@ DSPDeviceMIMOEngine::~DSPDeviceMIMOEngine()
wait();
}
void DSPDeviceMIMOEngine::setStateRx(State state)
{
if (m_stateRx != state)
{
m_stateRx = state;
emit stateChanged();
}
}
void DSPDeviceMIMOEngine::setStateTx(State state)
{
if (m_stateTx != state)
{
m_stateTx = state;
emit stateChanged();
}
}
void DSPDeviceMIMOEngine::run()
{
qDebug() << "DSPDeviceMIMOEngine::run";
m_stateRx = StIdle;
m_stateTx = StIdle;
setStateRx(StIdle);
setStateTx(StIdle);
exec();
}
@ -80,8 +98,8 @@ void DSPDeviceMIMOEngine::stop()
qDebug() << "DSPDeviceMIMOEngine::stop";
gotoIdle(0); // Rx
gotoIdle(1); // Tx
m_stateRx = StNotStarted;
m_stateTx = StNotStarted;
setStateRx(StNotStarted);
setStateTx(StNotStarted);
QThread::exit();
}
@ -763,12 +781,12 @@ DSPDeviceMIMOEngine::State DSPDeviceMIMOEngine::gotoError(int subsystemIndex, co
if (subsystemIndex == 0)
{
m_errorMessageRx = errorMessage;
m_stateRx = StError;
setStateRx(StError);
}
else if (subsystemIndex == 1)
{
m_errorMessageTx = errorMessage;
m_stateTx = StError;
setStateTx(StError);
}
return StError;
@ -881,10 +899,10 @@ void DSPDeviceMIMOEngine::handleSynchronousMessages()
if (DSPAcquisitionInit::match(*message))
{
m_stateRx = gotoIdle(0);
setStateRx(gotoIdle(0));
if (m_stateRx == StIdle) {
m_stateRx = gotoInit(0); // State goes ready if init is performed
setStateRx(gotoInit(0)); // State goes ready if init is performed
}
returnState = m_stateRx;
@ -892,22 +910,22 @@ void DSPDeviceMIMOEngine::handleSynchronousMessages()
else if (DSPAcquisitionStart::match(*message))
{
if (m_stateRx == StReady) {
m_stateRx = gotoRunning(0);
setStateRx(gotoRunning(0));
}
returnState = m_stateRx;
}
else if (DSPAcquisitionStop::match(*message))
{
m_stateRx = gotoIdle(0);
setStateRx(gotoIdle(0));
returnState = m_stateRx;
}
else if (DSPGenerationInit::match(*message))
{
m_stateTx = gotoIdle(1);
setStateTx(gotoIdle(1));
if (m_stateTx == StIdle) {
m_stateTx = gotoInit(1); // State goes ready if init is performed
setStateTx(gotoInit(1)); // State goes ready if init is performed
}
returnState = m_stateTx;
@ -915,14 +933,14 @@ void DSPDeviceMIMOEngine::handleSynchronousMessages()
else if (DSPGenerationStart::match(*message))
{
if (m_stateTx == StReady) {
m_stateTx = gotoRunning(1);
setStateTx(gotoRunning(1));
}
returnState = m_stateTx;
}
else if (DSPGenerationStop::match(*message))
{
m_stateTx = gotoIdle(1);
setStateTx(gotoIdle(1));
returnState = m_stateTx;
}
else if (GetMIMODeviceDescription::match(*message))

Wyświetl plik

@ -350,6 +350,8 @@ private:
State gotoInit(int subsystemIndex); //!< Go to the acquisition init state from idle
State gotoRunning(int subsystemIndex); //!< Go to the running state from ready state
State gotoError(int subsystemIndex, const QString& errorMsg); //!< Go to an error state
void setStateRx(State state);
void setStateTx(State state);
void handleSetMIMO(DeviceSampleMIMO* mimo); //!< Manage MIMO device setting
void iqCorrections(SampleVector::iterator begin, SampleVector::iterator end, int isource, bool imbalanceCorrection);
@ -361,6 +363,9 @@ private slots:
void handleDataTxAsync(int streamIndex); //!< Handle data when Tx samples have to be processed asynchronously
void handleSynchronousMessages(); //!< Handle synchronous messages with the thread
void handleInputMessages(); //!< Handle input message queue
signals:
void stateChanged();
};
#endif // SDRBASE_DSP_DSPDEVICEMIMOENGINE_H_

Wyświetl plik

@ -50,10 +50,19 @@ DSPDeviceSinkEngine::~DSPDeviceSinkEngine()
wait();
}
void DSPDeviceSinkEngine::setState(State state)
{
if (m_state != state)
{
m_state = state;
emit stateChanged();
}
}
void DSPDeviceSinkEngine::run()
{
qDebug() << "DSPDeviceSinkEngine::run";
m_state = StIdle;
setState(StIdle);
exec();
}
@ -67,7 +76,7 @@ void DSPDeviceSinkEngine::stop()
{
qDebug() << "DSPDeviceSinkEngine::stop";
gotoIdle();
m_state = StNotStarted;
setState(StNotStarted);
QThread::exit();
// DSPExit cmd;
// m_syncMessenger.sendWait(cmd);
@ -388,7 +397,7 @@ DSPDeviceSinkEngine::State DSPDeviceSinkEngine::gotoError(const QString& errorMe
m_errorMessage = errorMessage;
m_deviceDescription.clear();
m_state = StError;
setState(StError);
return StError;
}
@ -426,21 +435,21 @@ void DSPDeviceSinkEngine::handleSynchronousMessages()
if (DSPGenerationInit::match(*message))
{
m_state = gotoIdle();
setState(gotoIdle());
if(m_state == StIdle) {
m_state = gotoInit(); // State goes ready if init is performed
setState(gotoInit()); // State goes ready if init is performed
}
}
else if (DSPGenerationStart::match(*message))
{
if(m_state == StReady) {
m_state = gotoRunning();
setState(gotoRunning());
}
}
else if (DSPGenerationStop::match(*message))
{
m_state = gotoIdle();
setState(gotoIdle());
}
else if (DSPGetSinkDeviceDescription::match(*message))
{

Wyświetl plik

@ -113,6 +113,7 @@ private:
State gotoInit(); //!< Go to the acquisition init state from idle
State gotoRunning(); //!< Go to the running state from ready state
State gotoError(const QString& errorMsg); //!< Go to an error state
void setState(State state);
void handleSetSink(DeviceSampleSink* sink); //!< Manage sink setting
@ -120,6 +121,9 @@ private slots:
void handleData(); //!< Handle data when samples have to be written to the sample FIFO
void handleInputMessages(); //!< Handle input message queue
void handleSynchronousMessages(); //!< Handle synchronous messages with the thread
signals:
void stateChanged();
};

Wyświetl plik

@ -55,10 +55,19 @@ DSPDeviceSourceEngine::~DSPDeviceSourceEngine()
wait();
}
void DSPDeviceSourceEngine::setState(State state)
{
if (m_state != state)
{
m_state = state;
emit stateChanged();
}
}
void DSPDeviceSourceEngine::run()
{
qDebug() << "DSPDeviceSourceEngine::run";
m_state = StIdle;
setState(StIdle);
exec();
}
@ -72,7 +81,7 @@ void DSPDeviceSourceEngine::stop()
{
qDebug() << "DSPDeviceSourceEngine::stop";
gotoIdle();
m_state = StNotStarted;
setState(StNotStarted);
QThread::exit();
// DSPExit cmd;
// m_syncMessenger.sendWait(cmd);
@ -508,7 +517,7 @@ DSPDeviceSourceEngine::State DSPDeviceSourceEngine::gotoError(const QString& err
m_errorMessage = errorMessage;
m_deviceDescription.clear();
m_state = StError;
setState(StError);
return StError;
}
@ -549,21 +558,21 @@ void DSPDeviceSourceEngine::handleSynchronousMessages()
if (DSPAcquisitionInit::match(*message))
{
m_state = gotoIdle();
setState(gotoIdle());
if(m_state == StIdle) {
m_state = gotoInit(); // State goes ready if init is performed
setState(gotoInit()); // State goes ready if init is performed
}
}
else if (DSPAcquisitionStart::match(*message))
{
if(m_state == StReady) {
m_state = gotoRunning();
setState(gotoRunning());
}
}
else if (DSPAcquisitionStop::match(*message))
{
m_state = gotoIdle();
setState(gotoIdle());
}
else if (DSPGetSourceDeviceDescription::match(*message))
{

Wyświetl plik

@ -134,6 +134,7 @@ private:
State gotoInit(); //!< Go to the acquisition init state from idle
State gotoRunning(); //!< Go to the running state from ready state
State gotoError(const QString& errorMsg); //!< Go to an error state
void setState(State state);
void handleSetSource(DeviceSampleSource* source); //!< Manage source setting
@ -141,6 +142,9 @@ private slots:
void handleData(); //!< Handle data when samples from source FIFO are ready to be processed
void handleInputMessages(); //!< Handle input message queue
void handleSynchronousMessages(); //!< Handle synchronous messages with the thread
signals:
void stateChanged();
};
#endif // INCLUDE_DSPDEVICEENGINE_H

Wyświetl plik

@ -886,6 +886,7 @@ public:
signals:
void deviceSetAdded(int index, DeviceAPI *device);
void deviceChanged(int index);
void deviceStateChanged(int index, DeviceAPI *device);
void deviceSetRemoved(int index);
void channelAdded(int deviceSetIndex, ChannelAPI *channel);
void channelRemoved(int deviceSetIndex, ChannelAPI *oldChannel);

Wyświetl plik

@ -34,8 +34,10 @@
#include "channel/channelgui.h"
#include "feature/featuregui.h"
#include "device/devicegui.h"
#include "device/deviceset.h"
#include "mainspectrum/mainspectrumgui.h"
#include "workspace.h"
#include "maincore.h"
Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
QDockWidget(parent, flags),
@ -80,6 +82,11 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_addMIMODeviceButton->setToolTip("Add MIMO device");
m_addMIMODeviceButton->setFixedSize(20, 20);
m_startStopButton = new ButtonSwitch();
m_startStopButton->setCheckable(true);
updateStartStopButton(false);
m_startStopButton->setFixedSize(20, 20);
m_vline1 = new QFrame();
m_vline1->setFrameShape(QFrame::VLine);
m_vline1->setFrameShadow(QFrame::Sunken);
@ -142,6 +149,7 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
m_titleBarLayout->addWidget(m_addRxDeviceButton);
m_titleBarLayout->addWidget(m_addTxDeviceButton);
m_titleBarLayout->addWidget(m_addMIMODeviceButton);
m_titleBarLayout->addWidget(m_startStopButton);
m_titleBarLayout->addWidget(m_vline1);
m_titleBarLayout->addWidget(m_addFeatureButton);
m_titleBarLayout->addWidget(m_featurePresetsButton);
@ -211,6 +219,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::stackSubWindows
);
QObject::connect(
m_startStopButton,
&ButtonSwitch::clicked,
this,
&Workspace::startStopClicked
);
QObject::connect(
m_autoStackSubWindows,
&QPushButton::clicked,
@ -234,6 +249,13 @@ Workspace::Workspace(int index, QWidget *parent, Qt::WindowFlags flags) :
&Workspace::addFeatureEmitted
);
QObject::connect(
MainCore::instance(),
&MainCore::deviceStateChanged,
this,
&Workspace::deviceStateChanged
);
}
Workspace::~Workspace()
@ -247,6 +269,7 @@ Workspace::~Workspace()
delete m_cascadeSubWindows;
delete m_vline2;
delete m_vline1;
delete m_startStopButton;
delete m_addRxDeviceButton;
delete m_addTxDeviceButton;
delete m_addMIMODeviceButton;
@ -598,12 +621,66 @@ void Workspace::stackSubWindows()
void Workspace::autoStackSubWindows()
{
// FIXME: Need to save whether this is checked as a preference
if (m_autoStackSubWindows->isChecked()) {
stackSubWindows();
}
}
// Start/stop all devices in workspace
void Workspace::startStopClicked(bool checked)
{
if (!checked) {
emit stopAllDevices(this);
} else {
emit startAllDevices(this);
}
updateStartStopButton(checked);
}
void Workspace::updateStartStopButton(bool checked)
{
if (!checked)
{
QIcon startIcon(":/play.png");
m_startStopButton->setIcon(startIcon);
m_startStopButton->setStyleSheet("QToolButton { background-color : blue; }");
m_startStopButton->setToolTip("Start all devices in workspace");
}
else
{
QIcon stopIcon(":/stop.png");
m_startStopButton->setIcon(stopIcon);
m_startStopButton->setStyleSheet("QToolButton { background-color : green; }");
m_startStopButton->setToolTip("Stop all devices in workspace");
}
}
void Workspace::deviceStateChanged(int index, DeviceAPI *deviceAPI)
{
if (deviceAPI->getWorkspaceIndex() == m_index)
{
// Check state of all devices in workspace, to see if any are running or have errors
bool running = false;
bool error = false;
std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
for (auto deviceSet : deviceSets)
{
DeviceAPI::EngineState state = deviceSet->m_deviceAPI->state();
if (state == DeviceAPI::StRunning) {
running = true;
} else if (state == DeviceAPI::StError) {
error = true;
}
}
// Update start/stop button to reflect current state of devices
updateStartStopButton(running);
m_startStopButton->setChecked(running);
if (error) {
m_startStopButton->setStyleSheet("QToolButton { background-color : red; }");
}
}
}
void Workspace::resizeEvent(QResizeEvent *event)
{
QDockWidget::resizeEvent(event);

Wyświetl plik

@ -23,6 +23,7 @@
#include "export.h"
#include "featureadddialog.h"
#include "device/deviceapi.h"
class QHBoxLayout;
class QLabel;
@ -61,12 +62,14 @@ public:
void orderByIndex(QList<DeviceGUI *> &list);
void orderByIndex(QList<MainSpectrumGUI *> &list);
void adjustSubWindowsAfterRestore();
void updateStartStopButton(bool checked);
private:
int m_index;
QPushButton *m_addRxDeviceButton;
QPushButton *m_addTxDeviceButton;
QPushButton *m_addMIMODeviceButton;
ButtonSwitch *m_startStopButton;
QFrame *m_vline1;
QPushButton *m_addFeatureButton;
QPushButton *m_featurePresetsButton;
@ -99,8 +102,10 @@ private slots:
void tileSubWindows();
void stackSubWindows();
void autoStackSubWindows();
void startStopClicked(bool checked = false);
void addFeatureEmitted(int featureIndex);
void toggleFloating();
void deviceStateChanged(int index, DeviceAPI *deviceAPI);
signals:
void addRxDevice(Workspace *inWorkspace, int deviceIndex);
@ -108,6 +113,8 @@ signals:
void addMIMODevice(Workspace *inWorkspace, int deviceIndex);
void addFeature(Workspace*, int);
void featurePresetsDialogRequested(QPoint, Workspace*);
void startAllDevices(Workspace *inWorkspace);
void stopAllDevices(Workspace *inWorkspace);
};

Wyświetl plik

@ -43,6 +43,7 @@
#include "device/deviceenumerator.h"
#include "channel/channelapi.h"
#include "channel/channelgui.h"
#include "channel/channelwebapiutils.h"
#include "feature/featureuiset.h"
#include "feature/featureset.h"
#include "feature/feature.h"
@ -326,6 +327,13 @@ void MainWindow::sampleSourceAdd(Workspace *deviceWorkspace, Workspace *spectrum
&MainWindow::mainSpectrumRequestDeviceCenterFrequency
);
QObject::connect(
deviceAPI,
&DeviceAPI::stateChanged,
this,
&MainWindow::deviceStateChanged
);
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
@ -549,6 +557,13 @@ void MainWindow::sampleSinkAdd(Workspace *deviceWorkspace, Workspace *spectrumWo
&MainWindow::mainSpectrumRequestDeviceCenterFrequency
);
QObject::connect(
deviceAPI,
&DeviceAPI::stateChanged,
this,
&MainWindow::deviceStateChanged
);
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
@ -773,6 +788,13 @@ void MainWindow::sampleMIMOAdd(Workspace *deviceWorkspace, Workspace *spectrumWo
[=](int channelPluginIndex){ this->channelAddClicked(deviceWorkspace, deviceSetIndex, channelPluginIndex); }
);
QObject::connect(
deviceAPI,
&DeviceAPI::stateChanged,
this,
&MainWindow::deviceStateChanged
);
deviceWorkspace->addToMdiArea(m_deviceUIs.back()->m_deviceGUI);
spectrumWorkspace->addToMdiArea(m_deviceUIs.back()->m_mainSpectrumGUI);
emit m_mainCore->deviceSetAdded(deviceSetIndex, deviceAPI);
@ -1897,6 +1919,20 @@ void MainWindow::addWorkspace()
&MainWindow::openFeaturePresetsDialog
);
QObject::connect(
m_workspaces.back(),
&Workspace::startAllDevices,
this,
&MainWindow::startAllDevices
);
QObject::connect(
m_workspaces.back(),
&Workspace::stopAllDevices,
this,
&MainWindow::stopAllDevices
);
if (m_workspaces.size() > 1)
{
for (int i = 1; i < m_workspaces.size(); i++) {
@ -2704,6 +2740,41 @@ void MainWindow::showAllChannels(int deviceSetIndex)
}
}
// Start all devices in the workspace
void MainWindow::startAllDevices(Workspace *workspace)
{
int workspaceIndex = workspace->getIndex();
for (auto deviceUI : m_deviceUIs)
{
if (deviceUI->m_deviceAPI->getWorkspaceIndex() == workspaceIndex)
{
// We use WebAPI rather than call deviceUI->m_deviceAPI->startDeviceEngine();
// so that the start/stop button in the Device GUI is correctly updated
int deviceIndex = deviceUI->m_deviceAPI->getDeviceSetIndex();
ChannelWebAPIUtils::run(deviceIndex);
}
}
}
// Stop all devices in the workspace
void MainWindow::stopAllDevices(Workspace *workspace)
{
int workspaceIndex = workspace->getIndex();
for (auto deviceUI : m_deviceUIs)
{
if (deviceUI->m_deviceAPI->getWorkspaceIndex() == workspaceIndex)
{
int deviceIndex = deviceUI->m_deviceAPI->getDeviceSetIndex();
ChannelWebAPIUtils::stop(deviceIndex);
}
}
}
void MainWindow::deviceStateChanged(DeviceAPI *deviceAPI)
{
emit m_mainCore->deviceStateChanged(deviceAPI->getDeviceSetIndex(), deviceAPI);
}
void MainWindow::openFeaturePresetsDialog(QPoint p, Workspace *workspace)
{
FeaturePresetsDialog dialog;

Wyświetl plik

@ -203,7 +203,10 @@ private slots:
void channelAddClicked(Workspace *workspace, int deviceSetIndex, int channelPluginIndex);
void featureAddClicked(Workspace *workspace, int featureIndex);
void featureMove(FeatureGUI *gui, int wsIndexDestnation);
void deviceStateChanged(DeviceAPI *deviceAPI);
void openFeaturePresetsDialog(QPoint p, Workspace *workspace);
void startAllDevices(Workspace *workspace);
void stopAllDevices(Workspace *workspace);
void deviceMove(DeviceGUI *gui, int wsIndexDestnation);
void mainSpectrumMove(MainSpectrumGUI *gui, int wsIndexDestnation);
void mainSpectrumShow(int deviceSetIndex);