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#include <windows.h>
31#endif
32#include "OutputDevice.h"
33#include "OutputDevice_File.h"
34#include "OutputDevice_COUT.h"
35#include "OutputDevice_CERR.h"
37#include "PlainXMLFormatter.h"
45
46
47// ===========================================================================
48// static member definitions
49// ===========================================================================
50std::map<std::string, OutputDevice*> OutputDevice::myOutputDevices;
52
53
54// ===========================================================================
55// static method definitions
56// ===========================================================================
58OutputDevice::getDevice(const std::string& name, bool usePrefix) {
59#ifdef WIN32
60 // fix the windows console output on first call
61 if (myPrevConsoleCP == -1) {
62 myPrevConsoleCP = GetConsoleOutputCP();
63 SetConsoleOutputCP(CP_UTF8);
64 }
65#endif
66 // check whether the device has already been aqcuired
67 if (myOutputDevices.find(name) != myOutputDevices.end()) {
68 return *myOutputDevices[name];
69 }
70 // build the device
72 const int len = (int)name.length();
73 bool isCSV = (oc.exists("output.format") && oc.getString("output.format") == "csv") || (len > 4 && name.substr(len - 4) == ".csv") || (len > 7 && name.substr(len - 7) == ".csv.gz");
74 bool isParquet = (oc.exists("output.format") && oc.getString("output.format") == "parquet") || (len > 8 && name.substr(len - 8) == ".parquet");
75#ifndef HAVE_PARQUET
76 if (isParquet) {
77 WRITE_WARNING("Compiled without Parquet support, falling back to XML.")
78 isParquet = false;
79 }
80#endif
81 OutputDevice* dev = nullptr;
82 // check whether the device shall print to stdout
83 if (name == "stdout") {
85 isCSV = isParquet = false;
86 } else if (name == "stderr") {
88 isCSV = isParquet = false;
89 } else if (FileHelpers::isSocket(name)) {
90 try {
91 const bool ipv6 = name[0] == '['; // IPv6 addresses may be written like '[::1]:8000'
92 const size_t sepIndex = name.find(":", ipv6 ? name.find("]") : 0);
93 const int port = StringUtils::toInt(name.substr(sepIndex + 1));
94 dev = new OutputDevice_Network(ipv6 ? name.substr(1, sepIndex - 2) : name.substr(0, sepIndex), port);
95 } catch (NumberFormatException&) {
96 throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric.");
97 } catch (EmptyData&) {
98 throw IOError(TL("No port number given."));
99 }
100 } else {
101 std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name;
102 if (usePrefix && oc.isSet("output-prefix") && name2 != "/dev/null") {
103 std::string prefix = oc.getString("output-prefix");
104 const std::string::size_type metaTimeIndex = prefix.find("TIME");
105 if (metaTimeIndex != std::string::npos) {
106 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
107 char buffer [80];
108 struct tm* timeinfo = localtime(&rawtime);
109 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
110 prefix.replace(metaTimeIndex, 4, buffer);
111 }
112 name2 = FileHelpers::prependToLastPathComponent(prefix, name);
113 }
114 if (usePrefix && oc.isSet("output-suffix") && name2 != "/dev/null") {
115 std::string suffix = oc.getString("output-suffix");
116 const std::string::size_type metaTimeIndex = suffix.find("TIME");
117 if (metaTimeIndex != std::string::npos) {
118 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
119 char buffer [80];
120 struct tm* timeinfo = localtime(&rawtime);
121 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
122 suffix.replace(metaTimeIndex, 4, buffer);
123 }
124 name2 = FileHelpers::appendBeforeExtension(name2, suffix);
125 }
127 dev = new OutputDevice_File(name2, isParquet);
128 }
129 if (isCSV) {
130 dev->setFormatter(new CSVFormatter(oc.getString("output.column-header"), oc.getString("output.column-separator")[0]));
131 }
132#ifdef HAVE_PARQUET
133 if (isParquet) {
134 dev->setFormatter(new ParquetFormatter(oc.getString("output.column-header"), oc.getString("output.compression")));
135 }
136#endif
137 dev->setPrecision();
138 dev->getOStream() << std::setiosflags(std::ios::fixed);
139 dev->myWriteMetadata = oc.exists("write-metadata") && oc.getBool("write-metadata");
140 myOutputDevices[name] = dev;
141 return *dev;
142}
143
144
145bool
146OutputDevice::createDeviceByOption(const std::string& optionName,
147 const std::string& rootElement,
148 const std::string& schemaFile,
149 const int maximumDepth) {
150 if (!OptionsCont::getOptions().isSet(optionName)) {
151 return false;
152 }
153 OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
154 dev.setExpectedAttributes(0, maximumDepth);
155 if (rootElement != "") {
156 dev.writeXMLHeader(rootElement, schemaFile);
157 }
158 return true;
159}
160
161
163OutputDevice::getDeviceByOption(const std::string& optionName) {
164 std::string devName = OptionsCont::getOptions().getString(optionName);
165 if (myOutputDevices.find(devName) == myOutputDevices.end()) {
166 throw InvalidArgument("Output device '" + devName + "' for option '" + optionName + "' has not been created.");
167 }
168 return OutputDevice::getDevice(devName);
169}
170
171
172void
174 for (auto item : myOutputDevices) {
175 item.second->flush();
176 }
177}
178
179
180void
181OutputDevice::closeAll(bool keepErrorRetrievers) {
182 std::vector<OutputDevice*> errorDevices;
183 std::vector<OutputDevice*> nonErrorDevices;
184 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
185 if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
186 errorDevices.push_back(i->second);
187 } else {
188 nonErrorDevices.push_back(i->second);
189 }
190 }
191 for (OutputDevice* const dev : nonErrorDevices) {
192 try {
193 dev->close();
194 } catch (const IOError& e) {
195 WRITE_ERROR(TL("Error on closing output devices."));
196 WRITE_ERROR(e.what());
197 }
198 }
199 if (!keepErrorRetrievers) {
200 for (OutputDevice* const dev : errorDevices) {
201 try {
202 dev->close();
203 } catch (const IOError& e) {
204 std::cerr << "Error on closing error output devices." << std::endl;
205 std::cerr << e.what() << std::endl;
206 }
207 }
208#ifdef WIN32
209 if (myPrevConsoleCP != -1) {
210 SetConsoleOutputCP(myPrevConsoleCP);
211 }
212#endif
213 }
214}
215
216
217// ===========================================================================
218// member method definitions
219// ===========================================================================
220OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
221 myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
222}
223
224
228
229
230bool
232 return getOStream().good();
233}
234
235
236const std::string&
240
241void
243 while (closeTag()) {}
244 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
245 if (i->second == this) {
246 myOutputDevices.erase(i);
247 break;
248 }
249 }
251 delete this;
252}
253
254
255void
257 getOStream() << std::setprecision(precision);
258}
259
260
261bool
262OutputDevice::writeXMLHeader(const std::string& rootElement,
263 const std::string& schemaFile,
264 std::map<SumoXMLAttr, std::string> attrs,
265 bool includeConfig) {
266 if (schemaFile != "") {
267 attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
268 attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
269 }
270 return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, myWriteMetadata, includeConfig);
271}
272
273
275OutputDevice::openTag(const std::string& xmlElement) {
276 myFormatter->openTag(getOStream(), xmlElement);
277 return *this;
278}
279
280
283 myFormatter->openTag(getOStream(), xmlElement);
284 return *this;
285}
286
287
288bool
289OutputDevice::closeTag(const std::string& comment) {
290 if (myFormatter->closeTag(getOStream(), comment)) {
292 return true;
293 }
294 return false;
295}
296
297
298void
300
301
302void
303OutputDevice::inform(const std::string& msg, const bool progress) {
304 if (progress) {
305 getOStream() << msg;
306 } else {
307 getOStream() << msg << '\n';
308 }
310}
311
312
313const SumoXMLAttrMask
314OutputDevice::parseWrittenAttributes(const std::vector<std::string>& attrList, const std::string& desc, const std::map<std::string, SumoXMLAttrMask>& special) {
315 SumoXMLAttrMask result;
316 for (std::string attrName : attrList) {
317 if (attrName == "all") {
318 result.set();
319 } else if (special.count(attrName) > 0) {
320 result |= special.find(attrName)->second;
321 } else {
322 if (SUMOXMLDefinitions::Attrs.hasString(attrName)) {
323 int attrNr = SUMOXMLDefinitions::Attrs.get(attrName);
324 if (attrNr < (int)result.size()) {
325 result.set(attrNr);
326 } else {
327 WRITE_ERRORF(TL("Attribute '%' is not supported for filtering written attributes in %."), attrName, desc);
328 }
329 } else {
330 WRITE_ERRORF(TL("Unknown attribute '%' to write in %."), attrName, desc);
331 }
332 }
333 }
334 return result;
335}
336
337
338/****************************************************************************/
#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.
bool isRetriever(OutputDevice *retriever) const
Returns whether the given output device retrieves messages from the handler.
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()
const std::string & getFilename()
get filename or suitable description of this device
void setExpectedAttributes(const SumoXMLAttrMask &expected, const int depth)
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
virtual bool closeTag(std::ostream &into, const std::string &comment="")=0
Closes the most recently opened tag and optinally add a comment.
virtual bool writeXMLHeader(std::ostream &into, const std::string &rootElement, const std::map< SumoXMLAttr, std::string > &attrs, bool writeMetadata, bool includeConfig)
Writes an XML header with optional configuration.
virtual void openTag(std::ostream &into, const std::string &xmlElement)=0
Opens an XML tag.
Output formatter for Parquet output.
Output formatter for plain XML output.
static SequentialStringBijection Attrs
The names of SUMO-XML attributes for use in netbuild.
int get(const std::string &str) const
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,...