SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
command.cpp
Go to the documentation of this file.
1 // Copyright (C) 2018 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #include <chrono>
19 
20 #include <QKeySequence>
21 #include <QProcess>
22 
23 #include "command.h"
24 #include "util/simpleserializer.h"
25 #include "util/timeutil.h"
26 
28  m_currentProcess(nullptr),
29  m_currentProcessState(QProcess::NotRunning),
30  m_isInError(false),
31  m_currentProcessError(QProcess::UnknownError),
32  m_hasExited(false),
33  m_currentProcessExitCode(0),
34  m_currentProcessExitStatus(QProcess::NormalExit),
35  m_currentProcessPid(0)
36 {
39 
41 }
42 
43 Command::Command(const Command& command) :
44  QObject(),
45  m_group(command.m_group),
47  m_command(command.m_command),
48  m_argString(command.m_argString),
49  m_key(command.m_key),
52  m_release(command.m_release),
53  m_currentProcess(nullptr),
54  m_currentProcessState(QProcess::NotRunning),
55  m_isInError(false),
56  m_currentProcessError(QProcess::UnknownError),
57  m_hasExited(false),
59  m_currentProcessExitStatus(QProcess::NormalExit),
61 {
64 }
65 
67 {
68  if (m_currentProcess)
69  {
70 #if QT_VERSION < 0x051000
71  disconnect(m_currentProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
72 #else
73  disconnect(m_currentProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
74 #endif
75  disconnect(m_currentProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
76  disconnect(m_currentProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)));
77  m_currentProcess->deleteLater();
78  }
79 }
80 
82 {
83  m_group = "default";
84  m_description = "no name";
85  m_command = "";
86  m_argString = "";
87  m_key = static_cast<Qt::Key>(0);
88  m_keyModifiers = Qt::NoModifier;
89  m_associateKey = false;
90  m_release = false;
91 }
92 
93 QByteArray Command::serialize() const
94 {
95  SimpleSerializer s(1);
96 
97  s.writeString(1, m_group);
99  s.writeString(3, m_command);
100  s.writeString(4, m_argString);
101  s.writeS32(5, (int) m_key);
102  s.writeS32(6, (int) m_keyModifiers);
104  s.writeBool(8, m_release);
105 
106  return s.final();
107 }
108 
109 
110 bool Command::deserialize(const QByteArray& data)
111 {
112  SimpleDeserializer d(data);
113 
114  if (!d.isValid())
115  {
116  resetToDefaults();
117  return false;
118  }
119 
120  if (d.getVersion() == 1)
121  {
122  int tmpInt;
123 
124  d.readString(1, &m_group, "default");
125  d.readString(2, &m_description, "no name");
126  d.readString(3, &m_command, "");
127  d.readString(4, &m_argString, "");
128  d.readS32(5, &tmpInt, 0);
129  m_key = static_cast<Qt::Key>(tmpInt);
130  d.readS32(6, &tmpInt, 0);
131  m_keyModifiers = static_cast<Qt::KeyboardModifiers>(tmpInt);
132  d.readBool(7, &m_associateKey, false);
133  d.readBool(8, &m_release, false);
134 
135  return true;
136  }
137  else
138  {
139  resetToDefaults();
140  return false;
141  }
142 }
143 
144 QString Command::getKeyLabel() const
145 {
146  if (m_key == 0)
147  {
148  return "";
149  }
150  else if (m_keyModifiers != Qt::NoModifier)
151  {
152  QString altGrStr = m_keyModifiers & Qt::GroupSwitchModifier ? "Gr " : "";
153  int maskedModifiers = (m_keyModifiers & 0x3FFFFFFF) + ((m_keyModifiers & 0x40000000)>>3);
154  return altGrStr + QKeySequence(maskedModifiers, m_key).toString();
155  }
156  else
157  {
158  return QKeySequence(m_key).toString();
159  }
160 }
161 
162 void Command::run(const QString& apiAddress, int apiPort, int deviceSetIndex)
163 {
164  if (m_currentProcess)
165  {
166  qWarning("Command::run: process already running");
167  return;
168  }
169 
170  QString args = m_argString;
171 
172  if (m_argString.contains("%1"))
173  {
174  args = args.arg(apiAddress);
175  }
176 
177  if (m_argString.contains("%2"))
178  {
179  args.replace("%2", "%1");
180  args = args.arg(apiPort);
181  }
182 
183  if (m_argString.contains("%3"))
184  {
185  args.replace("%3", "%1");
186  args = args.arg(deviceSetIndex);
187  }
188 
189  m_currentProcessCommandLine = QString("%1 %2").arg(m_command).arg(args);
190  qDebug("Command::run: %s", qPrintable(m_currentProcessCommandLine));
191 
192  m_currentProcess = new QProcess(this);
193  m_isInError = false;
194  m_hasExited = false;
195 
196 #if QT_VERSION < 0x051000
197  connect(m_currentProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
198 #else
199  connect(m_currentProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
200 #endif
201  connect(m_currentProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
202  connect(m_currentProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)));
203 
204  m_currentProcess->setProcessChannelMode(QProcess::MergedChannels);
207 }
208 
210 {
211  if (m_currentProcess)
212  {
213  qDebug("Command::kill: %lld", m_currentProcessPid);
214  m_currentProcess->kill();
215  }
216 }
217 
218 QProcess::ProcessState Command::getLastProcessState() const
219 {
220  return m_currentProcessState;
221 }
222 
223 bool Command::getLastProcessError(QProcess::ProcessError& error) const
224 {
225  if (m_isInError) {
226  error = m_currentProcessError;
227  }
228 
229  return m_isInError;
230 }
231 
232 bool Command::getLastProcessExit(int& exitCode, QProcess::ExitStatus& exitStatus) const
233 {
234  if (m_hasExited)
235  {
236  exitCode = m_currentProcessExitCode;
237  exitStatus = m_currentProcessExitStatus;
238  }
239 
240  return m_hasExited;
241 }
242 
243 const QString& Command::getLastProcessLog() const
244 {
245  return m_log;
246 }
247 
248 void Command::processStateChanged(QProcess::ProcessState newState)
249 {
250  //qDebug("Command::processStateChanged: %d", newState);
251  if (newState == QProcess::Running) {
252  m_currentProcessPid = m_currentProcess->processId();
253  }
254 
255  m_currentProcessState = newState;
256 }
257 
258 void Command::processError(QProcess::ProcessError error)
259 {
260  //qDebug("Command::processError: %d state: %d", error, m_currentProcessState);
262  m_currentProcessError = error;
263  m_isInError = true;
264 
265  if (m_currentProcessState == QProcess::NotRunning)
266  {
267  m_log = m_currentProcess->readAllStandardOutput();
268 
269 #if QT_VERSION < 0x051000
270  disconnect(m_currentProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
271 #else
272  disconnect(m_currentProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
273 #endif
274  disconnect(m_currentProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
275  disconnect(m_currentProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)));
276 
277  m_currentProcess->deleteLater(); // make sure other threads can still access it until all events have been processed
278  m_currentProcess = nullptr; // for this thread it can assume it was deleted
279  }
280 }
281 
282 void Command::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
283 {
284  //qDebug("Command::processFinished: (%d) %d", exitCode, exitStatus);
286  m_currentProcessExitCode = exitCode;
287  m_currentProcessExitStatus = exitStatus;
288  m_hasExited = true;
289  m_log = m_currentProcess->readAllStandardOutput();
290 
291 #if QT_VERSION < 0x051000
292  disconnect(m_currentProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
293 #else
294  disconnect(m_currentProcess, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
295 #endif
296  disconnect(m_currentProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
297  disconnect(m_currentProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState)));
298 
299  m_currentProcess->deleteLater(); // make sure other threads can still access it until all events have been processed
300  m_currentProcess = nullptr; // for this thread it can assume it was deleted
301 }
QString m_group
Definition: command.h:95
QProcess::ExitStatus m_currentProcessExitStatus
Definition: command.h:109
QProcess::ProcessState getLastProcessState() const
Definition: command.cpp:218
Command()
Definition: command.cpp:27
void processFinished(int exitCode, QProcess::ExitStatus exitStatus)
Definition: command.cpp:282
QString m_description
Definition: command.h:96
QString m_argString
Definition: command.h:98
void processError(QProcess::ProcessError error)
Definition: command.cpp:258
Qt::Key m_key
Definition: command.h:99
QProcess * m_currentProcess
Definition: command.h:103
qint64 m_currentProcessPid
Definition: command.h:114
QString getKeyLabel() const
Definition: command.cpp:144
void resetToDefaults()
Definition: command.cpp:81
QString m_currentProcessCommandLine
Definition: command.h:113
bool readString(quint32 id, QString *result, const QString &def=QString::null) const
bool readBool(quint32 id, bool *result, bool def=false) const
void kill()
Definition: command.cpp:209
bool isValid() const
~Command()
Definition: command.cpp:66
void run(const QString &apiAddress, int apiPort, int deviceSetIndex)
Definition: command.cpp:162
uint64_t m_currentProcessFinishTimeStampms
Definition: command.h:112
bool m_release
Definition: command.h:102
bool readS32(quint32 id, qint32 *result, qint32 def=0) const
void processStateChanged(QProcess::ProcessState newState)
Definition: command.cpp:248
bool m_isInError
Definition: command.h:105
bool m_hasExited
Definition: command.h:107
QString m_command
Definition: command.h:97
void writeS32(quint32 id, qint32 value)
quint32 getVersion() const
const QString & getLastProcessLog() const
Definition: command.cpp:243
QProcess::ProcessState m_currentProcessState
Definition: command.h:104
Qt::KeyboardModifiers m_keyModifiers
Definition: command.h:100
QProcess::ProcessError m_currentProcessError
Definition: command.h:106
uint64_t m_currentProcessStartTimeStampms
Definition: command.h:111
static uint64_t nowms()
returns the current epoch in milliseconds
Definition: timeutil.cpp:20
void writeBool(quint32 id, bool value)
QString m_log
Definition: command.h:110
void writeString(quint32 id, const QString &value)
QByteArray serialize() const
Definition: command.cpp:93
bool m_associateKey
Definition: command.h:101
const QByteArray & final()
bool getLastProcessError(QProcess::ProcessError &error) const
Definition: command.cpp:223
bool getLastProcessExit(int &exitCode, QProcess::ExitStatus &exitStatus) const
Definition: command.cpp:232
int m_currentProcessExitCode
Definition: command.h:108
bool deserialize(const QByteArray &data)
Definition: command.cpp:110