Eclipse SUMO - Simulation of Urban MObility
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-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 /****************************************************************************/
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"
43 #include <utils/common/ToString.h>
47 
48 
49 // ===========================================================================
50 // static member definitions
51 // ===========================================================================
52 std::map<std::string, OutputDevice*> OutputDevice::myOutputDevices;
54 
55 
56 // ===========================================================================
57 // static method definitions
58 // ===========================================================================
60 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  if (myOutputDevices.find(name) != myOutputDevices.end()) {
70  return *myOutputDevices[name];
71  }
72  // build the device
73  OutputDevice* dev = nullptr;
74  // check whether the device shall print to stdout
75  if (name == "stdout") {
77  } else if (name == "stderr") {
79  } else if (FileHelpers::isSocket(name)) {
80  try {
81  const bool ipv6 = name[0] == '['; // IPv6 adresses may be written like '[::1]:8000'
82  const size_t sepIndex = name.find(":", ipv6 ? name.find("]") : 0);
83  const int port = StringUtils::toInt(name.substr(sepIndex + 1));
84  dev = new OutputDevice_Network(ipv6 ? name.substr(1, sepIndex - 2) : name.substr(0, sepIndex), port);
85  } catch (NumberFormatException&) {
86  throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric.");
87  } catch (EmptyData&) {
88  throw IOError(TL("No port number given."));
89  }
90  } else {
91  std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name;
92  if (usePrefix && OptionsCont::getOptions().isSet("output-prefix") && name2 != "/dev/null") {
93  std::string prefix = OptionsCont::getOptions().getString("output-prefix");
94  const std::string::size_type metaTimeIndex = prefix.find("TIME");
95  if (metaTimeIndex != std::string::npos) {
96  const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
97  char buffer [80];
98  struct tm* timeinfo = localtime(&rawtime);
99  strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
100  prefix.replace(metaTimeIndex, 4, buffer);
101  }
102  name2 = FileHelpers::prependToLastPathComponent(prefix, name);
103  }
105  const int len = (int)name.length();
106  dev = new OutputDevice_File(name2, len > 3 && name.substr(len - 3) == ".gz");
107  }
108  dev->setPrecision();
109  dev->getOStream() << std::setiosflags(std::ios::fixed);
110  myOutputDevices[name] = dev;
111  return *dev;
112 }
113 
114 
115 bool
116 OutputDevice::createDeviceByOption(const std::string& optionName,
117  const std::string& rootElement,
118  const std::string& schemaFile) {
119  if (!OptionsCont::getOptions().isSet(optionName)) {
120  return false;
121  }
122  OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
123  if (rootElement != "") {
124  dev.writeXMLHeader(rootElement, schemaFile);
125  }
126  return true;
127 }
128 
129 
131 OutputDevice::getDeviceByOption(const std::string& optionName) {
132  std::string devName = OptionsCont::getOptions().getString(optionName);
133  if (myOutputDevices.find(devName) == myOutputDevices.end()) {
134  throw InvalidArgument("Device '" + devName + "' has not been created.");
135  }
136  return OutputDevice::getDevice(devName);
137 }
138 
139 
140 void
142  for (auto item : myOutputDevices) {
143  item.second->flush();
144  }
145 }
146 
147 
148 void
149 OutputDevice::closeAll(bool keepErrorRetrievers) {
150  std::vector<OutputDevice*> errorDevices;
151  std::vector<OutputDevice*> nonErrorDevices;
152  for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
153  if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
154  errorDevices.push_back(i->second);
155  } else {
156  nonErrorDevices.push_back(i->second);
157  }
158  }
159  for (OutputDevice* const dev : nonErrorDevices) {
160  try {
161  dev->close();
162  } catch (const IOError& e) {
163  WRITE_ERROR(TL("Error on closing output devices."));
164  WRITE_ERROR(e.what());
165  }
166  }
167  if (!keepErrorRetrievers) {
168  for (OutputDevice* const dev : errorDevices) {
169  try {
170  dev->close();
171  } catch (const IOError& e) {
172  std::cerr << "Error on closing error output devices." << std::endl;
173  std::cerr << e.what() << std::endl;
174  }
175  }
176 #ifdef WIN32
177  if (myPrevConsoleCP != -1) {
178  SetConsoleOutputCP(myPrevConsoleCP);
179  }
180 #endif
181  }
182 }
183 
184 
185 std::string
186 OutputDevice::realString(const double v, const int precision) {
187  std::ostringstream oss;
188  if (v == 0) {
189  return "0";
190  }
191  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 }
201 
202 
203 // ===========================================================================
204 // member method definitions
205 // ===========================================================================
206 OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
207  myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
208 }
209 
210 
212  delete myFormatter;
213 }
214 
215 
216 bool
218  return getOStream().good();
219 }
220 
221 
222 const std::string&
224  return myFilename;
225 }
226 
227 void
229  while (closeTag()) {}
230  for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
231  if (i->second == this) {
232  myOutputDevices.erase(i);
233  break;
234  }
235  }
237  delete this;
238 }
239 
240 
241 void
243  getOStream() << std::setprecision(precision);
244 }
245 
246 
247 int
249  return (int)getOStream().precision();
250 }
251 
252 
253 bool
254 OutputDevice::writeXMLHeader(const std::string& rootElement,
255  const std::string& schemaFile,
256  std::map<SumoXMLAttr, std::string> attrs,
257  bool includeConfig) {
258  if (schemaFile != "") {
259  attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
260  attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
261  }
262  return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, includeConfig);
263 }
264 
265 
267 OutputDevice::openTag(const std::string& xmlElement) {
268  myFormatter->openTag(getOStream(), xmlElement);
269  return *this;
270 }
271 
272 
274 OutputDevice::openTag(const SumoXMLTag& xmlElement) {
275  myFormatter->openTag(getOStream(), xmlElement);
276  return *this;
277 }
278 
279 
280 bool
281 OutputDevice::closeTag(const std::string& comment) {
282  if (myFormatter->closeTag(getOStream(), comment)) {
283  postWriteHook();
284  return true;
285  }
286  return false;
287 }
288 
289 
290 void
292 
293 
294 void
295 OutputDevice::inform(const std::string& msg, const char progress) {
296  if (progress != 0) {
297  getOStream() << msg << progress;
298  } else {
299  getOStream() << msg << '\n';
300  }
301  postWriteHook();
302 }
303 
304 
305 /****************************************************************************/
#define WRITE_ERROR(msg)
Definition: MsgHandler.h:304
#define TL(string)
Definition: MsgHandler.h:315
SumoXMLTag
Numbers representing SUMO-XML - element names.
@ SUMO_ATTR_XMLNS
@ SUMO_ATTR_SCHEMA_LOCATION
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.
Definition: MsgHandler.cpp:92
static void removeRetrieverFromAllInstances(OutputDevice *out)
ensure that that given output device is no longer used as retriever by any instance
Definition: MsgHandler.cpp:260
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)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
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.
Definition: OutputDevice.h:61
virtual ~OutputDevice()
Destructor.
OutputDevice(const int defaultIndentation=0, const std::string &filename="")
Constructor.
static std::string realString(const double v, const int precision=gPrecision)
Helper method for string formatting.
void close()
Closes the device and removes it from the dictionary.
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.
int precision()
return precision set on the device
const std::string & getFilename()
get filename or suitable description of this device
virtual std::ostream & getOStream()=0
Returns the associated ostream.
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
Definition: OutputDevice.h:377
static void closeAll(bool keepErrorRetrievers=false)
void inform(const std::string &msg, const char progress=0)
Retrieves a message to this device.
static OutputDevice & getDevice(const std::string &name, bool usePrefix=true)
Returns the described OutputDevice.
OutputFormatter *const myFormatter
The formatter for XML.
Definition: OutputDevice.h:381
static int myPrevConsoleCP
old console code page to restore after ending
Definition: OutputDevice.h:374
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
Definition: OutputDevice.h:371
virtual bool ok()
returns the information whether one can write into the device
virtual bool writeXMLHeader(std::ostream &into, const std::string &rootElement, const std::map< SumoXMLAttr, std::string > &attrs, bool includeConfig=true)=0
Writes an XML header with optional configuration.
virtual bool closeTag(std::ostream &into, const std::string &comment="")=0
Closes the most recently opened tag and optinally add a comment.
virtual void openTag(std::ostream &into, const std::string &xmlElement)=0
Opens an XML tag.
Output formatter for plain XML output.
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,...