Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
GNEExternalRunner.cpp
Go to the documentation of this file.
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/****************************************************************************/
18// External runner for python and external tools
19/****************************************************************************/
20#include <config.h>
21#include <utils/xml/XMLSubSys.h>
22
23#ifdef HAVE_BOOST
24#ifdef _MSC_VER
25#include <windows.h>
26#endif
27#if __has_include(<boost/process/v1.hpp>) // Boost 1.86+
28#include <boost/process/v1.hpp>
29namespace bp = boost::process::v1;
30#else
31#include <boost/process.hpp>
32namespace bp = boost::process;
33#endif
34#endif
35
39
40#include "GNEExternalRunner.h"
41
42// ============================================-===============================
43// member method definitions
44// ===========================================================================
45
47 MFXSingleEventThread(applicationWindow->getApp(), applicationWindow) {
48 // set external runner in application window
49 applicationWindow->setExternalRunner(this);
50}
51
52
54
55
56void
58 // first abort any running process
59 abort();
60 // set run dialog
61 myRunDialog = runDialog;
62 // set flags
63 myRunning = false;
64 myErrorOccurred = false;
65 // start thread
66 start();
67}
68
69
70void
72 if (myRunning) {
73 // cancel thread
74 cancel();
75 // reset flags
76 myRunning = false;
77 myErrorOccurred = false;
78 // add event in runDialog
79 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("cancelled by user\n"))), true);
80 }
81}
82
83
84bool
86 return myRunning;
87}
88
89
90bool
94
95
96FXint
98// check if use boost version, or the "classic" version
99#ifdef HAVE_BOOST
100 try {
101 // declare both streams for read out and err
102 bp::opstream in;
103 bp::ipstream out;
104 bp::ipstream err;
105 // declare run command
106 const auto runCommand = myRunDialog->getRunCommand();
107 // begin running
108 myRunning = true;
109 // Show command
111 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
112 // run command derivating the std_out to out and std_err to err
113 bp::child c(runCommand, bp::std_in < in, bp::std_out > out, bp::std_err > err);
114 // declare a stdout reader thread
115 std::thread outReaderThread([&out, this]() {
116 std::string buffer;
117 // read until a \n appears
118 while (std::getline(out, buffer)) {
119 // clear '\r' character
120 if (!buffer.empty() && (buffer.back() == '\r')) {
121 buffer.pop_back();
122 }
123 buffer += "\n";
125 }
126 });
127 // declare a stderr reader thread
128 std::thread errReaderThread([&err, this]() {
129 std::string buffer;
130 // read until a \n appears
131 while (std::getline(err, buffer)) {
132 // clear '\r' character
133 if (!buffer.empty() && (buffer.back() == '\r')) {
134 buffer.pop_back();
135 }
136 buffer += "\n";
137 // show errors as warnings
139 }
140 });
141 // wait until child process is finish
142 c.wait();
143 // use readers for read output
144 if (outReaderThread.joinable()) {
145 outReaderThread.join();
146 }
147 if (errReaderThread.joinable()) {
148 errReaderThread.join();
149 }
150 // end running
151 myRunning = false;
152 // send end signal
153 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
155 // return exit code
156 return c.exit_code();
157 } catch (...) {
158 myRunning = false;
159 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, TL("Error running tool using boost::process")), true);
161 return EXIT_FAILURE;
162 }
163#else
164 // get run command
165 const std::string runCommand = myRunDialog->getRunCommand();
166 // declare buffer
167 char buffer[128];
168 for (int i = 0; i < 128; i++) {
169 buffer[i] = '\0';
170 }
171 // open process showing std::err in console
172#ifdef WIN32
173 myPipe = _popen(XMLSubSys::transcodeToLocal(runCommand + " 2>&1").c_str(), "r");
174#else
175 myPipe = popen((runCommand + " 2>&1").c_str(), "r");
176#endif
177 if (!myPipe) {
178 // set error ocurred flag
179 myErrorOccurred = true;
180 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, "popen() failed!"), false);
182 return 1;
183 } else {
184 // set running flag
185 myRunning = true;
186 // Show command
188 // start process
189 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
190 try {
191 // add buffer
192 while (fgets(buffer, sizeof buffer, myPipe) != NULL) {
194 }
195 } catch (...) {
196 // close process
197#ifdef WIN32
198 _pclose(myPipe);
199#else
200 pclose(myPipe);
201#endif
202 // set flags
203 myRunning = false;
204 myErrorOccurred = true;
205 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("error processing command\n"))), true);
206 return 1;
207 }
208 }
209 // close process
210#ifdef WIN32
211 _pclose(myPipe);
212#else
213 pclose(myPipe);
214#endif
215 myPipe = nullptr;
216 // set running flag
217 myRunning = false;
218 // end process
219 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
221 return 1;
222#endif
223}
224
225/****************************************************************************/
@ MESSAGE_OCCURRED
send when a message occurred
@ ERROR_OCCURRED
send when a error occurred
@ OUTPUT_OCCURRED
send when a tool produces output
@ TOOL_ENDED
send when a tool finishes
@ WARNING_OCCURRED
send when a warning occurred
#define TL(string)
Definition MsgHandler.h:304
void setExternalRunner(GNEExternalRunner *externalRunner)
set external runner
bool myErrorOccurred
flag for check if during execution an error was Occurred
~GNEExternalRunner()
destructor
GNERunDialog * myRunDialog
pointer to current run dialog
bool errorOccurred() const
check if during execution an error was Occurred
GNEExternalRunner(GNEApplicationWindow *applicationWindow)
Constructor.
FILE * myPipe
pipe file
bool isRunning() const
check if is running
void runTool(GNERunDialog *runDialog)
run tool called from dialog
void abort()
abort running
FXint run()
starts the thread. The thread ends after the tool is finished
bool myRunning
flag for check if we have a running process
void addEvent(GUIEvent *event, const bool signal)
add event in the queue
virtual std::string getRunCommand() const =0
get run command
static std::string transcodeToLocal(const std::string &utf8String)
convert a string from UTF-8 to the local codepage