c@94
|
1
|
c@94
|
2 #ifndef PIPER_PROCESS_QT_TRANSPORT_H
|
c@94
|
3 #define PIPER_PROCESS_QT_TRANSPORT_H
|
c@94
|
4
|
c@94
|
5 #include "SynchronousTransport.h"
|
c@94
|
6
|
c@94
|
7 #include <QProcess>
|
c@94
|
8 #include <QString>
|
c@100
|
9 #include <QMutex>
|
c@94
|
10
|
c@94
|
11 #include <iostream>
|
c@94
|
12
|
c@97
|
13 namespace piper_vamp {
|
c@97
|
14 namespace client {
|
c@94
|
15
|
c@100
|
16 /**
|
c@100
|
17 * A SynchronousTransport implementation that spawns a sub-process
|
c@100
|
18 * using Qt's QProcess abstraction and talks to it via stdin/stdout
|
c@100
|
19 * channels. Calls are completely serialized; the protocol only
|
c@100
|
20 * supports one call in process at a time, and therefore the transport
|
c@100
|
21 * only allows one at a time. This class is thread-safe because it
|
c@100
|
22 * serializes explicitly using a mutex.
|
c@100
|
23 */
|
c@94
|
24 class ProcessQtTransport : public SynchronousTransport
|
c@94
|
25 {
|
c@94
|
26 public:
|
c@101
|
27 ProcessQtTransport(std::string processName) :
|
c@94
|
28 m_completenessChecker(0) {
|
c@94
|
29 m_process = new QProcess();
|
c@94
|
30 m_process->setReadChannel(QProcess::StandardOutput);
|
c@94
|
31 m_process->setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
c@101
|
32 m_process->start(QString::fromStdString(processName));
|
c@94
|
33 if (!m_process->waitForStarted()) {
|
c@94
|
34 std::cerr << "server failed to start" << std::endl;
|
c@94
|
35 delete m_process;
|
c@94
|
36 m_process = nullptr;
|
c@94
|
37 }
|
c@94
|
38 }
|
c@94
|
39
|
c@94
|
40 ~ProcessQtTransport() {
|
c@94
|
41 if (m_process) {
|
c@94
|
42 if (m_process->state() != QProcess::NotRunning) {
|
c@94
|
43 m_process->closeWriteChannel();
|
c@94
|
44 m_process->waitForFinished(200);
|
c@94
|
45 m_process->close();
|
c@94
|
46 m_process->waitForFinished();
|
c@94
|
47 std::cerr << "server exited" << std::endl;
|
c@94
|
48 }
|
c@94
|
49 delete m_process;
|
c@94
|
50 }
|
c@94
|
51 }
|
c@94
|
52
|
c@94
|
53 void
|
c@94
|
54 setCompletenessChecker(MessageCompletenessChecker *checker) {
|
c@94
|
55 //!!! ownership?
|
c@94
|
56 m_completenessChecker = checker;
|
c@94
|
57 }
|
c@94
|
58
|
c@94
|
59 bool
|
c@94
|
60 isOK() const override {
|
c@94
|
61 return m_process != nullptr;
|
c@94
|
62 }
|
c@94
|
63
|
c@94
|
64 std::vector<char>
|
c@94
|
65 call(const char *ptr, size_t size) override {
|
c@94
|
66
|
c@100
|
67 QMutexLocker locker(&m_mutex);
|
c@100
|
68
|
c@94
|
69 if (!m_completenessChecker) {
|
c@94
|
70 throw std::logic_error("No completeness checker set on transport");
|
c@94
|
71 }
|
c@94
|
72
|
c@94
|
73 m_process->write(ptr, size);
|
c@94
|
74
|
c@94
|
75 std::vector<char> buffer;
|
c@94
|
76 bool complete = false;
|
c@94
|
77
|
c@94
|
78 while (!complete) {
|
c@94
|
79
|
c@94
|
80 qint64 byteCount = m_process->bytesAvailable();
|
c@94
|
81
|
c@101
|
82 if (!byteCount) {
|
c@101
|
83 std::cerr << "waiting for data from server..." << endl;
|
c@101
|
84 m_process->waitForReadyRead(1000);
|
c@94
|
85 if (m_process->state() == QProcess::NotRunning) {
|
c@94
|
86 std::cerr << "ERROR: Subprocess exited: Load failed" << std::endl;
|
c@94
|
87 throw std::runtime_error("Piper server exited unexpectedly");
|
c@94
|
88 }
|
c@94
|
89 } else {
|
c@94
|
90 size_t formerSize = buffer.size();
|
c@94
|
91 buffer.resize(formerSize + byteCount);
|
c@94
|
92 m_process->read(buffer.data() + formerSize, byteCount);
|
c@94
|
93 complete = m_completenessChecker->isComplete(buffer);
|
c@94
|
94 }
|
c@94
|
95 }
|
c@94
|
96
|
c@94
|
97 return buffer;
|
c@94
|
98 }
|
c@94
|
99
|
c@94
|
100 private:
|
c@94
|
101 MessageCompletenessChecker *m_completenessChecker; //!!! I don't own this (currently)
|
c@94
|
102 QProcess *m_process; // I own this
|
c@100
|
103 QMutex m_mutex;
|
c@94
|
104 };
|
c@94
|
105
|
c@94
|
106 }
|
c@94
|
107 }
|
c@94
|
108
|
c@94
|
109 #endif
|