Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
OutputDevice.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2004-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/****************************************************************************/
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"
39#include "PlainXMLFormatter.h"
47
48
49// ===========================================================================
50// static member definitions
51// ===========================================================================
52std::map<std::string, OutputDevice*> OutputDevice::myOutputDevices;
54
55
56// ===========================================================================
57// static method definitions
58// ===========================================================================
60OutputDevice::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 if (myOutputDevices.find(name) != myOutputDevices.end()) {
70 return *myOutputDevices[name];
71 }
72 // build the device
74 const int len = (int)name.length();
75 bool isParquet = (oc.exists("output.format") && oc.getString("output.format") == "parquet") || (len > 8 && name.substr(len - 8) == ".parquet");
76#ifndef HAVE_PARQUET
77 if (isParquet) {
78 WRITE_WARNING("Compiled without Parquet support, falling back to XML.")
79 isParquet = false;
80 }
81#endif
82 OutputDevice* dev = nullptr;
83 // check whether the device shall print to stdout
84 if (name == "stdout") {
86 } else if (name == "stderr") {
88 } else if (FileHelpers::isSocket(name)) {
89 try {
90 const bool ipv6 = name[0] == '['; // IPv6 addresses may be written like '[::1]:8000'
91 const size_t sepIndex = name.find(":", ipv6 ? name.find("]") : 0);
92 const int port = StringUtils::toInt(name.substr(sepIndex + 1));
93 dev = new OutputDevice_Network(ipv6 ? name.substr(1, sepIndex - 2) : name.substr(0, sepIndex), port);
94 } catch (NumberFormatException&) {
95 throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric.");
96 } catch (EmptyData&) {
97 throw IOError(TL("No port number given."));
98 }
99 } else {
100 std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name;
101 if (usePrefix && oc.isSet("output-prefix") && name2 != "/dev/null") {
102 std::string prefix = oc.getString("output-prefix");
103 const std::string::size_type metaTimeIndex = prefix.find("TIME");
104 if (metaTimeIndex != std::string::npos) {
105 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
106 char buffer [80];
107 struct tm* timeinfo = localtime(&rawtime);
108 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
109 prefix.replace(metaTimeIndex, 4, buffer);
110 }
111 name2 = FileHelpers::prependToLastPathComponent(prefix, name);
112 }
113 if (usePrefix && oc.isSet("output-suffix") && name2 != "/dev/null") {
114 std::string suffix = oc.getString("output-suffix");
115 const std::string::size_type metaTimeIndex = suffix.find("TIME");
116 if (metaTimeIndex != std::string::npos) {
117 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
118 char buffer [80];
119 struct tm* timeinfo = localtime(&rawtime);
120 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
121 suffix.replace(metaTimeIndex, 4, buffer);
122 }
123 name2 = FileHelpers::appendBeforeExtension(name2, suffix);
124 }
126 dev = new OutputDevice_File(name2, isParquet);
127 }
128 if ((oc.exists("output.format") && oc.getString("output.format") == "csv") || (len > 4 && name.substr(len - 4) == ".csv") || (len > 7 && name.substr(len - 7) == ".csv.gz")) {
129 dev->setFormatter(new CSVFormatter(oc.getString("output.column-header"), oc.getString("output.column-separator")[0]));
130 }
131#ifdef HAVE_PARQUET
132 if (isParquet) {
133 dev->setFormatter(new ParquetFormatter(oc.getString("output.column-header"), oc.getString("output.compression")));
134 }
135#endif
136 dev->setPrecision();
137 dev->getOStream() << std::setiosflags(std::ios::fixed);
138 dev->myWriteMetadata = oc.exists("write-metadata") && oc.getBool("write-metadata");
139 myOutputDevices[name] = dev;
140 return *dev;
141}
142
143
144bool
145OutputDevice::createDeviceByOption(const std::string& optionName,
146 const std::string& rootElement,
147 const std::string& schemaFile,
148 const int maximumDepth) {
149 if (!OptionsCont::getOptions().isSet(optionName)) {
150 return false;
151 }
152 OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
153 dev.setExpectedAttributes(0, maximumDepth);
154 if (rootElement != "") {
155 dev.writeXMLHeader(rootElement, schemaFile);
156 }
157 return true;
158}
159
160
162OutputDevice::getDeviceByOption(const std::string& optionName) {
163 std::string devName = OptionsCont::getOptions().getString(optionName);
164 if (myOutputDevices.find(devName) == myOutputDevices.end()) {
165 throw InvalidArgument("Output device '" + devName + "' for option '" + optionName + "' has not been created.");
166 }
167 return OutputDevice::getDevice(devName);
168}
169
170
171void
173 for (auto item : myOutputDevices) {
174 item.second->flush();
175 }
176}
177
178
179void
180OutputDevice::closeAll(bool keepErrorRetrievers) {
181 std::vector<OutputDevice*> errorDevices;
182 std::vector<OutputDevice*> nonErrorDevices;
183 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
184 if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
185 errorDevices.push_back(i->second);
186 } else {
187 nonErrorDevices.push_back(i->second);
188 }
189 }
190 for (OutputDevice* const dev : nonErrorDevices) {
191 try {
192 dev->close();
193 } catch (const IOError& e) {
194 WRITE_ERROR(TL("Error on closing output devices."));
195 WRITE_ERROR(e.what());
196 }
197 }
198 if (!keepErrorRetrievers) {
199 for (OutputDevice* const dev : errorDevices) {
200 try {
201 dev->close();
202 } catch (const IOError& e) {
203 std::cerr << "Error on closing error output devices." << std::endl;
204 std::cerr << e.what() << std::endl;
205 }
206 }
207#ifdef WIN32
208 if (myPrevConsoleCP != -1) {
209 SetConsoleOutputCP(myPrevConsoleCP);
210 }
211#endif
212 }
213}
214
215
216// ===========================================================================
217// member method definitions
218// ===========================================================================
219OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
220 myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
221}
222
223
227
228
229bool
231 return getOStream().good();
232}
233
234
235const std::string&
239
240void
242 while (closeTag()) {}
243 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
244 if (i->second == this) {
245 myOutputDevices.erase(i);
246 break;
247 }
248 }
250 delete this;
251}
252
253
254void
256 getOStream() << std::setprecision(precision);
257}
258
259
260bool
261OutputDevice::writeXMLHeader(const std::string& rootElement,
262 const std::string& schemaFile,
263 std::map<SumoXMLAttr, std::string> attrs,
264 bool includeConfig) {
265 if (schemaFile != "") {
266 attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
267 attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
268 }
269 return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, myWriteMetadata, includeConfig);
270}
271
272
274OutputDevice::openTag(const std::string& xmlElement) {
275 myFormatter->openTag(getOStream(), xmlElement);
276 return *this;
277}
278
279
282 myFormatter->openTag(getOStream(), xmlElement);
283 return *this;
284}
285
286
287bool
288OutputDevice::closeTag(const std::string& comment) {
289 if (myFormatter->closeTag(getOStream(), comment)) {
291 return true;
292 }
293 return false;
294}
295
296
297void
299
300
301void
302OutputDevice::inform(const std::string& msg, const bool progress) {
303 if (progress) {
304 getOStream() << msg;
305 } else {
306 getOStream() << msg << '\n';
307 }
309}
310
311
312const SumoXMLAttrMask
313OutputDevice::parseWrittenAttributes(const std::vector<std::string>& attrList, const std::string& desc, const std::map<std::string, SumoXMLAttrMask>& special) {
314 SumoXMLAttrMask result;
315 for (std::string attrName : attrList) {
316 if (attrName == "all") {
317 result.set();
318 } else if (special.count(attrName) > 0) {
319 result |= special.find(attrName)->second;
320 } else {
321 if (SUMOXMLDefinitions::Attrs.hasString(attrName)) {
322 int attrNr = SUMOXMLDefinitions::Attrs.get(attrName);
323 if (attrNr < (int)result.size()) {
324 result.set(attrNr);
325 } else {
326 WRITE_ERRORF(TL("Attribute '%' is not support for filtering written attributes in %."), attrName, desc);
327 }
328 } else {
329 WRITE_ERRORF(TL("Unknown attribute '%' to write in %."), attrName, desc);
330 }
331 }
332 }
333 return result;
334}
335
336
337/****************************************************************************/
#define WRITE_ERRORF(...)
Definition MsgHandler.h:296
#define WRITE_ERROR(msg)
Definition MsgHandler.h:295
#define WRITE_WARNING(msg)
Definition MsgHandler.h:286
#define TL(string)
Definition MsgHandler.h:304
SumoXMLTag
Numbers representing SUMO-XML - element names.
std::bitset< 96 > SumoXMLAttrMask
@ SUMO_ATTR_XMLNS
@ SUMO_ATTR_SCHEMA_LOCATION
Output formatter for CSV output.
static std::string appendBeforeExtension(const std::string &path, const std::string &suffix, bool checkSep=true)
append the given suffix after the file name but before the extension
static bool isSocket(const std::string &name)
Returns the information whether the given name represents a socket.
static std::string prependToLastPathComponent(const std::string &prefix, const std::string &path)
prepend the given prefix to the last path component of the given file path
static MsgHandler * getErrorInstance()
Returns the instance to add errors to.
static void removeRetrieverFromAllInstances(OutputDevice *out)
ensure that that given output device is no longer used as retriever by any instance
A storage for options typed value containers).
Definition OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String).
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool).
static OptionsCont & getOptions()
Retrieves the options.
static const std::chrono::time_point< std::chrono::system_clock > & getLoadTime()
Return the time stamp of the last init.
Definition OptionsIO.h:101
static OutputDevice * getDevice()
Returns the single cerr instance.
static OutputDevice * getDevice()
Returns the single cout instance.
An output device that encapsulates an ofstream.
An output device for TCP/IP network connections.
Static storage of an output device and its base (abstract) implementation.
virtual std::ostream & getOStream()=0
Returns the associated ostream.
virtual ~OutputDevice()
Destructor.
OutputDevice(const int defaultIndentation=0, const std::string &filename="")
Constructor.
void inform(const std::string &msg, const bool progress=false)
Retrieves a message to this device.
void close()
Closes the device and removes it from the dictionary.
static const SumoXMLAttrMask parseWrittenAttributes(const std::vector< std::string > &attrList, const std::string &desc, const std::map< std::string, SumoXMLAttrMask > &special=std::map< std::string, SumoXMLAttrMask >())
Parses a list of strings for attribute names and sets the relevant bits in the returned mask.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
virtual void postWriteHook()
Called after every write access.
static void flushAll()
void setExpectedAttributes(const SumoXMLAttrMask &expected, const int depth=2)
const std::string & getFilename()
get filename or suitable description of this device
OutputFormatter * myFormatter
The formatter for XML, CSV or Parquet.
static OutputDevice & getDeviceByOption(const std::string &name)
Returns the device described by the option.
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.
void setPrecision(int precision=gPrecision)
Sets the precision or resets it to default.
const std::string myFilename
static void closeAll(bool keepErrorRetrievers=false)
void setFormatter(OutputFormatter *formatter)
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
static int myPrevConsoleCP
old console code page to restore after ending
static bool createDeviceByOption(const std::string &optionName, const std::string &rootElement="", const std::string &schemaFile="", const int maximumDepth=2)
Creates the device using the output definition stored in the named option.
bool writeXMLHeader(const std::string &rootElement, const std::string &schemaFile, std::map< SumoXMLAttr, std::string > attrs=std::map< SumoXMLAttr, std::string >(), bool includeConfig=true)
Writes an XML header with optional configuration.
static std::map< std::string, OutputDevice * > myOutputDevices
map from names to output devices
virtual bool ok()
returns the information whether one can write into the device
Output formatter for Parquet output.
Output formatter for plain XML output.
static SequentialStringBijection Attrs
The names of SUMO-XML attributes for use in netbuild.
static std::string substituteEnvironment(const std::string &str, const std::chrono::time_point< std::chrono::system_clock > *const timeRef=nullptr)
Replaces an environment variable with its value (similar to bash); syntax for a variable is ${NAME}...
static int toInt(const std::string &sData)
converts a string into the integer value described by it by calling the char-type converter,...