Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2004-2024 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 OutputDevice.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date 2004
19 : ///
20 : // Static storage of an output device and its base (abstract) implementation
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <map>
25 : #include <fstream>
26 : #include <sstream>
27 : #include <string>
28 : #include <iomanip>
29 : #ifdef WIN32
30 : #define NOMINMAX
31 : #include <windows.h>
32 : #undef NOMINMAX
33 : #endif
34 : #include "OutputDevice.h"
35 : #include "OutputDevice_File.h"
36 : #include "OutputDevice_COUT.h"
37 : #include "OutputDevice_CERR.h"
38 : #include "OutputDevice_Network.h"
39 : #include "PlainXMLFormatter.h"
40 : #include <utils/common/StringUtils.h>
41 : #include <utils/common/UtilExceptions.h>
42 : #include <utils/common/FileHelpers.h>
43 : #include <utils/common/ToString.h>
44 : #include <utils/common/MsgHandler.h>
45 : #include <utils/options/OptionsCont.h>
46 : #include <utils/options/OptionsIO.h>
47 :
48 :
49 : // ===========================================================================
50 : // static member definitions
51 : // ===========================================================================
52 : std::map<std::string, OutputDevice*> OutputDevice::myOutputDevices;
53 : int OutputDevice::myPrevConsoleCP = -1;
54 :
55 :
56 : // ===========================================================================
57 : // static method definitions
58 : // ===========================================================================
59 : OutputDevice&
60 17843096 : OutputDevice::getDevice(const std::string& name, bool usePrefix) {
61 : #ifdef WIN32
62 : // fix the windows console output on first call
63 : if (myPrevConsoleCP == -1) {
64 : myPrevConsoleCP = GetConsoleOutputCP();
65 : SetConsoleOutputCP(CP_UTF8);
66 : }
67 : #endif
68 : // check whether the device has already been aqcuired
69 17843096 : if (myOutputDevices.find(name) != myOutputDevices.end()) {
70 17565601 : return *myOutputDevices[name];
71 : }
72 : // build the device
73 : OutputDevice* dev = nullptr;
74 : // check whether the device shall print to stdout
75 277495 : if (name == "stdout") {
76 85108 : dev = OutputDevice_COUT::getDevice();
77 192387 : } else if (name == "stderr") {
78 126348 : dev = OutputDevice_CERR::getDevice();
79 66039 : } else if (FileHelpers::isSocket(name)) {
80 : try {
81 4 : const bool ipv6 = name[0] == '['; // IPv6 adresses may be written like '[::1]:8000'
82 4 : const size_t sepIndex = name.find(":", ipv6 ? name.find("]") : 0);
83 4 : const int port = StringUtils::toInt(name.substr(sepIndex + 1));
84 8 : dev = new OutputDevice_Network(ipv6 ? name.substr(1, sepIndex - 2) : name.substr(0, sepIndex), port);
85 0 : } catch (NumberFormatException&) {
86 0 : throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric.");
87 0 : } catch (EmptyData&) {
88 0 : throw IOError(TL("No port number given."));
89 0 : }
90 : } else {
91 66035 : std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name;
92 132076 : if (usePrefix && OptionsCont::getOptions().isSet("output-prefix") && name2 != "/dev/null") {
93 1348 : std::string prefix = OptionsCont::getOptions().getString("output-prefix");
94 : const std::string::size_type metaTimeIndex = prefix.find("TIME");
95 674 : if (metaTimeIndex != std::string::npos) {
96 0 : const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
97 : char buffer [80];
98 0 : struct tm* timeinfo = localtime(&rawtime);
99 0 : strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
100 0 : prefix.replace(metaTimeIndex, 4, buffer);
101 : }
102 1348 : name2 = FileHelpers::prependToLastPathComponent(prefix, name);
103 : }
104 132154 : name2 = StringUtils::substituteEnvironment(name2, &OptionsIO::getLoadTime());
105 66035 : const int len = (int)name.length();
106 132034 : dev = new OutputDevice_File(name2, len > 3 && name.substr(len - 3) == ".gz");
107 : }
108 277411 : dev->setPrecision();
109 277411 : dev->getOStream() << std::setiosflags(std::ios::fixed);
110 277411 : myOutputDevices[name] = dev;
111 277411 : return *dev;
112 : }
113 :
114 :
115 : bool
116 951566 : OutputDevice::createDeviceByOption(const std::string& optionName,
117 : const std::string& rootElement,
118 : const std::string& schemaFile) {
119 951566 : if (!OptionsCont::getOptions().isSet(optionName)) {
120 : return false;
121 : }
122 44198 : OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
123 22077 : if (rootElement != "") {
124 44152 : dev.writeXMLHeader(rootElement, schemaFile);
125 : }
126 : return true;
127 : }
128 :
129 :
130 : OutputDevice&
131 17230108 : OutputDevice::getDeviceByOption(const std::string& optionName) {
132 17230108 : std::string devName = OptionsCont::getOptions().getString(optionName);
133 17230108 : if (myOutputDevices.find(devName) == myOutputDevices.end()) {
134 0 : throw InvalidArgument("Output device '" + devName + "' for option '" + optionName + "' has not been created.");
135 : }
136 34460216 : return OutputDevice::getDevice(devName);
137 : }
138 :
139 :
140 : void
141 0 : OutputDevice::flushAll() {
142 0 : for (auto item : myOutputDevices) {
143 : item.second->flush();
144 : }
145 0 : }
146 :
147 :
148 : void
149 166609 : OutputDevice::closeAll(bool keepErrorRetrievers) {
150 : std::vector<OutputDevice*> errorDevices;
151 : std::vector<OutputDevice*> nonErrorDevices;
152 469052 : for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
153 302443 : if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
154 166914 : errorDevices.push_back(i->second);
155 : } else {
156 135529 : nonErrorDevices.push_back(i->second);
157 : }
158 : }
159 302138 : for (OutputDevice* const dev : nonErrorDevices) {
160 : try {
161 135529 : dev->close();
162 1 : } catch (const IOError& e) {
163 1 : WRITE_ERROR(TL("Error on closing output devices."));
164 1 : WRITE_ERROR(e.what());
165 1 : }
166 : }
167 166609 : if (!keepErrorRetrievers) {
168 252829 : for (OutputDevice* const dev : errorDevices) {
169 : try {
170 126495 : dev->close();
171 0 : } catch (const IOError& e) {
172 : std::cerr << "Error on closing error output devices." << std::endl;
173 0 : std::cerr << e.what() << std::endl;
174 0 : }
175 : }
176 : #ifdef WIN32
177 : if (myPrevConsoleCP != -1) {
178 : SetConsoleOutputCP(myPrevConsoleCP);
179 : }
180 : #endif
181 : }
182 166609 : }
183 :
184 :
185 : std::string
186 1474795 : OutputDevice::realString(const double v, const int precision) {
187 1474795 : std::ostringstream oss;
188 1474795 : if (v == 0) {
189 329474 : return "0";
190 : }
191 1145321 : if (fabs(v) < pow(10., -precision)) {
192 : oss.setf(std::ios::scientific, std::ios::floatfield);
193 : } else {
194 : oss.setf(std::ios::fixed, std::ios::floatfield); // use decimal format
195 : oss.setf(std::ios::showpoint); // print decimal point
196 : oss << std::setprecision(precision);
197 : }
198 : oss << v;
199 : return oss.str();
200 1474795 : }
201 :
202 :
203 : // ===========================================================================
204 : // member method definitions
205 : // ===========================================================================
206 1488202 : OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
207 1488202 : myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
208 1488202 : }
209 :
210 :
211 1488031 : OutputDevice::~OutputDevice() {
212 1488031 : delete myFormatter;
213 1488031 : }
214 :
215 :
216 : bool
217 0 : OutputDevice::ok() {
218 0 : return getOStream().good();
219 : }
220 :
221 :
222 : const std::string&
223 0 : OutputDevice::getFilename() {
224 0 : return myFilename;
225 : }
226 :
227 : void
228 277371 : OutputDevice::close() {
229 619810 : while (closeTag()) {}
230 383302 : for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
231 383302 : if (i->second == this) {
232 : myOutputDevices.erase(i);
233 : break;
234 : }
235 : }
236 277370 : MsgHandler::removeRetrieverFromAllInstances(this);
237 277370 : delete this;
238 277370 : }
239 :
240 :
241 : void
242 3627227 : OutputDevice::setPrecision(int precision) {
243 3627227 : getOStream() << std::setprecision(precision);
244 3627227 : }
245 :
246 :
247 : int
248 49779 : OutputDevice::precision() {
249 49779 : return (int)getOStream().precision();
250 : }
251 :
252 :
253 : bool
254 61536 : OutputDevice::writeXMLHeader(const std::string& rootElement,
255 : const std::string& schemaFile,
256 : std::map<SumoXMLAttr, std::string> attrs,
257 : bool includeConfig) {
258 61536 : if (schemaFile != "") {
259 58000 : attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
260 58000 : attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
261 : }
262 61536 : return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, includeConfig);
263 : }
264 :
265 :
266 : OutputDevice&
267 7593666 : OutputDevice::openTag(const std::string& xmlElement) {
268 7593666 : myFormatter->openTag(getOStream(), xmlElement);
269 7593666 : return *this;
270 : }
271 :
272 :
273 : OutputDevice&
274 5729043 : OutputDevice::openTag(const SumoXMLTag& xmlElement) {
275 5729043 : myFormatter->openTag(getOStream(), xmlElement);
276 5729043 : return *this;
277 : }
278 :
279 :
280 : bool
281 13665671 : OutputDevice::closeTag(const std::string& comment) {
282 13665671 : if (myFormatter->closeTag(getOStream(), comment)) {
283 13388301 : postWriteHook();
284 13388300 : return true;
285 : }
286 : return false;
287 : }
288 :
289 :
290 : void
291 50635123 : OutputDevice::postWriteHook() {}
292 :
293 :
294 : void
295 647206 : OutputDevice::inform(const std::string& msg, const bool progress) {
296 647206 : if (progress) {
297 28610 : getOStream() << msg;
298 : } else {
299 1237192 : getOStream() << msg << '\n';
300 : }
301 647206 : postWriteHook();
302 647206 : }
303 :
304 :
305 : /****************************************************************************/
|