LCOV - code coverage report
Current view: top level - src/utils/iodevices - OutputDevice.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 78.2 % 133 104
Test Date: 2026-07-25 16:16:11 Functions: 78.9 % 19 15

            Line data    Source code
       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              : /****************************************************************************/
      14              : /// @file    OutputDevice.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @date    2004
      19              : ///
      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"
      40              : #include <utils/common/StringUtils.h>
      41              : #include <utils/common/UtilExceptions.h>
      42              : #include <utils/common/FileHelpers.h>
      43              : #include <utils/common/ToString.h>
      44              : #include <utils/common/MsgHandler.h>
      45              : #include <utils/options/OptionsCont.h>
      46              : #include <utils/options/OptionsIO.h>
      47              : 
      48              : 
      49              : // ===========================================================================
      50              : // static member definitions
      51              : // ===========================================================================
      52              : std::map<std::string, OutputDevice*> OutputDevice::myOutputDevices;
      53              : int OutputDevice::myPrevConsoleCP = -1;
      54              : 
      55              : 
      56              : // ===========================================================================
      57              : // static method definitions
      58              : // ===========================================================================
      59              : OutputDevice&
      60     25780165 : 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     25780165 :     if (myOutputDevices.find(name) != myOutputDevices.end()) {
      70     25475257 :         return *myOutputDevices[name];
      71              :     }
      72              :     // build the device
      73       304908 :     const OptionsCont& oc = OptionsCont::getOptions();
      74       304908 :     const int len = (int)name.length();
      75      1301780 :     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");
      76       974033 :     bool isParquet = (oc.exists("output.format") && oc.getString("output.format") == "parquet") || (len > 8 && name.substr(len - 8) == ".parquet");
      77              : #ifndef HAVE_PARQUET
      78              :     if (isParquet) {
      79              :         WRITE_WARNING("Compiled without Parquet support, falling back to XML.")
      80              :         isParquet = false;
      81              :     }
      82              : #endif
      83              :     OutputDevice* dev = nullptr;
      84              :     // check whether the device shall print to stdout
      85       304908 :     if (name == "stdout") {
      86        85466 :         dev = OutputDevice_COUT::getDevice();
      87              :         isCSV = isParquet = false;
      88       219442 :     } else if (name == "stderr") {
      89       130992 :         dev = OutputDevice_CERR::getDevice();
      90              :         isCSV = isParquet = false;
      91        88450 :     } else if (FileHelpers::isSocket(name)) {
      92              :         try {
      93            3 :             const bool ipv6 = name[0] == '[';  // IPv6 addresses may be written like '[::1]:8000'
      94            3 :             const size_t sepIndex = name.find(":", ipv6 ? name.find("]") : 0);
      95            3 :             const int port = StringUtils::toInt(name.substr(sepIndex + 1));
      96            6 :             dev = new OutputDevice_Network(ipv6 ? name.substr(1, sepIndex - 2) : name.substr(0, sepIndex), port);
      97            0 :         } catch (NumberFormatException&) {
      98            0 :             throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric.");
      99            0 :         } catch (EmptyData&) {
     100            0 :             throw IOError(TL("No port number given."));
     101            0 :         }
     102              :     } else {
     103        88447 :         std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name;
     104       176900 :         if (usePrefix && oc.isSet("output-prefix") && name2 != "/dev/null") {
     105         2192 :             std::string prefix = oc.getString("output-prefix");
     106              :             const std::string::size_type metaTimeIndex = prefix.find("TIME");
     107         1096 :             if (metaTimeIndex != std::string::npos) {
     108            0 :                 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
     109              :                 char buffer [80];
     110            0 :                 struct tm* timeinfo = localtime(&rawtime);
     111            0 :                 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
     112            0 :                 prefix.replace(metaTimeIndex, 4, buffer);
     113              :             }
     114         2192 :             name2 = FileHelpers::prependToLastPathComponent(prefix, name);
     115              :         }
     116       176894 :         if (usePrefix && oc.isSet("output-suffix") && name2 != "/dev/null") {
     117          216 :             std::string suffix = oc.getString("output-suffix");
     118              :             const std::string::size_type metaTimeIndex = suffix.find("TIME");
     119          108 :             if (metaTimeIndex != std::string::npos) {
     120            0 :                 const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime());
     121              :                 char buffer [80];
     122            0 :                 struct tm* timeinfo = localtime(&rawtime);
     123            0 :                 strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo);
     124            0 :                 suffix.replace(metaTimeIndex, 4, buffer);
     125              :             }
     126          216 :             name2 = FileHelpers::appendBeforeExtension(name2, suffix);
     127              :         }
     128        88531 :         name2 = StringUtils::substituteEnvironment(name2, &OptionsIO::getLoadTime());
     129        88447 :         dev = new OutputDevice_File(name2, isParquet);
     130              :     }
     131       304824 :     if (isCSV) {
     132          123 :         dev->setFormatter(new CSVFormatter(oc.getString("output.column-header"), oc.getString("output.column-separator")[0]));
     133              :     }
     134              : #ifdef HAVE_PARQUET
     135       304824 :     if (isParquet) {
     136          177 :         dev->setFormatter(new ParquetFormatter(oc.getString("output.column-header"), oc.getString("output.compression")));
     137              :     }
     138              : #endif
     139       304824 :     dev->setPrecision();
     140       304824 :     dev->getOStream() << std::setiosflags(std::ios::fixed);
     141       609262 :     dev->myWriteMetadata = oc.exists("write-metadata") && oc.getBool("write-metadata");
     142       304824 :     myOutputDevices[name] = dev;
     143       304824 :     return *dev;
     144              : }
     145              : 
     146              : 
     147              : bool
     148       992135 : OutputDevice::createDeviceByOption(const std::string& optionName,
     149              :                                    const std::string& rootElement,
     150              :                                    const std::string& schemaFile,
     151              :                                    const int maximumDepth) {
     152       992135 :     if (!OptionsCont::getOptions().isSet(optionName)) {
     153              :         return false;
     154              :     }
     155        51984 :     OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName));
     156        25970 :     dev.setExpectedAttributes(0, maximumDepth);
     157        25970 :     if (rootElement != "") {
     158        51938 :         dev.writeXMLHeader(rootElement, schemaFile);
     159              :     }
     160              :     return true;
     161              : }
     162              : 
     163              : 
     164              : OutputDevice&
     165     25135761 : OutputDevice::getDeviceByOption(const std::string& optionName) {
     166     25135761 :     std::string devName = OptionsCont::getOptions().getString(optionName);
     167     25135761 :     if (myOutputDevices.find(devName) == myOutputDevices.end()) {
     168            0 :         throw InvalidArgument("Output device '" + devName + "' for option '" + optionName + "' has not been created.");
     169              :     }
     170     50271522 :     return OutputDevice::getDevice(devName);
     171              : }
     172              : 
     173              : 
     174              : void
     175            0 : OutputDevice::flushAll() {
     176            0 :     for (auto item : myOutputDevices) {
     177              :         item.second->flush();
     178              :     }
     179            0 : }
     180              : 
     181              : 
     182              : void
     183       173416 : OutputDevice::closeAll(bool keepErrorRetrievers) {
     184              :     std::vector<OutputDevice*> errorDevices;
     185              :     std::vector<OutputDevice*> nonErrorDevices;
     186       511307 :     for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
     187       337891 :         if (MsgHandler::getErrorInstance()->isRetriever(i->second)) {
     188       173836 :             errorDevices.push_back(i->second);
     189              :         } else {
     190       164055 :             nonErrorDevices.push_back(i->second);
     191              :         }
     192              :     }
     193       337471 :     for (OutputDevice* const dev : nonErrorDevices) {
     194              :         try {
     195       164055 :             dev->close();
     196            0 :         } catch (const IOError& e) {
     197            0 :             WRITE_ERROR(TL("Error on closing output devices."));
     198            0 :             WRITE_ERROR(e.what());
     199            0 :         }
     200              :     }
     201       173416 :     if (!keepErrorRetrievers) {
     202       262207 :         for (OutputDevice* const dev : errorDevices) {
     203              :             try {
     204       131245 :                 dev->close();
     205            0 :             } catch (const IOError& e) {
     206              :                 std::cerr << "Error on closing error output devices." << std::endl;
     207            0 :                 std::cerr << e.what() << std::endl;
     208            0 :             }
     209              :         }
     210              : #ifdef WIN32
     211              :         if (myPrevConsoleCP != -1) {
     212              :             SetConsoleOutputCP(myPrevConsoleCP);
     213              :         }
     214              : #endif
     215              :     }
     216       173416 : }
     217              : 
     218              : 
     219              : // ===========================================================================
     220              : // member method definitions
     221              : // ===========================================================================
     222      1833507 : OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) :
     223      1833507 :     myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) {
     224      1833507 : }
     225              : 
     226              : 
     227      1833184 : OutputDevice::~OutputDevice() {
     228      1833184 :     delete myFormatter;
     229      1833184 : }
     230              : 
     231              : 
     232              : bool
     233            0 : OutputDevice::ok() {
     234            0 :     return getOStream().good();
     235              : }
     236              : 
     237              : 
     238              : const std::string&
     239            0 : OutputDevice::getFilename() {
     240            0 :     return myFilename;
     241              : }
     242              : 
     243              : void
     244       304732 : OutputDevice::close() {
     245       696487 :     while (closeTag()) {}
     246       412076 :     for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) {
     247       412076 :         if (i->second == this) {
     248              :             myOutputDevices.erase(i);
     249              :             break;
     250              :         }
     251              :     }
     252       304732 :     MsgHandler::removeRetrieverFromAllInstances(this);
     253       304732 :     delete this;
     254       304732 : }
     255              : 
     256              : 
     257              : void
     258     13461098 : OutputDevice::setPrecision(int precision) {
     259     13461098 :     getOStream() << std::setprecision(precision);
     260     13461098 : }
     261              : 
     262              : 
     263              : bool
     264        99679 : OutputDevice::writeXMLHeader(const std::string& rootElement,
     265              :                              const std::string& schemaFile,
     266              :                              std::map<SumoXMLAttr, std::string> attrs,
     267              :                              bool includeConfig) {
     268        99679 :     if (schemaFile != "") {
     269        95401 :         attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance";
     270        95401 :         attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile;
     271              :     }
     272        99679 :     return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, myWriteMetadata, includeConfig);
     273              : }
     274              : 
     275              : 
     276              : OutputDevice&
     277      9440351 : OutputDevice::openTag(const std::string& xmlElement) {
     278      9440351 :     myFormatter->openTag(getOStream(), xmlElement);
     279      9440351 :     return *this;
     280              : }
     281              : 
     282              : 
     283              : OutputDevice&
     284     16145767 : OutputDevice::openTag(const SumoXMLTag& xmlElement) {
     285     16145767 :     myFormatter->openTag(getOStream(), xmlElement);
     286     16145767 :     return *this;
     287              : }
     288              : 
     289              : 
     290              : bool
     291     25978416 : OutputDevice::closeTag(const std::string& comment) {
     292     25978416 :     if (myFormatter->closeTag(getOStream(), comment)) {
     293     25673684 :         postWriteHook();
     294     25673683 :         return true;
     295              :     }
     296              :     return false;
     297              : }
     298              : 
     299              : 
     300              : void
     301     29344214 : OutputDevice::postWriteHook() {}
     302              : 
     303              : 
     304              : void
     305      6917741 : OutputDevice::inform(const std::string& msg, const bool progress) {
     306      6917741 :     if (progress) {
     307        33877 :         getOStream() << msg;
     308              :     } else {
     309     13767728 :         getOStream() << msg << '\n';
     310              :     }
     311      6917741 :     postWriteHook();
     312      6917741 : }
     313              : 
     314              : 
     315              : const SumoXMLAttrMask
     316        26032 : OutputDevice::parseWrittenAttributes(const std::vector<std::string>& attrList, const std::string& desc, const std::map<std::string, SumoXMLAttrMask>& special) {
     317        26032 :     SumoXMLAttrMask result;
     318        32600 :     for (std::string attrName : attrList) {
     319         6568 :         if (attrName == "all") {
     320              :             result.set();
     321              :         } else if (special.count(attrName) > 0) {
     322              :             result |= special.find(attrName)->second;
     323              :         } else {
     324              :             if (SUMOXMLDefinitions::Attrs.hasString(attrName)) {
     325         6506 :                 int attrNr = SUMOXMLDefinitions::Attrs.get(attrName);
     326         6506 :                 if (attrNr < (int)result.size()) {
     327         6496 :                     result.set(attrNr);
     328              :                 } else {
     329           30 :                     WRITE_ERRORF(TL("Attribute '%' is not support for filtering written attributes in %."), attrName, desc);
     330              :                 }
     331              :             } else {
     332            0 :                 WRITE_ERRORF(TL("Unknown attribute '%' to write in %."), attrName, desc);
     333              :             }
     334              :         }
     335              :     }
     336        26032 :     return result;
     337              : }
     338              : 
     339              : 
     340              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1