Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/GNEExternalRunner.cpp
193889 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GNEExternalRunner.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Mar 2023
17
///
18
// External runner for python and external tools
19
/****************************************************************************/
20
#include <config.h>
21
22
#ifdef HAVE_BOOST
23
#ifdef _MSC_VER
24
#pragma warning(push, 0)
25
#define WIN32_LEAN_AND_MEAN
26
#define NOMINMAX
27
#include <windows.h>
28
#include <boost/process.hpp>
29
#include <boost/process/v1/child.hpp>
30
#include <boost/process/v1/io.hpp>
31
#pragma warning(pop)
32
#else
33
#pragma GCC diagnostic push
34
#pragma GCC diagnostic ignored "-Wall"
35
#pragma GCC diagnostic ignored "-Wextra"
36
#include <boost/process.hpp>
37
#include <boost/process/v1/child.hpp>
38
#include <boost/process/v1/io.hpp>
39
#pragma GCC diagnostic pop
40
#endif
41
#endif
42
43
#include <netedit/GNEApplicationWindow.h>
44
#include <netedit/dialogs/run/GNERunDialog.h>
45
#include <utils/gui/events/GUIEvent_Message.h>
46
47
#include "GNEExternalRunner.h"
48
49
// ============================================-===============================
50
// member method definitions
51
// ===========================================================================
52
53
GNEExternalRunner::GNEExternalRunner(GNEApplicationWindow* applicationWindow) :
54
MFXSingleEventThread(applicationWindow->getApp(), applicationWindow) {
55
// set external runner in application window
56
applicationWindow->setExternalRunner(this);
57
}
58
59
60
GNEExternalRunner::~GNEExternalRunner() {}
61
62
63
void
64
GNEExternalRunner::runTool(GNERunDialog* runDialog) {
65
// first abort any running process
66
abort();
67
// set run dialog
68
myRunDialog = runDialog;
69
// set flags
70
myRunning = false;
71
myErrorOccurred = false;
72
// start thread
73
start();
74
}
75
76
77
void
78
GNEExternalRunner::abort() {
79
if (myRunning) {
80
// cancel thread
81
cancel();
82
// reset flags
83
myRunning = false;
84
myErrorOccurred = false;
85
// add event in runDialog
86
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("cancelled by user\n"))), true);
87
}
88
}
89
90
91
bool
92
GNEExternalRunner::isRunning() const {
93
return myRunning;
94
}
95
96
97
bool
98
GNEExternalRunner::errorOccurred() const {
99
return myErrorOccurred;
100
}
101
102
103
FXint
104
GNEExternalRunner::run() {
105
// check if use boost version, or the "classic" version
106
#ifdef HAVE_BOOST
107
try {
108
// declare both streams for read out and err
109
boost::process::v1::opstream in;
110
boost::process::v1::ipstream out;
111
boost::process::v1::ipstream err;
112
// declare run command
113
const auto runCommand = myRunDialog->getRunCommand();
114
// begin running
115
myRunning = true;
116
// Show command
117
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::OUTPUT_OCCURRED, runCommand + "\n"), false);
118
// run command derivating the std_out to out and std_err to err
119
boost::process::v1::child c(runCommand,
120
boost::process::v1::std_in < in,
121
boost::process::v1::std_out > out,
122
boost::process::v1::std_err > err);
123
// declare a stdout reader thread
124
std::thread outReaderThread([&out, this]() {
125
std::string buffer;
126
// read until a \n appears
127
while (std::getline(out, buffer)) {
128
// clear '\r' character
129
if (!buffer.empty() && (buffer.back() == '\r')) {
130
buffer.pop_back();
131
}
132
buffer += "\n";
133
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::OUTPUT_OCCURRED, buffer.c_str()), true);
134
}
135
});
136
// declare a stderr reader thread
137
std::thread errReaderThread([&err, this]() {
138
std::string buffer;
139
// read until a \n appears
140
while (std::getline(err, buffer)) {
141
// clear '\r' character
142
if (!buffer.empty() && (buffer.back() == '\r')) {
143
buffer.pop_back();
144
}
145
buffer += "\n";
146
// show errors as warnings
147
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::WARNING_OCCURRED, buffer.c_str()), true);
148
}
149
});
150
// wait until child process is finish
151
c.wait();
152
// use readers for read output
153
if (outReaderThread.joinable()) {
154
outReaderThread.join();
155
}
156
if (errReaderThread.joinable()) {
157
errReaderThread.join();
158
}
159
// end running
160
myRunning = false;
161
// send end signal
162
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::TOOL_ENDED, ""), true);
163
// return exit code
164
return c.exit_code();
165
} catch (...) {
166
myRunning = false;
167
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, TL("Error running tool using boost::process")), true);
168
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::TOOL_ENDED, ""), true);
169
return EXIT_FAILURE;
170
}
171
#else
172
// get run command
173
const std::string runCommand = myRunDialog->getRunCommand();
174
// declare buffer
175
char buffer[128];
176
for (int i = 0; i < 128; i++) {
177
buffer[i] = '\0';
178
}
179
// open process showing std::err in console
180
#ifdef WIN32
181
myPipe = _popen(StringUtils::transcodeToLocal(runCommand + " 2>&1").c_str(), "r");
182
#else
183
myPipe = popen((runCommand + " 2>&1").c_str(), "r");
184
#endif
185
if (!myPipe) {
186
// set error ocurred flag
187
myErrorOccurred = true;
188
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, "popen() failed!"), false);
189
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::TOOL_ENDED, ""), true);
190
return 1;
191
} else {
192
// set running flag
193
myRunning = true;
194
// Show command
195
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::OUTPUT_OCCURRED, runCommand + "\n"), false);
196
// start process
197
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
198
try {
199
// add buffer
200
while (fgets(buffer, sizeof buffer, myPipe) != NULL) {
201
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::OUTPUT_OCCURRED, buffer), true);
202
}
203
} catch (...) {
204
// close process
205
#ifdef WIN32
206
_pclose(myPipe);
207
#else
208
pclose(myPipe);
209
#endif
210
// set flags
211
myRunning = false;
212
myErrorOccurred = true;
213
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("error processing command\n"))), true);
214
return 1;
215
}
216
}
217
// close process
218
#ifdef WIN32
219
_pclose(myPipe);
220
#else
221
pclose(myPipe);
222
#endif
223
myPipe = nullptr;
224
// set running flag
225
myRunning = false;
226
// end process
227
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
228
myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::TOOL_ENDED, ""), true);
229
return 1;
230
#endif
231
}
232
233
/****************************************************************************/
234
235