mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-26 01:39:05 -05:00
Merge pull request #1455 from srcejon/fix_1452
Satellite Tracker: Add variable substitutions for commands
This commit is contained in:
commit
01fd2c63af
@ -3100,7 +3100,7 @@ void ADSBDemodGUI::speechNotification(Aircraft *aircraft, const QString &speech)
|
|||||||
void ADSBDemodGUI::commandNotification(Aircraft *aircraft, const QString &command)
|
void ADSBDemodGUI::commandNotification(Aircraft *aircraft, const QString &command)
|
||||||
{
|
{
|
||||||
QString commandLine = subAircraftString(aircraft, command);
|
QString commandLine = subAircraftString(aircraft, command);
|
||||||
QStringList allArgs = commandLine.split(" ");
|
QStringList allArgs = QProcess::splitCommand(commandLine);
|
||||||
|
|
||||||
if (allArgs.size() > 0)
|
if (allArgs.size() > 0)
|
||||||
{
|
{
|
||||||
|
@ -53,8 +53,8 @@ To perform an action on an SDRangel device set on AOS or LOS, press the "Add dev
|
|||||||
* Whether to stop acquisition on LOS.
|
* Whether to stop acquisition on LOS.
|
||||||
* Whether any file sinks in the preset should be started on AOS and stopped on LOS. This allows the baseband signal received from the satellite to be recorded to a file.
|
* Whether any file sinks in the preset should be started on AOS and stopped on LOS. This allows the baseband signal received from the satellite to be recorded to a file.
|
||||||
* Whether to override the centre frequency in the preset. This allows a single preset to be used with multiple satellites.
|
* Whether to override the centre frequency in the preset. This allows a single preset to be used with multiple satellites.
|
||||||
* A command or script to execute on AOS.
|
* A command or script to execute on AOS. See (8) for list of subsitituions.
|
||||||
* A command or script to execute on LOS.
|
* A command or script to execute on LOS. See (8) for list of subsitituions.
|
||||||
|
|
||||||
Multiple tabs can be added, to allow independent control of multiple device sets. To remove a tab, click the cross next to the device set name in the tab list.
|
Multiple tabs can be added, to allow independent control of multiple device sets. To remove a tab, click the cross next to the device set name in the tab list.
|
||||||
|
|
||||||
@ -93,12 +93,14 @@ On the Settings tab, you can set:
|
|||||||
* A time window for which passes must start and end between, to be displayed or acted upon. For example, for day time passes, you could set "must start after" to 8:00 and "must end before" to 18:00. For night time passes, set "must start after" to 20:00 and "must end before" to 6:00.
|
* A time window for which passes must start and end between, to be displayed or acted upon. For example, for day time passes, you could set "must start after" to 8:00 and "must end before" to 18:00. For night time passes, set "must start after" to 20:00 and "must end before" to 6:00.
|
||||||
* The maximum azimuth angle in degrees supported by your rotator. 450 degree support is beneficial for passes that pass through 360/0 degrees, to avoid the rotator having to do a complete rotation mid pass.
|
* The maximum azimuth angle in degrees supported by your rotator. 450 degree support is beneficial for passes that pass through 360/0 degrees, to avoid the rotator having to do a complete rotation mid pass.
|
||||||
* The maximum elevation angle in degrees supported by your rotator. 180 degree support is beneficial for passes that pass through 360/0 degrees, to avoid the rotator having to do a complete rotation mid pass.
|
* The maximum elevation angle in degrees supported by your rotator. 180 degree support is beneficial for passes that pass through 360/0 degrees, to avoid the rotator having to do a complete rotation mid pass.
|
||||||
* A speech warning to be given on AOS. ${name} will be substituted with the name of the satellite, ${duration} the pass duration and ${elevation} the maximum elevation of the pass.
|
* A speech warning to be given on AOS. See below for a list of variable substitutions.
|
||||||
* A speech warning to be given on LOS. ${name} will be substituted with the name of the satellite.
|
* A speech warning to be given on LOS.
|
||||||
* A command/script to be executed on AOS. This applies to all satellites. It is also possible to set a per-satellite command in the SDRangel Control dialog.
|
* A command/script to be executed on AOS. This applies to all satellites. It is also possible to set a per-satellite command in the SDRangel Control dialog. See below for a list of variable substitions.
|
||||||
* A command/script to be executed on LOS. This applies to all satellites. It is also possible to set a per-satellite command in the SDRangel Control dialog.
|
* A command/script to be executed on LOS. This applies to all satellites. It is also possible to set a per-satellite command in the SDRangel Control dialog.
|
||||||
* The Doppler correction period in seconds, which controls how frequently Doppler correction is applied. Which channels have Doppler correction applied is set on a per-channel basis in the SDRangel Control dialog.
|
* The Doppler correction period in seconds, which controls how frequently Doppler correction is applied. Which channels have Doppler correction applied is set on a per-channel basis in the SDRangel Control dialog.
|
||||||
|
|
||||||
|
For commands, scripts and speech, the following variables can be sustituted: ${aos}, ${los}, ${elevation}, ${aosAzimuth}, ${losAzimuth}, ${northToSouth}, ${latitude}, ${longitude}, ${altitude}, ${azimuth}, ${elevation}, ${range}, ${rangeRate}, ${speed} and ${period}.
|
||||||
|
|
||||||
![Satellite tracker settings dialog](../../../doc/img/SatelliteTracker_plugin_settingsdialog2.png)
|
![Satellite tracker settings dialog](../../../doc/img/SatelliteTracker_plugin_settingsdialog2.png)
|
||||||
|
|
||||||
On the TLEs tab, you can provide a list of URL from which satellite Two Line Element files can be downloaded from.
|
On the TLEs tab, you can provide a list of URL from which satellite Two Line Element files can be downloaded from.
|
||||||
|
@ -145,7 +145,7 @@ bool SatelliteTrackerGUI::handleMessage(const Message& message)
|
|||||||
else if (SatelliteTrackerReport::MsgReportAOS::match(message))
|
else if (SatelliteTrackerReport::MsgReportAOS::match(message))
|
||||||
{
|
{
|
||||||
SatelliteTrackerReport::MsgReportAOS& aosReport = (SatelliteTrackerReport::MsgReportAOS&) message;
|
SatelliteTrackerReport::MsgReportAOS& aosReport = (SatelliteTrackerReport::MsgReportAOS&) message;
|
||||||
aos(aosReport.getName(), aosReport.getDuration(), aosReport.getMaxElevation());
|
aos(aosReport.getName(), aosReport.getSpeech());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (SatelliteTrackerReport::MsgReportTarget::match(message))
|
else if (SatelliteTrackerReport::MsgReportTarget::match(message))
|
||||||
@ -157,7 +157,7 @@ bool SatelliteTrackerGUI::handleMessage(const Message& message)
|
|||||||
else if (SatelliteTrackerReport::MsgReportLOS::match(message))
|
else if (SatelliteTrackerReport::MsgReportLOS::match(message))
|
||||||
{
|
{
|
||||||
SatelliteTrackerReport::MsgReportLOS& losReport = (SatelliteTrackerReport::MsgReportLOS&) message;
|
SatelliteTrackerReport::MsgReportLOS& losReport = (SatelliteTrackerReport::MsgReportLOS&) message;
|
||||||
los(losReport.getName());
|
los(losReport.getName(), losReport.getSpeech());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (SatelliteTracker::MsgSatData::match(message))
|
else if (SatelliteTracker::MsgSatData::match(message))
|
||||||
@ -397,28 +397,20 @@ void SatelliteTrackerGUI::onMenuDialogCalled(const QPoint &p)
|
|||||||
resetContextMenuType();
|
resetContextMenuType();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatelliteTrackerGUI::aos(const QString& name, int duration, int maxElevation)
|
void SatelliteTrackerGUI::aos(const QString& name, const QString &speech)
|
||||||
{
|
{
|
||||||
// Call plotChart() to start the periodic updates with sat position in polar chart
|
// Call plotChart() to start the periodic updates with sat position in polar chart
|
||||||
plotChart();
|
plotChart();
|
||||||
// Give speech notification of pass
|
// Give speech notification of pass
|
||||||
QString speech = m_settings.m_aosSpeech.trimmed();
|
if (!speech.isEmpty()) {
|
||||||
if (!speech.isEmpty())
|
|
||||||
{
|
|
||||||
speech = speech.replace("${name}", name);
|
|
||||||
speech = speech.replace("${duration}", QString::number(duration));
|
|
||||||
speech = speech.replace("${elevation}", QString::number(maxElevation));
|
|
||||||
m_speech->say(speech);
|
m_speech->say(speech);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatelliteTrackerGUI::los(const QString& name)
|
void SatelliteTrackerGUI::los(const QString& name, const QString &speech)
|
||||||
{
|
{
|
||||||
// Give speech notification of end of pass
|
// Give speech notification of end of pass
|
||||||
QString speech = m_settings.m_losSpeech.trimmed();
|
if (!speech.isEmpty()) {
|
||||||
if (!speech.isEmpty())
|
|
||||||
{
|
|
||||||
speech = speech.replace("${name}", name);
|
|
||||||
m_speech->say(speech);
|
m_speech->say(speech);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,8 +111,8 @@ private:
|
|||||||
explicit SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
explicit SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
|
||||||
virtual ~SatelliteTrackerGUI();
|
virtual ~SatelliteTrackerGUI();
|
||||||
|
|
||||||
void aos(const QString& name, int duration, int maxElevation);
|
void aos(const QString& name, const QString &speech);
|
||||||
void los(const QString& name);
|
void los(const QString& name, const QString &speech);
|
||||||
|
|
||||||
void blockApplySettings(bool block);
|
void blockApplySettings(bool block);
|
||||||
void applySettings(bool force = false);
|
void applySettings(bool force = false);
|
||||||
|
@ -56,24 +56,21 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
QString getName() const { return m_name; }
|
QString getName() const { return m_name; }
|
||||||
int getDuration() const { return m_duration; }
|
QString getSpeech() const { return m_speech; }
|
||||||
int getMaxElevation() const { return m_maxElevation; }
|
|
||||||
|
|
||||||
static MsgReportAOS* create(const QString& name, int duration, int maxElevation)
|
static MsgReportAOS* create(const QString& name, const QString &speech)
|
||||||
{
|
{
|
||||||
return new MsgReportAOS(name, duration, maxElevation);
|
return new MsgReportAOS(name, speech);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
int m_duration;
|
QString m_speech;
|
||||||
int m_maxElevation;
|
|
||||||
|
|
||||||
MsgReportAOS(const QString& name, int duration, int maxElevation) :
|
MsgReportAOS(const QString& name, const QString &speech) :
|
||||||
Message(),
|
Message(),
|
||||||
m_name(name),
|
m_name(name),
|
||||||
m_duration(duration),
|
m_speech(speech)
|
||||||
m_maxElevation(maxElevation)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -84,18 +81,21 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
QString getName() const { return m_name; }
|
QString getName() const { return m_name; }
|
||||||
|
QString getSpeech() const { return m_speech; }
|
||||||
|
|
||||||
static MsgReportLOS* create(const QString& name)
|
static MsgReportLOS* create(const QString& name, const QString &speech)
|
||||||
{
|
{
|
||||||
return new MsgReportLOS(name);
|
return new MsgReportLOS(name, speech);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
|
QString m_speech;
|
||||||
|
|
||||||
MsgReportLOS(const QString& name) :
|
MsgReportLOS(const QString& name, const QString &speech) :
|
||||||
Message(),
|
Message(),
|
||||||
m_name(name)
|
m_name(name),
|
||||||
|
m_speech(speech)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -545,11 +545,8 @@ void SatelliteTrackerWorker::aos(SatWorkerState *satWorkerState)
|
|||||||
// Indicate AOS to GUI
|
// Indicate AOS to GUI
|
||||||
if (getMessageQueueToGUI())
|
if (getMessageQueueToGUI())
|
||||||
{
|
{
|
||||||
int durationMins = (int)round((satWorkerState->m_los.toSecsSinceEpoch() - satWorkerState->m_aos.toSecsSinceEpoch())/60.0);
|
QString speech = substituteVariables(m_settings.m_aosSpeech, satWorkerState->m_name);
|
||||||
int maxElevation = 0;
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportAOS::create(satWorkerState->m_name, speech));
|
||||||
if (satWorkerState->m_satState.m_passes.size() > 0)
|
|
||||||
maxElevation = satWorkerState->m_satState.m_passes[0].m_maxElevation;
|
|
||||||
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportAOS::create(satWorkerState->m_name, durationMins, maxElevation));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update target
|
// Update target
|
||||||
@ -602,21 +599,59 @@ void SatelliteTrackerWorker::calculateRotation(SatWorkerState *satWorkerState)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name)
|
QString SatelliteTrackerWorker::substituteVariables(const QString &textIn, const QString &satelliteName)
|
||||||
{
|
{
|
||||||
// Execute global program/script
|
SatWorkerState *satWorkerState = m_workerState.value(satelliteName);
|
||||||
if (!m_settings.m_aosCommand.isEmpty())
|
if (!satWorkerState) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int durationMins = (int)round((satWorkerState->m_los.toSecsSinceEpoch() - satWorkerState->m_aos.toSecsSinceEpoch())/60.0);
|
||||||
|
|
||||||
|
QString text = textIn;
|
||||||
|
text = text.replace("${name}", satelliteName);
|
||||||
|
text = text.replace("${duration}", QString::number(durationMins));
|
||||||
|
if (satWorkerState->m_satState.m_passes.size() > 0)
|
||||||
{
|
{
|
||||||
qDebug() << "SatelliteTrackerWorker::aos: executing command: " << m_settings.m_aosCommand;
|
text = text.replace("${aos}", satWorkerState->m_satState.m_passes[0].m_aos.toString());
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
text = text.replace("${los}", satWorkerState->m_satState.m_passes[0].m_los.toString());
|
||||||
QStringList allArgs = m_settings.m_aosCommand.split(" ", Qt::SkipEmptyParts);
|
text = text.replace("${elevation}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_maxElevation)));
|
||||||
#else
|
text = text.replace("${aosAzimuth}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_aosAzimuth)));
|
||||||
QStringList allArgs = m_settings.m_aosCommand.split(" ", QString::SkipEmptyParts);
|
text = text.replace("${losAzimuth}", QString::number(std::round(satWorkerState->m_satState.m_passes[0].m_losAzimuth)));
|
||||||
#endif
|
text = text.replace("${northToSouth}", QString::number(satWorkerState->m_satState.m_passes[0].m_northToSouth));
|
||||||
|
text = text.replace("${latitude}", QString::number(satWorkerState->m_satState.m_latitude));
|
||||||
|
text = text.replace("${longitude}", QString::number(satWorkerState->m_satState.m_longitude));
|
||||||
|
text = text.replace("${altitude}", QString::number(satWorkerState->m_satState.m_altitude));
|
||||||
|
text = text.replace("${azimuth}", QString::number(std::round(satWorkerState->m_satState.m_azimuth)));
|
||||||
|
text = text.replace("${elevation}", QString::number(std::round(satWorkerState->m_satState.m_elevation)));
|
||||||
|
text = text.replace("${range}", QString::number(std::round(satWorkerState->m_satState.m_range)));
|
||||||
|
text = text.replace("${rangeRate}", QString::number(std::round(satWorkerState->m_satState.m_rangeRate)));
|
||||||
|
text = text.replace("${speed}", QString::number(std::round(satWorkerState->m_satState.m_speed)));
|
||||||
|
text = text.replace("${period}", QString::number(satWorkerState->m_satState.m_period));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SatelliteTrackerWorker::executeCommand(const QString &command, const QString &satelliteName)
|
||||||
|
{
|
||||||
|
if (!command.isEmpty())
|
||||||
|
{
|
||||||
|
// Replace variables
|
||||||
|
QString cmd = substituteVariables(command, satelliteName);
|
||||||
|
QStringList allArgs = QProcess::splitCommand(cmd);
|
||||||
|
qDebug() << "SatelliteTrackerWorker::executeCommand: Executing: " << allArgs;
|
||||||
QString program = allArgs[0];
|
QString program = allArgs[0];
|
||||||
allArgs.pop_front();
|
allArgs.pop_front();
|
||||||
QProcess::startDetached(program, allArgs);
|
QProcess::startDetached(program, allArgs);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name)
|
||||||
|
{
|
||||||
|
// Execute global program/script
|
||||||
|
if (!m_settings.m_aosCommand.isEmpty()) {
|
||||||
|
executeCommand(m_settings.m_aosCommand, name);
|
||||||
|
}
|
||||||
|
|
||||||
// Update device set
|
// Update device set
|
||||||
if (m_settings.m_deviceSettings.contains(name))
|
if (m_settings.m_deviceSettings.contains(name))
|
||||||
@ -682,17 +717,8 @@ void SatelliteTrackerWorker::applyDeviceAOSSettings(const QString& name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute per satellite program/script
|
// Execute per satellite program/script
|
||||||
if (!devSettings->m_aosCommand.isEmpty())
|
if (!devSettings->m_aosCommand.isEmpty()) {
|
||||||
{
|
executeCommand(devSettings->m_aosCommand, name);
|
||||||
qDebug() << "SatelliteTrackerWorker::aos: executing command: " << devSettings->m_aosCommand;
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QStringList allArgs = devSettings->m_aosCommand.split(" ", Qt::SkipEmptyParts);
|
|
||||||
#else
|
|
||||||
QStringList allArgs = devSettings->m_aosCommand.split(" ", QString::SkipEmptyParts);
|
|
||||||
#endif
|
|
||||||
QString program = allArgs[0];
|
|
||||||
allArgs.pop_front();
|
|
||||||
QProcess::startDetached(program, allArgs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -827,7 +853,10 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState)
|
|||||||
|
|
||||||
// Indicate LOS to GUI
|
// Indicate LOS to GUI
|
||||||
if (getMessageQueueToGUI())
|
if (getMessageQueueToGUI())
|
||||||
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportLOS::create(satWorkerState->m_name));
|
{
|
||||||
|
QString speech = substituteVariables(m_settings.m_losSpeech, satWorkerState->m_name);
|
||||||
|
getMessageQueueToGUI()->push(SatelliteTrackerReport::MsgReportLOS::create(satWorkerState->m_name, speech));
|
||||||
|
}
|
||||||
|
|
||||||
// Stop Doppler timer, and set interval to 0, so we don't restart it in start()
|
// Stop Doppler timer, and set interval to 0, so we don't restart it in start()
|
||||||
satWorkerState->m_dopplerTimer.stop();
|
satWorkerState->m_dopplerTimer.stop();
|
||||||
@ -836,17 +865,8 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState)
|
|||||||
if (m_settings.m_target == satWorkerState->m_name)
|
if (m_settings.m_target == satWorkerState->m_name)
|
||||||
{
|
{
|
||||||
// Execute program/script
|
// Execute program/script
|
||||||
if (!m_settings.m_losCommand.isEmpty())
|
if (!m_settings.m_losCommand.isEmpty()) {
|
||||||
{
|
executeCommand(m_settings.m_losCommand, satWorkerState->m_name);
|
||||||
qDebug() << "SatelliteTrackerWorker::los: executing command: " << m_settings.m_losCommand;
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QStringList allArgs = m_settings.m_losCommand.split(" ", Qt::SkipEmptyParts);
|
|
||||||
#else
|
|
||||||
QStringList allArgs = m_settings.m_losCommand.split(" ", QString::SkipEmptyParts);
|
|
||||||
#endif
|
|
||||||
QString program = allArgs[0];
|
|
||||||
allArgs.pop_front();
|
|
||||||
QProcess::startDetached(program, allArgs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send LOS message to channels/features
|
// Send LOS message to channels/features
|
||||||
@ -883,17 +903,8 @@ void SatelliteTrackerWorker::los(SatWorkerState *satWorkerState)
|
|||||||
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
for (int i = 0; i < m_deviceSettingsList->size(); i++)
|
||||||
{
|
{
|
||||||
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
SatelliteTrackerSettings::SatelliteDeviceSettings *devSettings = m_deviceSettingsList->at(i);
|
||||||
if (!devSettings->m_losCommand.isEmpty())
|
if (!devSettings->m_losCommand.isEmpty()) {
|
||||||
{
|
executeCommand(devSettings->m_losCommand, satWorkerState->m_name);
|
||||||
qDebug() << "SatelliteTrackerWorker::los: executing command: " << devSettings->m_losCommand;
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
||||||
QStringList allArgs = devSettings->m_losCommand.split(" ", Qt::SkipEmptyParts);
|
|
||||||
#else
|
|
||||||
QStringList allArgs = devSettings->m_losCommand.split(" ", QString::SkipEmptyParts);
|
|
||||||
#endif
|
|
||||||
QString program = allArgs[0];
|
|
||||||
allArgs.pop_front();
|
|
||||||
QProcess::startDetached(program, allArgs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,8 @@ private:
|
|||||||
void applyDeviceAOSSettings(const QString& name);
|
void applyDeviceAOSSettings(const QString& name);
|
||||||
void startStopSinks(bool start);
|
void startStopSinks(bool start);
|
||||||
void calculateRotation(SatWorkerState *satWorkerState);
|
void calculateRotation(SatWorkerState *satWorkerState);
|
||||||
|
QString substituteVariables(const QString &textIn, const QString &satelliteName);
|
||||||
|
void executeCommand(const QString &command, const QString &satelliteName);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void stopWork();
|
void stopWork();
|
||||||
|
Loading…
Reference in New Issue
Block a user