Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/iodevices/OutputDevice.h
169678 views
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
/****************************************************************************/
14
/// @file OutputDevice.h
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @author Mario Krumnow
19
/// @date 2004
20
///
21
// Static storage of an output device and its base (abstract) implementation
22
/****************************************************************************/
23
#pragma once
24
#include <config.h>
25
26
#include <string>
27
#include <map>
28
#include <cassert>
29
#include <utils/common/ToString.h>
30
#include <utils/xml/SUMOXMLDefinitions.h>
31
#include "CSVFormatter.h"
32
#ifdef HAVE_PARQUET
33
#include "ParquetFormatter.h"
34
#endif
35
#include "PlainXMLFormatter.h"
36
37
// ===========================================================================
38
// class definitions
39
// ===========================================================================
40
/**
41
* @class OutputDevice
42
* @brief Static storage of an output device and its base (abstract) implementation
43
*
44
* OutputDevices are basically a capsule around an std::ostream, which give a
45
* unified access to sockets, files and stdout.
46
*
47
* Usually, an application builds as many output devices as needed. Each
48
* output device may also be used to save outputs from several sources
49
* (several detectors, for example). Building is done using OutputDevice::getDevice()
50
* what also parses the given output description in order to decide
51
* what kind of an OutputDevice shall be built. OutputDevices are
52
* closed via OutputDevice::closeAll(), normally called at the application's
53
* end.
54
*
55
* Although everything that can be written to a stream can also be written
56
* to an OutputDevice, there is special support for XML tags (remembering
57
* all open tags to close them at the end). OutputDevices are still lacking
58
* support for function pointers with the '<<' operator (no endl, use '\n').
59
* The most important method to implement in subclasses is getOStream,
60
* the most used part of the interface is the '<<' operator.
61
*
62
* The Boolean markers are used rarely and might get removed in future versions.
63
*/
64
class OutputDevice {
65
public:
66
/// @name static access methods to OutputDevices
67
/// @{
68
69
/** @brief Returns the described OutputDevice
70
*
71
* Creates and returns the named device. "stdout" and "stderr" refer to the relevant console streams,
72
* "hostname:port" initiates socket connection. Otherwise a filename
73
* is assumed (where "nul" and "/dev/null" do what you would expect on both platforms).
74
* If there already is a device with the same name this one is returned.
75
*
76
* @param[in] name The description of the output name/port/whatever
77
* @return The corresponding (built or existing) device
78
* @exception IOError If the output could not be built for any reason (error message is supplied)
79
*/
80
static OutputDevice& getDevice(const std::string& name, bool usePrefix = true);
81
82
83
/** @brief Creates the device using the output definition stored in the named option
84
*
85
* Creates and returns the device named by the option. Asks whether the option
86
* and retrieves the name from the option if so. Optionally the XML header
87
* gets written as well. Returns whether a device was created (option was set).
88
*
89
* Please note, that we do not have to consider the "application base" herein,
90
* because this call is only used to get file names of files referenced
91
* within XML-declarations of structures which paths already is aware of the
92
* cwd.
93
*
94
* @param[in] optionName The name of the option to use for retrieving the output definition
95
* @param[in] rootElement The root element to use (XML-output)
96
* @param[in] schemaFile The basename of the schema file to use (XML-output)
97
* @return Whether a device was built (the option was set)
98
* @exception IOError If the output could not be built for any reason (error message is supplied)
99
*/
100
static bool createDeviceByOption(const std::string& optionName,
101
const std::string& rootElement = "",
102
const std::string& schemaFile = "");
103
104
105
/** @brief Returns the device described by the option
106
*
107
* Returns the device named by the option. If the option is unknown, unset
108
* or the device was not created before, InvalidArgument is thrown.
109
*
110
* Please note, that we do not have to consider the "application base" herein.
111
*
112
* @param[in] name The name of the option to use for retrieving the output definition
113
* @return The corresponding (built or existing) device
114
* @exception IOError If the output could not be built for any reason (error message is supplied)
115
* @exception InvalidArgument If the option with the given name does not exist
116
*/
117
static OutputDevice& getDeviceByOption(const std::string& name);
118
119
/** Flushes all registered devices
120
*/
121
static void flushAll();
122
123
/** Closes all registered devices
124
*/
125
static void closeAll(bool keepErrorRetrievers = false);
126
/// @}
127
128
public:
129
/// @name OutputDevice member methods
130
/// @{
131
132
/// @brief Constructor
133
OutputDevice(const int defaultIndentation = 0, const std::string& filename = "");
134
135
136
/// @brief Destructor
137
virtual ~OutputDevice();
138
139
140
/** @brief returns the information whether one can write into the device
141
* @return Whether the device can be used (stream is good)
142
*/
143
virtual bool ok();
144
145
/** @brief returns the information whether the device will discard all output
146
* @return Whether the device redirects to /dev/null
147
*/
148
virtual bool isNull() {
149
return false;
150
}
151
152
/// @brief get filename or suitable description of this device
153
const std::string& getFilename();
154
155
/** @brief Closes the device and removes it from the dictionary
156
*/
157
void close();
158
159
void setFormatter(OutputFormatter* formatter) {
160
delete myFormatter;
161
myFormatter = formatter;
162
}
163
164
/** @brief Sets the precision or resets it to default
165
* @param[in] precision The accuracy (number of digits behind '.') to set
166
*/
167
void setPrecision(int precision = gPrecision);
168
169
/** @brief Returns the precision of the underlying stream
170
*/
171
int getPrecision() {
172
return (int)getOStream().precision();
173
}
174
175
/** @brief Writes an XML header with optional configuration
176
*
177
* If something has been written (myXMLStack is not empty), nothing
178
* is written and false returned.
179
*
180
* @param[in] rootElement The root element to use
181
* @param[in] schemaFile The basename of the schema file to use
182
* @param[in] attrs Additional attributes to save within the rootElement
183
* @return Whether the header could be written (stack was empty)
184
* @todo Describe what is saved
185
*/
186
bool writeXMLHeader(const std::string& rootElement,
187
const std::string& schemaFile,
188
std::map<SumoXMLAttr, std::string> attrs = std::map<SumoXMLAttr, std::string>(),
189
bool includeConfig = true);
190
191
/** @brief Opens an XML tag
192
*
193
* An indentation, depending on the current xml-element-stack size, is written followed
194
* by the given xml element ("<" + xmlElement)
195
* The xml element is added to the stack, then.
196
*
197
* @param[in] xmlElement Name of element to open
198
* @return The OutputDevice for further processing
199
*/
200
OutputDevice& openTag(const std::string& xmlElement);
201
202
/** @brief Opens an XML tag
203
*
204
* Helper method which finds the correct string before calling openTag.
205
*
206
* @param[in] xmlElement Id of the element to open
207
* @return The OutputDevice for further processing
208
*/
209
OutputDevice& openTag(const SumoXMLTag& xmlElement);
210
211
/** @brief Closes the most recently opened tag and optionally adds a comment
212
*
213
* The topmost xml-element from the stack is written into the stream
214
* as a closing element. Depending on the formatter used
215
* this may be something like "</" + element + ">" or "/>" or
216
* nothing at all.
217
*
218
* @return Whether a further element existed in the stack and could be closed
219
* @todo it is not verified that the topmost element was closed
220
*/
221
bool closeTag(const std::string& comment = "");
222
223
/** @brief writes a line feed if applicable
224
*/
225
void lf() {
226
getOStream() << "\n";
227
}
228
229
/** @brief writes a named attribute
230
*
231
* @param[in] attr The attribute (name)
232
* @param[in] val The attribute value
233
* @return The OutputDevice for further processing
234
*/
235
template <typename T>
236
OutputDevice& writeAttr(const SumoXMLAttr attr, const T& val) {
237
if (myFormatter->getType() == OutputFormatterType::XML) {
238
PlainXMLFormatter::writeAttr(getOStream(), attr, val);
239
#ifdef HAVE_PARQUET
240
} else if (myFormatter->getType() == OutputFormatterType::PARQUET) {
241
static_cast<ParquetFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val);
242
#endif
243
} else {
244
static_cast<CSVFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val);
245
}
246
return *this;
247
}
248
249
/** @brief Parses a list of strings for attribute names and sets the relevant bits in the returned mask.
250
*
251
* It honors the special value "all" to set all bits and other special values for predefined bit sets given as parameter
252
*
253
* @param[in] attrList The attribute names and special values
254
* @param[in] desc A descriptive string for the error message if the attribute is unknown
255
* @param[in] special special values for predefined bitsets
256
* @return The corresponding mask of bits being set
257
*/
258
static const SumoXMLAttrMask parseWrittenAttributes(const std::vector<std::string>& attrList, const std::string& desc,
259
const std::map<std::string, SumoXMLAttrMask>& special = std::map<std::string, SumoXMLAttrMask>());
260
261
/** @brief writes a named attribute unless filtered
262
*
263
* @param[in] attr The attribute (name)
264
* @param[in] val The attribute value
265
* @param[in] attributeMask The filter that specifies whether the attribute shall be written
266
* @return The OutputDevice for further processing
267
*/
268
template <typename T>
269
OutputDevice& writeOptionalAttr(const SumoXMLAttr attr, const T& val, const SumoXMLAttrMask& attributeMask, const bool isNull = false) {
270
assert((int)attr <= (int)attributeMask.size());
271
if (attributeMask.none() || attributeMask.test(attr)) {
272
if (myFormatter->getType() == OutputFormatterType::XML) {
273
if (!isNull) {
274
PlainXMLFormatter::writeAttr(getOStream(), attr, val);
275
}
276
#ifdef HAVE_PARQUET
277
} else if (myFormatter->getType() == OutputFormatterType::PARQUET) {
278
static_cast<ParquetFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val, isNull);
279
#endif
280
} else {
281
if (isNull) {
282
static_cast<CSVFormatter*>(myFormatter)->writeNull(getOStream(), attr);
283
} else {
284
static_cast<CSVFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val);
285
}
286
}
287
}
288
return *this;
289
}
290
291
template <typename Func>
292
OutputDevice& writeFuncAttr(const SumoXMLAttr attr, const Func& valFunc, const SumoXMLAttrMask& attributeMask, const bool isNull = false) {
293
assert((int)attr <= (int)attributeMask.size());
294
if (attributeMask.none() || attributeMask.test(attr)) {
295
if (myFormatter->getType() == OutputFormatterType::XML) {
296
if (!isNull) {
297
PlainXMLFormatter::writeAttr(getOStream(), attr, valFunc());
298
}
299
#ifdef HAVE_PARQUET
300
} else if (myFormatter->getType() == OutputFormatterType::PARQUET) {
301
static_cast<ParquetFormatter*>(myFormatter)->writeAttr(getOStream(), attr, valFunc(), isNull);
302
#endif
303
} else {
304
if (isNull) {
305
static_cast<CSVFormatter*>(myFormatter)->writeNull(getOStream(), attr);
306
} else {
307
static_cast<CSVFormatter*>(myFormatter)->writeAttr(getOStream(), attr, valFunc());
308
}
309
}
310
}
311
return *this;
312
}
313
314
/** @brief writes an arbitrary attribute
315
*
316
* @param[in] attr The attribute (name)
317
* @param[in] val The attribute value
318
* @return The OutputDevice for further processing
319
*/
320
template <typename T>
321
OutputDevice& writeAttr(const std::string& attr, const T& val) {
322
if (myFormatter->getType() == OutputFormatterType::XML) {
323
PlainXMLFormatter::writeAttr(getOStream(), attr, val);
324
#ifdef HAVE_PARQUET
325
} else if (myFormatter->getType() == OutputFormatterType::PARQUET) {
326
static_cast<ParquetFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val);
327
#endif
328
} else {
329
static_cast<CSVFormatter*>(myFormatter)->writeAttr(getOStream(), attr, val);
330
}
331
return *this;
332
}
333
334
/** @brief writes a string attribute only if it is not the empty string and not the string "default"
335
*
336
* @param[in] attr The attribute (name)
337
* @param[in] val The attribute value
338
* @return The OutputDevice for further processing
339
*/
340
OutputDevice& writeNonEmptyAttr(const SumoXMLAttr attr, const std::string& val) {
341
if (val != "" && val != "default") {
342
writeAttr(attr, val);
343
}
344
return *this;
345
}
346
347
OutputDevice& writeTime(const SumoXMLAttr attr, const SUMOTime val) {
348
myFormatter->writeTime(getOStream(), attr, val);
349
return *this;
350
}
351
352
/** @brief writes a preformatted tag to the device but ensures that any
353
* pending tags are closed
354
* @param[in] val The preformatted data
355
* @return The OutputDevice for further processing
356
*/
357
OutputDevice& writePreformattedTag(const std::string& val) {
358
myFormatter->writePreformattedTag(getOStream(), val);
359
return *this;
360
}
361
362
/// @brief writes padding (ignored for binary output)
363
OutputDevice& writePadding(const std::string& val) {
364
myFormatter->writePadding(getOStream(), val);
365
return *this;
366
}
367
368
/** @brief Retrieves a message to this device.
369
*
370
* Implementation of the MessageRetriever interface. Writes the given message to the output device.
371
*
372
* @param[in] msg The msg to write to the device
373
*/
374
void inform(const std::string& msg, const bool progress = false);
375
376
377
/** @brief Abstract output operator
378
* @return The OutputDevice for further processing
379
*/
380
template <class T>
381
OutputDevice& operator<<(const T& t) {
382
getOStream() << t;
383
postWriteHook();
384
return *this;
385
}
386
387
void flush() {
388
getOStream().flush();
389
}
390
391
bool wroteHeader() const {
392
return myFormatter->wroteHeader();
393
}
394
395
void setExpectedAttributes(const SumoXMLAttrMask& expected, const int depth = 2) {
396
myFormatter->setExpectedAttributes(expected, depth);
397
}
398
399
protected:
400
/// @brief Returns the associated ostream
401
virtual std::ostream& getOStream() = 0;
402
403
/** @brief Called after every write access.
404
*
405
* Default implementation does nothing.
406
*/
407
virtual void postWriteHook();
408
409
410
private:
411
/// @brief map from names to output devices
412
static std::map<std::string, OutputDevice*> myOutputDevices;
413
414
/// @brief old console code page to restore after ending
415
static int myPrevConsoleCP;
416
417
protected:
418
const std::string myFilename;
419
420
bool myWriteMetadata;
421
422
/// @brief The formatter for XML, CSV or Parquet
423
OutputFormatter* myFormatter;
424
425
private:
426
/// @brief Invalidated copy constructor.
427
OutputDevice(const OutputDevice&) = delete;
428
429
/// @brief Invalidated assignment operator.
430
OutputDevice& operator=(const OutputDevice&) = delete;
431
432
};
433
434