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-2025 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 }
114 dev = new OutputDevice_File(name2, isParquet);
115 }
116 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")) {
117 dev->setFormatter(new CSVFormatter(oc.getString("output.column-header"), oc.getString("output.column-separator")[0]));
118 }
119#ifdef HAVE_PARQUET
120 if (isParquet) {
121 dev->setFormatter(new ParquetFormatter(oc.getString("output.column-header"), oc.getString("output.compression")));
122 }
123#endif
124 dev->setPrecision();
125 dev->getOStream() << std::setiosflags(std::ios::fixed);
126 dev->myWriteMetadata = oc.exists("write-metadata") && oc.getBool("write-metadata");
127 myOutputDevices[name] = dev;
128 return *dev;
129}
130
131
132bool
133OutputDevice::createDeviceByOption(const std::string& optionName,
134 const std::string& rootElement,
135 const std::string& schemaFile) {
136 if (!OptionsCont::getOptions().isSet(optionName)) {
137 return false;
138 }
139 OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
140 if (rootElement != "") {
141 dev.writeXMLHeader(rootElement, schemaFile);
142 }
143 return true;
144}
145
146
148OutputDevice::getDeviceByOption(const std::string& optionName) {
149 std::string devName = OptionsCont::getOptions().getString(optionName);
150 if (myOutputDevices.find(devName) == myOutputDevices.end()) {
151 throw InvalidArgument("Output device '" + devName + "' for option '" + optionName + "' has not been created.");
152 }
153 return OutputDevice::getDevice(devName);
154}
155
156
157void
159 for (auto item : myOutputDevices) {
160 item.second->flush();
161 }
162}
163
164
165void
166OutputDevice::closeAll(bool keepErrorRetrievers) {
167 std::vector<OutputDevice*> errorDevices;
168 std::vector<OutputDevice*> nonErrorDevices;
169 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
170 if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
171 errorDevices.push_back(i->second);
172 } else {
173 nonErrorDevices.push_back(i->second);
174 }
175 }
176 for (OutputDevice* const dev : nonErrorDevices) {
177 try {
178 dev->close();
179 } catch (const IOError& e) {
180 WRITE_ERROR(TL("Error on closing output devices."));
181 WRITE_ERROR(e.what());
182 }
183 }
184 if (!keepErrorRetrievers) {
185 for (OutputDevice* const dev : errorDevices) {
186 try {
187 dev->close();
188 } catch (const IOError& e) {
189 std::cerr << "Error on closing error output devices." << std::endl;
190 std::cerr << e.what() << std::endl;
191 }
192 }
193#ifdef WIN32
194 if (myPrevConsoleCP != -1) {
195 SetConsoleOutputCP(myPrevConsoleCP);
196 }
197#endif
198 }
199}
200
201
202// ===========================================================================
203// member method definitions
204// ===========================================================================
205OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
206 myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
207}
208
209
213
214
215bool
217 return getOStream().good();
218}
219
220
221const std::string&
225
226void
228 while (closeTag()) {}
229 for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
230 if (i->second == this) {
231 myOutputDevices.erase(i);
232 break;
233 }
234 }
236 delete this;
237}
238
239
240void
242 getOStream() << std::setprecision(precision);
243}
244
245
246bool
247OutputDevice::writeXMLHeader(const std::string& rootElement,
248 const std::string& schemaFile,
249 std::map<SumoXMLAttr, std::string> attrs,
250 bool includeConfig) {
251 if (schemaFile != "") {
252 attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
253 attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
254 }
255 return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, myWriteMetadata, includeConfig);
256}
257
258
260OutputDevice::openTag(const std::string& xmlElement) {
261 myFormatter->openTag(getOStream(), xmlElement);
262 return *this;
263}
264
265
268 myFormatter->openTag(getOStream(), xmlElement);
269 return *this;
270}
271
272
273bool
274OutputDevice::closeTag(const std::string& comment) {
275 if (myFormatter->closeTag(getOStream(), comment)) {
277 return true;
278 }
279 return false;
280}
281
282
283void
285
286
287void
288OutputDevice::inform(const std::string& msg, const bool progress) {
289 if (progress) {
290 getOStream() << msg;
291 } else {
292 getOStream() << msg << '\n';
293 }
295}
296
297
298const SumoXMLAttrMask
299OutputDevice::parseWrittenAttributes(const std::vector<std::string>& attrList, const std::string& desc, const std::map<std::string, SumoXMLAttrMask>& special) {
300 SumoXMLAttrMask result;
301 for (std::string attrName : attrList) {
302 if (attrName == "all") {
303 result.set();
304 } else if (special.count(attrName) > 0) {
305 result |= special.find(attrName)->second;
306 } else {
307 if (SUMOXMLDefinitions::Attrs.hasString(attrName)) {
308 int attrNr = SUMOXMLDefinitions::Attrs.get(attrName);
309 if (attrNr < (int)result.size()) {
310 result.set(attrNr);
311 } else {
312 WRITE_ERRORF(TL("Attribute '%' is not support for filtering written attributes in %."), attrName, desc);
313 }
314 } else {
315 WRITE_ERRORF(TL("Unknown attribute '%' to write in %."), attrName, desc);
316 }
317 }
318 }
319 return result;
320}
321
322
323/****************************************************************************/
#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 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()
static bool createDeviceByOption(const std::string &optionName, const std::string &rootElement="", const std::string &schemaFile="")
Creates the device using the output definition stored in the named option.
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
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,...