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