1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

MainWindow: Add FSMs to avoid blocking on the GUI thread.

DSPDevice*Engine: Add signals to indicate when commands have been processed.
DSPDeviceSourceEngine: Fix small memory leak.
DSPEngine::removeDeviceEngineAt: Remove wait to avoid blocking thread. Return QThread to get finished signal.
DSPEngine::addDevice*Engine: Don't call deleteLater for device*Engine, as these objects are deleted manually in MainWindow, which will crash if deleteLater called first.
This commit is contained in:
srcejon 2024-10-10 13:57:11 +01:00
parent f24600b909
commit 78d0160514
12 changed files with 1271 additions and 558 deletions

View File

@ -704,6 +704,7 @@ if (BUILD_GUI)
QuickWidgets
Svg
SvgWidgets
StateMachine
OPTIONAL_COMPONENTS
WebEngineQuick
WebEngineCore

View File

@ -1030,6 +1030,7 @@ bool DSPDeviceMIMOEngine::handleMessage(const Message& message)
else if (DSPAcquisitionStop::match(message))
{
setStateRx(gotoIdle(0));
emit acquisitionStopped();
return true;
}
else if (DSPGenerationInit::match(message))
@ -1053,11 +1054,13 @@ bool DSPDeviceMIMOEngine::handleMessage(const Message& message)
else if (DSPGenerationStop::match(message))
{
setStateTx(gotoIdle(1));
emit generationStopped();
return true;
}
else if (SetSampleMIMO::match(message)) {
const auto& cmd = (const SetSampleMIMO&) message;
handleSetMIMO(cmd.getSampleMIMO());
emit sampleSet();
return true;
}
else if (AddBasebandSampleSink::match(message))
@ -1194,6 +1197,7 @@ bool DSPDeviceMIMOEngine::handleMessage(const Message& message)
BasebandSampleSink* spectrumSink = msg.getSampleSink();
spectrumSink->stop();
m_spectrumSink = nullptr;
emit spectrumSinkRemoved();
return true;
}
else if (SetSpectrumSinkInput::match(message))

View File

@ -356,6 +356,11 @@ private slots:
signals:
void stateChanged();
void acquisitionStopped();
void sampleSet();
void generationStopped();
void spectrumSinkRemoved();
};
#endif // SDRBASE_DSP_DSPDEVICEMIMOENGINE_H_

View File

@ -458,12 +458,14 @@ bool DSPDeviceSinkEngine::handleMessage(const Message& message)
else if (DSPGenerationStop::match(message))
{
setState(gotoIdle());
emit generationStopped();
return true;
}
else if (DSPSetSink::match(message))
{
const auto& cmd = (const DSPSetSink&) message;
handleSetSink(cmd.getSampleSink());
emit sampleSet();
return true;
}
else if (DSPRemoveSpectrumSink::match(message))
@ -476,6 +478,7 @@ bool DSPDeviceSinkEngine::handleMessage(const Message& message)
}
m_spectrumSink = nullptr;
emit spectrumSinkRemoved();
return true;
}
else if (DSPAddBasebandSampleSource::match(message))

View File

@ -121,6 +121,10 @@ private slots:
signals:
void stateChanged();
void generationStopped();
void sampleSet();
void spectrumSinkRemoved();
};

View File

@ -601,12 +601,15 @@ bool DSPDeviceSourceEngine::handleMessage(const Message& message)
else if (DSPAcquisitionStop::match(message))
{
setState(gotoIdle());
emit acquistionStopped();
return true;
}
else if (DSPSetSource::match(message))
{
auto cmd = (const DSPSetSource&) message;
handleSetSource(cmd.getSampleSource());
emit sampleSet();
return true;
}
else if (DSPAddBasebandSampleSink::match(message))
{
@ -620,6 +623,7 @@ bool DSPDeviceSourceEngine::handleMessage(const Message& message)
if(m_state == State::StRunning) {
sink->start();
}
return true;
}
else if (DSPRemoveBasebandSampleSink::match(message))
{
@ -631,6 +635,8 @@ bool DSPDeviceSourceEngine::handleMessage(const Message& message)
}
m_basebandSampleSinks.remove(sink);
emit sinkRemoved();
return true;
}
return false;

View File

@ -142,6 +142,10 @@ private slots:
signals:
void stateChanged();
void acquistionStopped();
void sampleSet();
void sinkRemoved();
};
#endif // INCLUDE_DSPDEVICEENGINE_H

