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#pragma warning(push)
26#define WIN32_LEAN_AND_MEAN
27#define NOMINMAX
28#include <windows.h>
29#endif
30#if __has_include(<boost/process/v1.hpp>) // Boost 1.86+
31#include <boost/process/v1.hpp>
32namespace bp = boost::process::v1;
33#else
34#include <boost/process.hpp>
35namespace bp = boost::process;
36#endif
37#ifdef _MSC_VER
38#pragma warning(pop)
39#endif
40#endif
41
45
46#include "GNEExternalRunner.h"
47
48// ============================================-===============================
49// member method definitions
50// ===========================================================================
51
53 MFXSingleEventThread(applicationWindow->getApp(), applicationWindow) {
54 // set external runner in application window
55 applicationWindow->setExternalRunner(this);
56}
57
58
60
61
62void
64 // first abort any running process
65 abort();
66 // set run dialog
67 myRunDialog = runDialog;
68 // set flags
69 myRunning = false;
70 myErrorOccurred = false;
71 // start thread
72 start();
73}
74
75
76void
78 if (myRunning) {
79 // cancel thread
80 cancel();
81 // reset flags
82 myRunning = false;
83 myErrorOccurred = false;
84 // add event in runDialog
85 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("cancelled by user\n"))), true);
86 }
87}
88
89
90bool
92 return myRunning;
93}
94
95
96bool
100
101
102FXint
104// check if use boost version, or the "classic" version
105#ifdef HAVE_BOOST
106 try {
107 // declare both streams for read out and err
108 bp::opstream in;
109 bp::ipstream out;
110 bp::ipstream err;
111 // declare run command
112 const auto runCommand = myRunDialog->getRunCommand();
113 // begin running
114 myRunning = true;
115 // Show command
117 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
118 // run command derivating the std_out to out and std_err to err
119 bp::child c(runCommand, bp::std_in < in, bp::std_out > out, bp::std_err > err);
120 // declare a stdout reader thread
121 std::thread outReaderThread([&out, this]() {
122 std::string buffer;
123 // read until a \n appears
124 while (std::getline(out, buffer)) {
125 // clear '\r' character
126 if (!buffer.empty() && (buffer.back() == '\r')) {
127 buffer.pop_back();
128 }
129 buffer += "\n";
131 }
132 });
133 // declare a stderr reader thread
134 std::thread errReaderThread([&err, this]() {
135 std::string buffer;
136 // read until a \n appears
137 while (std::getline(err, buffer)) {
138 // clear '\r' character
139 if (!buffer.empty() && (buffer.back() == '\r')) {
140 buffer.pop_back();
141 }
142 buffer += "\n";
143 // show errors as warnings
145 }
146 });
147 // wait until child process is finish
148 c.wait();
149 // use readers for read output
150 if (outReaderThread.joinable()) {
151 outReaderThread.join();
152 }
153 if (errReaderThread.joinable()) {
154 errReaderThread.join();
155 }
156 // end running
157 myRunning = false;
158 // send end signal
159 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
161 // return exit code
162 return c.exit_code();
163 } catch (...) {
164 myRunning = false;
165 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, TL("Error running tool using boost::process")), true);
167 return EXIT_FAILURE;
168 }
169#else
170 // get run command
171 const std::string runCommand = myRunDialog->getRunCommand();
172 // declare buffer
173 char buffer[128];
174 for (int i = 0; i < 128; i++) {
175 buffer[i] = '\0';
176 }
177 // open process showing std::err in console
178#ifdef WIN32
179 myPipe = _popen(XMLSubSys::transcodeToLocal(runCommand + " 2>&1").c_str(), "r");
180#else
181 myPipe = popen((runCommand + " 2>&1").c_str(), "r");
182#endif
183 if (!myPipe) {
184 // set error ocurred flag
185 myErrorOccurred = true;
186 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, "popen() failed!"), false);
188 return 1;
189 } else {
190 // set running flag
191 myRunning = true;
192 // Show command
194 // start process
195 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("starting process...\n"))), true);
196 try {
197 // add buffer
198 while (fgets(buffer, sizeof buffer, myPipe) != NULL) {
200 }
201 } catch (...) {
202 // close process
203#ifdef WIN32
204 _pclose(myPipe);
205#else
206 pclose(myPipe);
207#endif
208 // set flags
209 myRunning = false;
210 myErrorOccurred = true;
211 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::ERROR_OCCURRED, std::string(TL("error processing command\n"))), true);
212 return 1;
213 }
214 }
215 // close process
216#ifdef WIN32
217 _pclose(myPipe);
218#else
219 pclose(myPipe);
220#endif
221 myPipe = nullptr;
222 // set running flag
223 myRunning = false;
224 // end process
225 myRunDialog->addEvent(new GUIEvent_Message(GUIEventType::MESSAGE_OCCURRED, std::string(TL("process finished\n"))), false);
227 return 1;
228#endif
229}
230
231/****************************************************************************/
@ 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