GCC Code Coverage Report


Directory: ./
File: qtpdcom/src/Process.cpp
Date: 2025-02-23 04:08:29
Exec Total Coverage
Lines: 215 329 65.3%
Branches: 152 374 40.6%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright (C) 2009-2022 Florian Pose <fp@igh.de>
4 *
5 * This file is part of the QtPdCom library.
6 *
7 * The QtPdCom library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * The QtPdCom library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ****************************************************************************/
21
22 #include "Process.h"
23 #include "MessageManager.h"
24
25 #include <pdcom5/MessageManagerBase.h>
26 #include <pdcom5/Exception.h>
27
28 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
29 #include "LoginManager_p.h"
30 #include "LoginManager.h"
31 #endif
32
33
34 #ifdef __WIN32__
35 #include <windows.h> // GetUserName(), gethostname()
36 #include <lmcons.h> // UNLEN
37 #else
38 #include <unistd.h> // getlogin()
39 #endif
40
41 #include <string>
42 #include <list>
43
44 #include <QTranslator>
45 #include <QFutureInterface>
46 #include <QQueue>
47 #include <QSslSocket>
48 #include <QSslKey>
49 #include <QSslCertificate>
50 #include <QSslConfiguration>
51
52 #include <QtPdCom1.h>
53 #include <git_revision_hash.h>
54
55 #define STR(x) #x
56 #define QUOTE(x) STR(x)
57
58 const char *const QtPdCom::qtpdcom_version_code = QUOTE(
59 QTPDCOM_MAJOR) "." QUOTE(QTPDCOM_MINOR) "." QUOTE(QTPDCOM_RELEASE);
60
61 const char *const QtPdCom::qtpdcom_full_version = GIT_REV;
62
63 #define DEBUG_DATA 0
64
65 using QtPdCom::Process;
66 using VariablePromise = QFutureInterface<PdCom::Variable>;
67 using ListPromise = QFutureInterface<QtPdCom::VariableList>;
68 using PingPromise = QFutureInterface<void>;
69 using ClientStatisticsPromise =
70 QFutureInterface<std::vector<PdCom::ClientStatistics>>;
71
72 /****************************************************************************/
73
74 18 struct Process::Impl
75 {
76 18 Impl(Process *process):
77 process(process),
78 messageManager(),
79 appName("QtPdCom1"),
80 socket(),
81 defaultSslConfig(socket.sslConfiguration()),
82 socketValid(false),
83 connectionState(Disconnected),
84 rxBytes(0),
85
8/16
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 15 taken 18 times.
✗ Branch 16 not taken.
✓ Branch 22 taken 18 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 18 times.
✗ Branch 28 not taken.
✓ Branch 31 taken 18 times.
✗ Branch 32 not taken.
✓ Branch 43 taken 18 times.
✗ Branch 44 not taken.
✓ Branch 49 taken 18 times.
✗ Branch 50 not taken.
18 txBytes(0)
86 {
87 // set the default process to the always last instance of process
88 18 defaultProcess = process;
89 18 }
90
91 void connectToHost(const QString &address, quint16 port);
92
93 Process *const process;
94
95 QtPdCom::MessageManager messageManager;
96
97 QString appName; /**< Our application name, that is announced to the
98 server. Default: QtPdCom1. */
99
100 QUrl url;
101 SslCaMode caMode = SslCaMode::NoTLS;
102 QList<QSslCertificate> caCertificates;
103 QSslKey privateKey;
104 QSslCertificate privateCert;
105 QSslSocket socket; /**< TCP socket to the process. */
106 // make backup from inital ssl config
107 const QSslConfiguration defaultSslConfig;
108
109 bool socketValid; /**< Connection state of the socket. */
110 ConnectionState connectionState; /**< The current connection state. */
111 QString errorString; /**< Error reason. Set, before error() is
112 emitted. */
113 quint64 rxBytes;
114 quint64 txBytes;
115
116 QQueue<VariablePromise> findVariableQueue;
117 VariablePromise currentFindVariablePromise;
118 QQueue<ListPromise> listVariableQueue;
119 ListPromise currentListPromise;
120 QQueue<PingPromise> pingQueue;
121 QQueue<ClientStatisticsPromise> clientStatisticsQueue;
122
123 static QtPdCom::Process *defaultProcess; /**< last created process
124 is the default process */
125
126 50 void setConnectionState(ConnectionState const state)
127 {
128
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 50 times.
✗ Branch 4 not taken.
50 if (connectionState != state) {
129 50 connectionState = state;
130 50 emit process->connectionStatusChanged();
131 }
132 50 }
133 };
134
135 /****************************************************************************/
136
137 QtPdCom::Process *QtPdCom::Process::Impl::defaultProcess = nullptr;
138
139 /*****************************************************************************
140 * Public implementation
141 ****************************************************************************/
142
143 /** Constructor.
144 */
145 18 Process::Process(QObject *parent):
146 QObject(parent),
147 PdCom::Process(),
148
3/6
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 18 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 18 times.
✗ Branch 16 not taken.
18 impl {std::unique_ptr<Impl> {new Impl {this}}}
149 {
150
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket,
151 SIGNAL(connected()),
152 this,
153 SLOT(socketConnected()));
154
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket,
155 SIGNAL(disconnected()),
156 this,
157 SLOT(socketDisconnected()));
158 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
159 connect(&impl->socket,
160 &QAbstractSocket::errorOccurred,
161 this,
162 &Process::socketError);
163 #else
164
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket,
165 SIGNAL(error(QAbstractSocket::SocketError)),
166 this,
167 SLOT(socketError()));
168 #endif
169
3/6
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 connect(&impl->socket, SIGNAL(readyRead()), this, SLOT(socketRead()));
170
171
1/2
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
18 setMessageManager(&impl->messageManager);
172 18 }
173
174 /****************************************************************************/
175
176 /** Destructor.
177 */
178 37 Process::~Process()
179 {
180 18 setMessageManager(nullptr);
181
182 // reset default process if this instance is the default
183
2/2
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 1 times.
18 if (impl->defaultProcess == this) {
184 17 impl->defaultProcess = nullptr;
185 }
186
187 18 disconnectFromHost();
188 18 reset();
189 19 }
190
191 /****************************************************************************/
192
193 /** Sets the application name.
194 */
195 1 void Process::setApplicationName(const QString &name)
196 {
197 1 impl->appName = name;
198 1 }
199
200 QString Process::getApplicationName() const
201 {
202 return impl->appName;
203 }
204
205 QVariant Process::nameQt() const
206 {
207 try {
208 return QString::fromStdString(name());
209 }
210 catch (PdCom::NotConnected const &) {
211 return {};
212 }
213 }
214
215 QVariant Process::versionQt() const
216 {
217 try {
218 return QString::fromStdString(version());
219 }
220 catch (PdCom::NotConnected const &) {
221 return {};
222 }
223 }
224
225 /****************************************************************************/
226
227 /** Starts to connect to a process.
228 */
229 18 void Process::connectToHost(const QString &address, quint16 port)
230 {
231 18 impl->connectToHost(address, port);
232 18 }
233
234 18 void Process::Impl::connectToHost(const QString &address, quint16 port)
235 {
236
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 url.setHost(address);
237
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 url.setPort(port);
238
4/6
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 6 times.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 18 times.
✗ Branch 11 not taken.
18 url.setScheme(caMode == SslCaMode::NoTLS ? "msr" : "msrs");
239
1/2
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
18 setConnectionState(Connecting);
240
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 6 times.
18 if (caMode == SslCaMode::NoTLS) {
241
1/2
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
12 socket.connectToHost(address, port);
242 12 return;
243 }
244
3/4
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 6 times.
✓ Branch 8 taken 12 times.
12 auto ssl_config = defaultSslConfig;
245
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 ssl_config.setPeerVerifyMode(QSslSocket::VerifyPeer);
246
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
6 if (caMode == SslCaMode::CustomCAs) {
247
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 ssl_config.setCaCertificates(caCertificates);
248 }
249
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 else if (caMode == SslCaMode::IgnoreCertificate) {
250
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ssl_config.setPeerVerifyMode(QSslSocket::VerifyNone);
251 }
252
7/10
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 2 times.
✓ Branch 13 taken 4 times.
6 if (!privateCert.isNull() && !privateKey.isNull()) {
253
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ssl_config.setPrivateKey(privateKey);
254
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ssl_config.setLocalCertificate(privateCert);
255 }
256
1/2
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 socket.setSslConfiguration(ssl_config);
257
1/2
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
6 socket.connectToHostEncrypted(address, port);
258 }
259
260 /****************************************************************************/
261
262 /** Disconnects from a process.
263 */
264 18 void Process::disconnectFromHost()
265 {
266
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 3 times.
18 switch (impl->connectionState) {
267 15 case Connecting:
268 case Connected:
269 15 impl->socketValid = false;
270 15 impl->setConnectionState(Disconnected);
271 15 asyncData();
272 15 impl->rxBytes = 0;
273 15 impl->txBytes = 0;
274 15 reset();
275 15 impl->socket.disconnectFromHost();
276 15 impl->socket.abort();
277 15 emit disconnected();
278 15 break;
279
280 3 default:
281 3 break;
282 }
283 18 }
284
285 /****************************************************************************/
286
287 /**
288 * \return The connection state.
289 */
290 15 Process::ConnectionState Process::getConnectionState() const
291 {
292
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
15 return impl->connectionState;
293 }
294
295 /****************************************************************************/
296
297 /**
298 * \return \a true, if the process is connected.
299 */
300 73 bool Process::isConnected() const
301 {
302
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 73 times.
73 return impl->connectionState == Connected;
303 }
304
305 /****************************************************************************/
306
307 /**
308 * \return Error reason after the error() signal was emitted.
309 */
310 const QString &Process::getErrorString() const
311 {
312 return impl->errorString;
313 }
314
315 /****************************************************************************/
316
317 /**
318 * \return Host name of the process.
319 */
320 QString Process::getPeerName() const
321 {
322 return impl->socket.peerName();
323 }
324
325 /****************************************************************************/
326
327 /** Wrapper function for Process::findVariable.
328 */
329
330 15 QFuture<PdCom::Variable> Process::find(const QString &path)
331 {
332
1/2
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
15 impl->currentFindVariablePromise = {};
333 15 impl->currentFindVariablePromise.reportStarted();
334 15 auto ans = impl->currentFindVariablePromise.future();
335
4/6
✓ Branch 5 taken 15 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 9 times.
✓ Branch 14 taken 6 times.
15 if (!PdCom::Process::find(path.toStdString())) {
336 // variable was not cached, findReply() will be called later
337
1/2
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
9 impl->findVariableQueue.append(impl->currentFindVariablePromise);
338 }
339
2/4
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 15 times.
✗ Branch 12 not taken.
15 impl->currentFindVariablePromise = {};
340 15 return ans;
341 }
342
343 /****************************************************************************/
344
345 /** Wrapper function for Process::list.
346 */
347
348 1 QFuture<QtPdCom::VariableList> Process::list(const QString &path)
349 {
350
1/2
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
1 impl->currentListPromise = {};
351 1 impl->currentListPromise.reportStarted();
352 1 auto ans = impl->currentListPromise.future();
353
3/6
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
1 if (!PdCom::Process::list(path.toStdString())) {
354
1/2
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 impl->listVariableQueue.append(impl->currentListPromise);
355 }
356
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
1 impl->currentListPromise = {};
357 1 return ans;
358 }
359
360 /****************************************************************************/
361
362 QFuture<std::vector<PdCom::ClientStatistics>>
363 2 QtPdCom::Process::getClientStatisticsQt()
364 {
365
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 ClientStatisticsPromise ans;
366
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 impl->clientStatisticsQueue.append(ans);
367
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 ans.reportStarted();
368
1/2
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 PdCom::Process::getClientStatistics();
369
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return ans.future();
370 }
371
372 /****************************************************************************/
373
374 /** Send a broadcast message.
375 */
376 2 void Process::sendBroadcast(const QString &msg, const QString &attr)
377 {
378
2/4
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 2 times.
✗ Branch 13 not taken.
2 broadcast(msg.toStdString(), attr.toStdString());
379 2 }
380
381 /****************************************************************************/
382
383 1 quint64 Process::getRxBytes() const
384 {
385 1 return impl->rxBytes;
386 }
387
388 /****************************************************************************/
389
390 1 quint64 Process::getTxBytes() const
391 {
392 1 return impl->txBytes;
393 }
394
395 6 void QtPdCom::Process::setCaMode(SslCaMode mode)
396 {
397
1/2
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
6 if (impl->caMode != mode) {
398 6 impl->caMode = mode;
399
2/4
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
6 impl->url.setScheme(mode == SslCaMode::NoTLS ? "msr" : "msrs");
400 6 emit sslCaModeChanged();
401 }
402 6 }
403
404 Process::SslCaMode Process::getCaMode() const
405 {
406 return impl->caMode;
407 }
408
409
410 2 void QtPdCom::Process::setClientCertificate(
411 const QSslCertificate &cert,
412 const QSslKey &key)
413 {
414 2 impl->privateKey = key;
415 2 impl->privateCert = cert;
416 2 }
417
418 4 void QtPdCom::Process::setCustomCAs(QList<QSslCertificate> cas)
419 {
420 4 impl->caCertificates = std::move(cas);
421 4 }
422
423 /****************************************************************************/
424
425 QtPdCom::Process *Process::getDefaultProcess()
426 {
427 return QtPdCom::Process::Impl::defaultProcess;
428 }
429
430 /****************************************************************************/
431
432 /** Set default process "manually"
433 */
434 void Process::setDefaultProcess(QtPdCom::Process *process)
435 {
436 QtPdCom::Process::Impl::defaultProcess = process;
437 }
438
439 /****************************************************************************/
440
441 PdCom::MessageManagerBase *Process::getMessageManager() const
442 {
443 return &impl->messageManager;
444 }
445
446 /****************************************************************************/
447
448 4 void QtPdCom::Process::setLoginManager(LoginManager *lm)
449 {
450 Q_UNUSED(lm)
451
452 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
453
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (lm) {
454 4 setAuthManager(lm->d_ptr.data());
455 }
456 else {
457 setAuthManager(nullptr);
458 }
459 #else
460 throw PdCom::InvalidArgument("build without sasl support");
461 #endif
462 4 }
463
464 /****************************************************************************/
465
466 QtPdCom::LoginManager *Process::getLoginManager() const
467 {
468 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
469 if (const auto p = getAuthManager()) {
470 auto *lm_p = static_cast<LoginManagerPrivate *>(p);
471 return lm_p->q_ptr;
472 }
473 return nullptr;
474 #else
475 throw PdCom::InvalidArgument("build without sasl support");
476 #endif
477 }
478
479 /*****************************************************************************
480 * private methods
481 ****************************************************************************/
482
483 14 std::string Process::applicationName() const
484 {
485 14 return impl->appName.toStdString();
486 }
487
488 /****************************************************************************/
489
490 14 std::string Process::hostname() const
491 {
492 14 char hostname[256];
493
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 if (!gethostname(hostname, sizeof(hostname))) {
494
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
14 return hostname;
495 }
496 return "";
497 }
498
499 /****************************************************************************/
500
501 /** Read data from the socket.
502 */
503 79 int Process::read(char *buf, int count)
504 {
505
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 79 times.
✓ Branch 5 taken 15 times.
✓ Branch 6 taken 64 times.
158 if (impl->connectionState == Disconnected
506
4/6
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 64 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
79 || impl->connectionState == ConnectedError) {
507 15 return 0;
508 }
509 64 qint64 ret(impl->socket.read(buf, count));
510
1/2
✓ Branch 0 taken 64 times.
✗ Branch 1 not taken.
64 if (ret > 0) {
511 64 impl->rxBytes += ret;
512 }
513 64 return ret;
514 }
515
516 /****************************************************************************/
517
518 /** Sends data via the socket.
519 *
520 * This is the implementation of the virtual PdCom::Process
521 * method to enable the Process object to send data to the process.
522 */
523 3902 void Process::write(const char *buf, size_t count)
524 {
525 #if DEBUG_DATA
526 qDebug() << "Writing:" << count << QByteArray(buf, count);
527 #endif
528
2/2
✓ Branch 0 taken 1951 times.
✓ Branch 1 taken 1951 times.
5853 while (count > 0) {
529 1951 qint64 ret(impl->socket.write(buf, count));
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1951 times.
1951 if (ret <= 0) {
531 qWarning("write() failed.");
532 impl->socketValid = false;
533 impl->rxBytes = 0;
534 impl->txBytes = 0;
535 reset();
536 impl->socket.disconnectFromHost();
537 emit error();
538 return;
539 }
540 1951 count -= ret;
541 1951 buf += ret;
542 1951 impl->txBytes += ret;
543 }
544 }
545
546 /****************************************************************************/
547
548 /** Flushed the socket.
549 */
550 62 void Process::flush()
551 {
552 #if DEBUG_DATA
553 qDebug() << "Flushing not implemented.";
554 #endif
555 62 }
556
557 /****************************************************************************/
558
559 /** The process is connected and ready.
560 *
561 * This virtual function from PdCom::Process has to be overloaded to let
562 * subclasses know about this event.
563 */
564 14 void Process::connected()
565 {
566 14 impl->setConnectionState(Connected);
567 14 emit processConnected();
568 14 }
569
570 /****************************************************************************/
571
572 /** Broadcast Reply.
573 */
574 4 void Process::broadcastReply(
575 const std::string &message,
576 const std::string &attr,
577 std::chrono::nanoseconds time_ns,
578 const std::string &user)
579
580 {
581
1/2
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
20 emit broadcastReceived(
582
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 QString::fromStdString(message),
583
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 QString::fromStdString(attr),
584 4 time_ns.count(),
585 8 QString::fromStdString(user));
586 4 }
587
588 /****************************************************************************/
589
590 /** Ping Reply.
591 */
592 3 void Process::pingReply()
593 {
594
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (impl->pingQueue.empty()) {
595 return;
596 }
597
598
2/4
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
6 auto promise = impl->pingQueue.dequeue();
599
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 promise.reportFinished();
600 }
601
602 /****************************************************************************/
603
604 4 QFuture<void> Process::pingQt()
605 {
606
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 PingPromise promise;
607
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 promise.reportStarted();
608
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 impl->pingQueue.push_back(promise);
609
1/2
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 PdCom::Process::ping();
610
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 return promise.future();
611 }
612
613 /****************************************************************************/
614
615 /** Resets the PdCom process.
616 */
617 36 void Process::reset()
618 {
619 try {
620
1/2
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 PdCom::Process::reset();
621 }
622 catch (std::exception &e) {
623 // do nothing
624 }
625
626 36 impl->messageManager.reset();
627 // cancel all pending futures
628
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->pingQueue.empty()) {
629
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->pingQueue.dequeue().reportCanceled();
630 }
631
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
1 while (!impl->findVariableQueue.empty()) {
632
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->findVariableQueue.dequeue().reportCanceled();
633 }
634
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 if (impl->currentFindVariablePromise.isRunning()) {
635 impl->currentFindVariablePromise.reportCanceled();
636 impl->currentFindVariablePromise = {};
637 }
638
639
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->listVariableQueue.empty()) {
640
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->listVariableQueue.dequeue().reportCanceled();
641 }
642
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
36 if (impl->currentListPromise.isRunning()) {
643 impl->currentListPromise.reportCanceled();
644 impl->currentListPromise = {};
645 }
646
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 36 times.
38 while (!impl->clientStatisticsQueue.empty()) {
647
1/2
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 impl->clientStatisticsQueue.dequeue().reportCanceled();
648 }
649 36 }
650
651 /****************************************************************************/
652
653 /** Socket connection established.
654 *
655 * This is called, when the pure socket connection was established and the
656 * Process object can start using it.
657 */
658 18 void Process::socketConnected()
659 {
660 18 impl->socketValid = true;
661
1/2
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
18 impl->socket.setSocketOption(QAbstractSocket::KeepAliveOption, 1);
662 18 }
663
664 /****************************************************************************/
665
666 /** Socket disconnected.
667 *
668 * The socket was closed and the process has to be told, that it is
669 * disconnected.
670 */
671 18 void Process::socketDisconnected()
672 {
673
2/5
✗ Branch 3 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18 times.
18 switch (impl->connectionState) {
674 case Connecting:
675 impl->socketValid = false;
676 impl->setConnectionState(ConnectError);
677 impl->rxBytes = 0;
678 impl->txBytes = 0;
679 reset();
680 emit error();
681 break;
682
683 case Connected:
684 impl->socketValid = false;
685 impl->setConnectionState(Disconnected);
686 asyncData();
687 impl->rxBytes = 0;
688 impl->txBytes = 0;
689 reset();
690 emit disconnected();
691 break;
692
693 18 default:
694 18 break;
695 }
696 18 }
697
698 /****************************************************************************/
699
700 /** There was a socket error.
701 *
702 * The error could come up either while connecting the socket, or when the
703 * socket was already connected.
704 */
705 4 void Process::socketError()
706 {
707 4 impl->errorString = impl->socket.errorString();
708
709
3/5
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
4 switch (impl->connectionState) {
710 3 case Connecting:
711 3 impl->socketValid = false;
712 3 impl->setConnectionState(ConnectError);
713 3 impl->rxBytes = 0;
714 3 impl->txBytes = 0;
715 3 reset();
716 3 emit error();
717 3 break;
718
719 case Connected:
720 impl->socketValid = false;
721 impl->setConnectionState(ConnectedError);
722 asyncData();
723 impl->rxBytes = 0;
724 impl->txBytes = 0;
725 reset();
726 emit error();
727 break;
728
729 1 default:
730 1 break;
731 }
732 4 }
733
734 /****************************************************************************/
735
736 /** The socket has new data to read.
737 */
738 128 void Process::socketRead()
739 {
740 try {
741
3/4
✓ Branch 6 taken 128 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 64 times.
✓ Branch 9 taken 64 times.
192 while (impl->socket.bytesAvailable() > 0) {
742
1/2
✓ Branch 4 taken 64 times.
✗ Branch 5 not taken.
64 asyncData();
743 }
744 }
745 catch (std::exception &e) {
746 impl->errorString = "Exception during asyncData(): ";
747 impl->errorString += e.what();
748 qCritical() << impl->errorString;
749 impl->socketValid = false;
750 if (impl->connectionState == Connected) {
751 impl->setConnectionState(ConnectedError);
752 }
753 else {
754 impl->setConnectionState(ConnectError);
755 }
756 reset();
757 impl->socket.disconnectFromHost();
758 emit error();
759 }
760 catch (...) {
761 impl->errorString = "Unknown exception during asyncData()";
762 qCritical() << impl->errorString;
763 impl->socketValid = false;
764 if (impl->connectionState == Connected) {
765 impl->setConnectionState(ConnectedError);
766 }
767 else {
768 impl->setConnectionState(ConnectError);
769 }
770 reset();
771 impl->socket.disconnectFromHost();
772 emit error();
773 }
774 64 }
775
776 /****************************************************************************/
777
778
779 14 void Process::findReply(const PdCom::Variable &var)
780 {
781
2/2
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 8 times.
14 if (impl->currentFindVariablePromise.isRunning()) {
782 // variable is cached, result of PdCom::Process::find() will be true
783 6 impl->currentFindVariablePromise.reportResult(var);
784 6 impl->currentFindVariablePromise.reportFinished();
785 }
786
1/2
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 else if (!impl->findVariableQueue.empty()) {
787
1/2
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 auto promise = impl->findVariableQueue.dequeue();
788
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 promise.reportResult(var);
789
1/2
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 promise.reportFinished();
790 }
791 14 }
792
793 void Process::listReply(
794 const std::vector<PdCom::Variable> vars,
795 const std::vector<std::string> dirs)
796 {
797 ListPromise promise;
798 if (impl->currentListPromise.isRunning()) {
799 // list is cached, result of PdCom::Process::list() will be true
800 promise = impl->currentListPromise;
801 }
802 else if (impl->listVariableQueue.empty()) {
803 return;
804 }
805 else {
806 promise = impl->listVariableQueue.dequeue();
807 }
808 QVector<QString> qt_dirs;
809 qt_dirs.reserve(dirs.size());
810 for (const auto &s : dirs) {
811 qt_dirs.append(QString::fromStdString(s));
812 }
813 QVector<PdCom::Variable> qt_vars;
814 qt_vars.resize(vars.size());
815 std::copy(vars.begin(), vars.end(), qt_vars.begin());
816 promise.reportResult(
817 VariableList {std::move(qt_dirs), std::move(qt_vars)});
818 promise.reportFinished();
819 }
820
821 QUrl Process::getUrl() const
822 {
823 return impl->url;
824 }
825
826 int Process::getPort() const
827 {
828 return impl->url.port();
829 }
830
831 QString Process::getHost() const
832 {
833 return impl->url.host();
834 }
835
836 1 void QtPdCom::Process::clientStatisticsReply(
837 std::vector<PdCom::ClientStatistics> statistics)
838 {
839
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (impl->clientStatisticsQueue.empty()) {
840 return;
841 }
842
843
2/4
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
2 auto promise = impl->clientStatisticsQueue.dequeue();
844
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 promise.reportResult(statistics);
845
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 promise.reportFinished();
846 }
847
848 namespace {
849
850 using QtPdCom::details::takes_obj_as_first_parameter;
851
852 struct A
853 {};
854
855 struct F1
856 {
857 int operator()(A &a, int);
858 };
859 struct F2
860 {
861 void operator()(int);
862 };
863
864 struct F3
865 {
866 void operator()();
867 };
868
869 struct F4
870 {
871 void operator()(A &);
872 };
873
874 template <class Fn, class Arg, class Param, class = void>
875 struct is_well_formed: std::false_type
876 {};
877
878 template <class Fn, class Arg, class Param> struct is_well_formed<
879 Fn,
880 Arg,
881 Param,
882 typename std::enable_if<
883 takes_obj_as_first_parameter<Fn, Arg, Param>()
884 || true>::type>: std::true_type
885 {};
886
887 static_assert(
888 takes_obj_as_first_parameter<F1, A, int>(),
889 "F1 takes object ref and int");
890 static_assert(
891 !takes_obj_as_first_parameter<F2, A, int>(),
892 "F2 only takes int");
893
894
895 static_assert(
896 !takes_obj_as_first_parameter<F3, A>(),
897 "F3 takes no arguments");
898 static_assert(
899 takes_obj_as_first_parameter<F4, A>(),
900 "F4 takes obj ref as argument");
901
902 static_assert(is_well_formed<F1, A, int>::value, "");
903 static_assert(is_well_formed<F2, A, int>::value, "");
904 static_assert(
905 !is_well_formed<F3, A, int>::value,
906 "Mismatch beween expected arguments and actual lambda arguments is "
907 "detected");
908
909 } // namespace
910