View File

@ -70,12 +70,6 @@ DSPDeviceSourceEngine *DSPEngine::addDeviceSourceEngine()
m_deviceEngineReferences.push_back(DeviceEngineReference{0, m_deviceSourceEngines.back(), nullptr, nullptr, deviceThread});
deviceSourceEngine->moveToThread(deviceThread);
QObject::connect(
deviceThread,
&QThread::finished,
deviceSourceEngine,
&QObject::deleteLater
);
QObject::connect(
deviceThread,
&QThread::finished,
@ -118,12 +112,6 @@ DSPDeviceSinkEngine *DSPEngine::addDeviceSinkEngine()
m_deviceEngineReferences.push_back(DeviceEngineReference{1, nullptr, m_deviceSinkEngines.back(), nullptr, deviceThread});
deviceSinkEngine->moveToThread(deviceThread);
QObject::connect(
deviceThread,
&QThread::finished,
deviceSinkEngine,
&QObject::deleteLater
);
QObject::connect(
deviceThread,
&QThread::finished,
@ -166,12 +154,6 @@ DSPDeviceMIMOEngine *DSPEngine::addDeviceMIMOEngine()
m_deviceEngineReferences.push_back(DeviceEngineReference{2, nullptr, nullptr, m_deviceMIMOEngines.back(), deviceThread});
deviceMIMOEngine->moveToThread(deviceThread);
QObject::connect(
deviceThread,
&QThread::finished,
deviceMIMOEngine,
&QObject::deleteLater
);
QObject::connect(
deviceThread,
&QThread::finished,
@ -205,38 +187,39 @@ void DSPEngine::removeLastDeviceMIMOEngine()
}
}
void DSPEngine::removeDeviceEngineAt(int deviceIndex)
QThread * DSPEngine::removeDeviceEngineAt(int deviceIndex)
{
if (deviceIndex >= m_deviceEngineReferences.size()) {
return;
return nullptr;
}
QThread *deviceThread = nullptr;
if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 0) // source
{
DSPDeviceSourceEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceSourceEngine;
QThread *deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread->exit();
deviceThread->wait();
m_deviceSourceEngines.removeAll(deviceEngine);
}
else if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 1) // sink
{
DSPDeviceSinkEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceSinkEngine;
QThread *deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread->exit();
deviceThread->wait();
m_deviceSinkEngines.removeAll(deviceEngine);
}
else if (m_deviceEngineReferences[deviceIndex].m_deviceEngineType == 2) // MIMO
{
DSPDeviceMIMOEngine *deviceEngine = m_deviceEngineReferences[deviceIndex].m_deviceMIMOEngine;
QThread *deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread = m_deviceEngineReferences[deviceIndex].m_thread;
deviceThread->exit();
deviceThread->wait();
m_deviceMIMOEngines.removeAll(deviceEngine);
}
m_deviceEngineReferences.removeAt(deviceIndex);
return deviceThread;
}
void DSPEngine::createFFTFactory(const QString& fftWisdomFileName)

View File

@ -53,7 +53,7 @@ public:
DSPDeviceMIMOEngine *addDeviceMIMOEngine();
void removeLastDeviceMIMOEngine();
void removeDeviceEngineAt(int deviceIndex);
QThread *removeDeviceEngineAt(int deviceIndex);
AudioDeviceManager *getAudioDeviceManager() { return &m_audioDeviceManager; }

View File

