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
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
46
47#include "GNEExternalRunner.h"
48
49// ============================================-===============================
50// member method definitions
51// ===========================================================================
52
54 MFXSingleEventThread(applicationWindow->getApp(), applicationWindow) {
55 // set external runner in application window
56 applicationWindow->setExternalRunner(this);
57}
58
59
61
62
63void
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
77void
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
91bool
93 return myRunning;
94}
95
96
97bool
101
102
103FXint
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
118 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
119 // run command derivating the std_out to out and std_err to err
120 boost::process::v1::child c(runCommand,
121 boost::process::v1::std_in < in,
122 boost::process::v1::std_out > out,
123 boost::process::v1::std_err > err);
124 // declare a stdout reader thread
125 std::thread outReaderThread([&out, this]() {
126 std::string buffer;
127 // read until a \n appears
128 while (std::getline(out, buffer)) {
129 // clear '\r' character
130 if (!buffer.empty() && (buffer.back() == '\r')) {
131 buffer.pop_back();
132 }
133 buffer += "\n";
135 }
136 });
137 // declare a stderr reader thread
138 std::thread errReaderThread([&err, this]() {
139 std::string buffer;
140 // read until a \n appears
141 while (std::getline(err, buffer)) {
142 // clear '\r' character
143 if (!buffer.empty() && (buffer.back() == '\r')) {
144 buffer.pop_back();
145 }
146 buffer += "\n";
147 // show errors as warnings
149 }
150 });
151 // wait until child process is finish
152 c.wait();
153 // use readers for read output
154 if (outReaderThread.joinable()) {
155 outReaderThread.join();
156 }
157 if (errReaderThread.joinable()) {
158 errReaderThread.join();
159 }
160 // end running
161 myRunning = false;
162 // send end signal
163 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
165 // return exit code
166 return c.exit_code();
167 } catch (...) {
168 myRunning = false;
169 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, TL("Error running tool using boost::process")), true);
171 return EXIT_FAILURE;
172 }
173#else
174 // get run command
175 const std::string runCommand = myRunDialog->getRunCommand();
176 // declare buffer
177 char buffer[128];
178 for (int i = 0; i < 128; i++) {
179 buffer[i] = '\0';
180 }
181 // open process showing std::err in console
182#ifdef WIN32
183 myPipe = _popen(StringUtils::transcodeToLocal(runCommand + " 2>&1").c_str(), "r");
184#else
185 myPipe = popen((runCommand + " 2>&1").c_str(), "r");
186#endif
187 if (!myPipe) {
188 // set error ocurred flag
189 myErrorOccurred = true;
190 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, "popen() failed!"), false);
192 return 1;
193 } else {
194 // set running flag
195 myRunning = true;
196 // Show command
198 // start process
199 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
200 try {
201 // add buffer
202 while (fgets(buffer, sizeof buffer, myPipe) != NULL) {
204 }
205 } catch (...) {
206 // close process
207#ifdef WIN32
208 _pclose(myPipe);
209#else
210 pclose(myPipe);
211#endif
212 // set flags
213 myRunning = false;
214 myErrorOccurred = true;
215 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("error processing command\n"))), true);
216 return 1;
217 }
218 }
219 // close process
220#ifdef WIN32
221 _pclose(myPipe);
222#else
223 pclose(myPipe);
224#endif
225 myPipe = nullptr;
226 // set running flag
227 myRunning = false;
228 // end process
229 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
231 return 1;
232#endif
233}
234
235/****************************************************************************/
@ 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