@ -343,6 +343,9 @@ target_link_libraries(sdrgui
logging
)
if (Qt6_FOUND)
target_link_libraries(sdrgui
Qt6::StateMachine
)
target_link_libraries(sdrbase
Qt::OpenGLWidgets
)

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,11 @@
#include <QTimer>
#include <QList>
#include <QProcess>
#include <QStateMachine>
#include <QState>
#include <QFinalState>
#include <QSignalTransition>
#include <QProgressDialog>
#include "settings/mainsettings.h"
#include "util/message.h"
@ -43,6 +48,7 @@ class QToolButton;
class DSPEngine;
class DSPDeviceSourceEngine;
class DSPDeviceSinkEngine;
class DSPDeviceMIMOEngine;
class Indicator;
class GLSpectrumGUI;
class MainSpectrumGUI;
@ -65,13 +71,217 @@ class CommandKeyReceiver;
class ConfigurationsDialog;
class ProfileDialog;
class SerializableInterface;
class SDRangelSplash;
class QMenuBar;
class Workspace;
class MainWindow;
// Would preferablly have these FSM classes as nested classes of MainWindow
// However, as they inherit from QObject, they should have Q_OBJECT macro which isn't supported for nested classes
// Instead we have to declare them as friend classes to MainWindow
class MainWindowFSM : public QStateMachine {
Q_OBJECT
public:
MainWindowFSM(MainWindow *mainWindow, QObject *parent=nullptr);
protected:
void createStates(int states); // number of states to create, including final state
MainWindow *m_mainWindow;
QList<QState *> m_states;
QFinalState *m_finalState;
};
class AddSampleSourceFSM : public MainWindowFSM {
Q_OBJECT
public:
AddSampleSourceFSM(MainWindow *mainWindow, Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex, bool loadDefaults, QObject *parent=nullptr);
private:
Workspace *m_deviceWorkspace;
Workspace *m_spectrumWorkspace;
int m_deviceIndex;
bool m_loadDefaults;
int m_deviceSetIndex;
DeviceAPI *m_deviceAPI;
DeviceUISet *m_deviceUISet;
DSPDeviceSourceEngine *m_dspDeviceSourceEngine;
void addEngine();
void addDevice();
void addDeviceUI();
};
class AddSampleSinkFSM : public MainWindowFSM {
Q_OBJECT
public:
AddSampleSinkFSM(MainWindow *mainWindow, Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex, bool loadDefaults, QObject *parent=nullptr);
private:
Workspace *m_deviceWorkspace;
Workspace *m_spectrumWorkspace;
int m_deviceIndex;
bool m_loadDefaults;
int m_deviceSetIndex;
DeviceAPI *m_deviceAPI;
DeviceUISet *m_deviceUISet;
DSPDeviceSinkEngine *m_dspDeviceSinkEngine;
void addEngine();
void addDevice();
void addDeviceUI();
};
class AddSampleMIMOFSM : public MainWindowFSM {
Q_OBJECT
public:
AddSampleMIMOFSM(MainWindow *mainWindow, Workspace *deviceWorkspace, Workspace *spectrumWorkspace, int deviceIndex, bool loadDefaults, QObject *parent=nullptr);
private:
Workspace *m_deviceWorkspace;
Workspace *m_spectrumWorkspace;
int m_deviceIndex;
bool m_loadDefaults;
int m_deviceSetIndex;
DeviceAPI *m_deviceAPI;
DeviceUISet *m_deviceUISet;
DSPDeviceMIMOEngine *m_dspDeviceMIMOEngine;
void addEngine();
void addDevice();
void addDeviceUI();
};
class RemoveDeviceSetFSM : public MainWindowFSM {
Q_OBJECT
public:
RemoveDeviceSetFSM(MainWindow *mainWindow, int deviceSetIndex, QObject *parent=nullptr);
private:
int m_deviceSetIndex;
DeviceUISet *m_deviceUISet;
DSPDeviceSourceEngine *m_deviceSourceEngine;
DSPDeviceSinkEngine *m_deviceSinkEngine;
DSPDeviceMIMOEngine *m_deviceMIMOEngine;
QSignalTransition *m_t1;
QSignalTransition *m_t2;
void stopAcquisition();
void removeSink();
void removeUI();
void stopEngine();
void removeDeviceSet();
};
class RemoveAllDeviceSetsFSM : public MainWindowFSM {
Q_OBJECT
public:
RemoveAllDeviceSetsFSM(MainWindow *mainWindow, QObject *parent=nullptr);
private:
void removeNext();
};
class RemoveAllWorkspacesFSM : public MainWindowFSM {
Q_OBJECT
public:
RemoveAllWorkspacesFSM(MainWindow *mainWindow, QObject *parent=nullptr);
private:
RemoveAllDeviceSetsFSM *m_removeAllDeviceSetsFSM;
void removeDeviceSets();
void removeWorkspaces();
};
class LoadConfigurationFSM : public MainWindowFSM {
Q_OBJECT
public:
LoadConfigurationFSM(MainWindow *mainWindow, const Configuration *configuration, QProgressDialog *waitBox, QObject *parent=nullptr);
private:
const Configuration *m_configuration;
QProgressDialog *m_waitBox;
RemoveAllWorkspacesFSM *m_removeAllWorkspacesFSM;
void clearWorkspace();
void createWorkspaces();
void loadDeviceSets();
void loadDeviceSetSettings();
void loadFeatureSets();
void restoreGeometry();
};
class CloseFSM : public MainWindowFSM {
Q_OBJECT
public:
CloseFSM(MainWindow *mainWindow, QObject *parent=nullptr);
private:
void on_started();
void on_finished();
};
class InitFSM : public MainWindowFSM {
Q_OBJECT
public:
InitFSM(MainWindow *mainWindow, SDRangelSplash *splash, bool loadDefault, QObject *parent=nullptr);
private:
SDRangelSplash *m_splash;
LoadConfigurationFSM *m_loadConfigurationFSM;
void loadDefaultConfiguration();
void showDefaultConfigurations();
};
class SDRGUI_API MainWindow : public QMainWindow {
Q_OBJECT
friend InitFSM;
friend AddSampleSourceFSM;
friend AddSampleSinkFSM;
friend AddSampleMIMOFSM;
friend LoadConfigurationFSM;
friend RemoveDeviceSetFSM;
friend RemoveAllDeviceSetsFSM;
friend RemoveAllWorkspacesFSM;
friend CloseFSM;
public:
explicit MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parser, QWidget* parent = nullptr);
~MainWindow() final;
@ -130,7 +340,11 @@ private:
CommandKeyReceiver *m_commandKeyReceiver;
ProfileDialog *m_profileDialog;
#if QT_CONFIG(process)
QProcess *m_fftWisdomProcess;
#endif
bool m_settingsSaved; // Records if settings have already been saved in response to a QCloseEvent
void loadSettings();
void loadDeviceSetPresetSettings(const Preset* preset, int deviceSetIndex);
@ -146,6 +360,7 @@ private:
void removeDeviceSet(int deviceSetIndex);
void removeLastDeviceSet();
void removeAllDeviceSets();
void addFeatureSet();
void removeFeatureSet(unsigned int featureSetIndex);
void removeAllFeatureSets();
@ -160,15 +375,30 @@ private:
int deviceIndex,
DeviceUISet *deviceUISet
);
void sampleSourceCreateUI(
int deviceSetIndex,
int deviceIndex,
DeviceUISet *deviceUISet
);
void sampleSinkCreate(
int deviceSetIndex,
int deviceIndex,
DeviceUISet *deviceUISet
);
void sampleSinkCreateUI(
int deviceSetIndex,
int deviceIndex,
DeviceUISet *deviceUISet
);
void sampleMIMOCreate(
int deviceSetIndex,
int deviceIndex,
DeviceUISet *deviceUISet
);
void sampleMIMOCreateUI(
int deviceSetIndex,
int deviceIndex,
DeviceUISet *deviceUISet
);
void deleteFeature(int featureSetIndex, int featureIndex);
void loadDefaultPreset(const QString& pluginId, SerializableInterface *serializableInterface);
@ -178,6 +408,12 @@ private:
protected:
void keyPressEvent(QKeyEvent* event) override;
signals:
// For internal FSM usage
void allDeviceSetsRemoved();
void allDeviceSetsAdded();
void engineStopped();
private slots:
void handleMessages();
void handleWorkspaceVisibility(Workspace *workspace, bool visibility);
@ -193,10 +429,13 @@ private slots:
void on_action_Graphics_triggered();
void on_action_Logging_triggered();
void on_action_FFT_triggered();
#if QT_CONFIG(process)
void on_action_FFTWisdom_triggered();
void on_action_commands_triggered();
#endif
void on_action_My_Position_triggered();
void on_action_DeviceUserArguments_triggered();
void on_action_commands_triggered();
void on_action_Welcome_triggered();
void on_action_Quick_Start_triggered() const;
void on_action_Main_Window_triggered() const;
void on_action_Loaded_Plugins_triggered();
@ -228,7 +467,9 @@ private slots:
void showAllChannels(int deviceSetIndex);
void openDeviceSetPresetsDialog(QPoint p, const DeviceGUI *deviceGUI);
void commandKeyPressed(Qt::Key key, Qt::KeyboardModifiers keyModifiers, bool release) const;
#if QT_CONFIG(process)
void fftWisdomProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
#endif
void orientationChanged(Qt::ScreenOrientation orientation);
};