Line data Source code
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 : /****************************************************************************/ 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 17610623 : 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 17610623 : if (myOutputDevices.find(name) != myOutputDevices.end()) { 70 17383915 : return *myOutputDevices[name]; 71 : } 72 : // build the device 73 : OutputDevice* dev = nullptr; 74 : // check whether the device shall print to stdout 75 226708 : if (name == "stdout") { 76 72001 : dev = OutputDevice_COUT::getDevice(); 77 154707 : } else if (name == "stderr") { 78 110354 : dev = OutputDevice_CERR::getDevice(); 79 44353 : } else if (FileHelpers::isSocket(name)) { 80 : try { 81 12 : int port = StringUtils::toInt(name.substr(name.find(":") + 1)); 82 12 : dev = new OutputDevice_Network(name.substr(0, name.find(":")), port); 83 0 : } catch (NumberFormatException&) { 84 0 : throw IOError("Given port number '" + name.substr(name.find(":") + 1) + "' is not numeric."); 85 0 : } catch (EmptyData&) { 86 0 : throw IOError(TL("No port number given.")); 87 0 : } 88 : } else { 89 88694 : std::string name2 = (name == "nul" || name == "NUL") ? "/dev/null" : name; 90 90024 : if (usePrefix && OptionsCont::getOptions().isSet("output-prefix") && name2 != "/dev/null") { 91 2636 : std::string prefix = OptionsCont::getOptions().getString("output-prefix"); 92 : const std::string::size_type metaTimeIndex = prefix.find("TIME"); 93 1318 : if (metaTimeIndex != std::string::npos) { 94 0 : const time_t rawtime = std::chrono::system_clock::to_time_t(OptionsIO::getLoadTime()); 95 : char buffer [80]; 96 0 : struct tm* timeinfo = localtime(&rawtime); 97 0 : strftime(buffer, 80, "%Y-%m-%d-%H-%M-%S", timeinfo); 98 0 : prefix.replace(metaTimeIndex, 4, buffer); 99 : } 100 2636 : name2 = FileHelpers::prependToLastPathComponent(prefix, name); 101 : } 102 88694 : name2 = StringUtils::substituteEnvironment(name2, &OptionsIO::getLoadTime()); 103 44347 : const int len = (int)name.length(); 104 177225 : dev = new OutputDevice_File(name2, len > 3 && name.substr(len - 3) == ".gz"); 105 : } 106 226624 : dev->setPrecision(); 107 226624 : dev->getOStream() << std::setiosflags(std::ios::fixed); 108 226624 : myOutputDevices[name] = dev; 109 226624 : return *dev; 110 : } 111 : 112 : 113 : bool 114 709037 : OutputDevice::createDeviceByOption(const std::string& optionName, 115 : const std::string& rootElement, 116 : const std::string& schemaFile) { 117 709037 : if (!OptionsCont::getOptions().isSet(optionName)) { 118 : return false; 119 : } 120 44232 : OutputDevice& dev = OutputDevice::getDevice(OptionsCont::getOptions().getString(optionName)); 121 22094 : if (rootElement != "") { 122 44186 : dev.writeXMLHeader(rootElement, schemaFile); 123 : } 124 : return true; 125 : } 126 : 127 : 128 : OutputDevice& 129 17100061 : OutputDevice::getDeviceByOption(const std::string& optionName) { 130 17100061 : std::string devName = OptionsCont::getOptions().getString(optionName); 131 17100061 : if (myOutputDevices.find(devName) == myOutputDevices.end()) { 132 0 : throw InvalidArgument("Device '" + devName + "' has not been created."); 133 : } 134 34200122 : return OutputDevice::getDevice(devName); 135 : } 136 : 137 : 138 : void 139 0 : OutputDevice::flushAll() { 140 0 : for (auto item : myOutputDevices) { 141 : item.second->flush(); 142 : } 143 0 : } 144 : 145 : 146 : void 147 145531 : OutputDevice::closeAll(bool keepErrorRetrievers) { 148 : std::vector<OutputDevice*> errorDevices; 149 : std::vector<OutputDevice*> nonErrorDevices; 150 398481 : for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) { 151 252950 : if (MsgHandler::getErrorInstance()->isRetriever(i->second)) { 152 146083 : errorDevices.push_back(i->second); 153 : } else { 154 106867 : nonErrorDevices.push_back(i->second); 155 : } 156 : } 157 252398 : for (OutputDevice* const dev : nonErrorDevices) { 158 : try { 159 106867 : dev->close(); 160 0 : } catch (const IOError& e) { 161 0 : WRITE_ERROR(TL("Error on closing output devices.")); 162 0 : WRITE_ERROR(e.what()); 163 0 : } 164 : } 165 145531 : if (!keepErrorRetrievers) { 166 220971 : for (OutputDevice* const dev : errorDevices) { 167 : try { 168 110628 : dev->close(); 169 0 : } catch (const IOError& e) { 170 : std::cerr << "Error on closing error output devices." << std::endl; 171 0 : std::cerr << e.what() << std::endl; 172 0 : } 173 : } 174 : #ifdef WIN32 175 : if (myPrevConsoleCP != -1) { 176 : SetConsoleOutputCP(myPrevConsoleCP); 177 : } 178 : #endif 179 : } 180 145531 : } 181 : 182 : 183 : std::string 184 2246532 : OutputDevice::realString(const double v, const int precision) { 185 2246532 : std::ostringstream oss; 186 2246532 : if (v == 0) { 187 476833 : return "0"; 188 : } 189 1769699 : if (v < pow(10., -precision)) { 190 : oss.setf(std::ios::scientific, std::ios::floatfield); 191 : } else { 192 : oss.setf(std::ios::fixed, std::ios::floatfield); // use decimal format 193 : oss.setf(std::ios::showpoint); // print decimal point 194 : oss << std::setprecision(precision); 195 : } 196 : oss << v; 197 : return oss.str(); 198 2246532 : } 199 : 200 : 201 : // =========================================================================== 202 : // member method definitions 203 : // =========================================================================== 204 1368257 : OutputDevice::OutputDevice(const int defaultIndentation, const std::string& filename) : 205 1368257 : myFilename(filename), myFormatter(new PlainXMLFormatter(defaultIndentation)) { 206 1368257 : } 207 : 208 : 209 1368129 : OutputDevice::~OutputDevice() { 210 1368129 : delete myFormatter; 211 1368129 : } 212 : 213 : 214 : bool 215 0 : OutputDevice::ok() { 216 0 : return getOStream().good(); 217 : } 218 : 219 : 220 : const std::string& 221 0 : OutputDevice::getFilename() { 222 0 : return myFilename; 223 : } 224 : 225 : void 226 226596 : OutputDevice::close() { 227 496339 : while (closeTag()) {} 228 316359 : for (std::map<std::string, OutputDevice*>::iterator i = myOutputDevices.begin(); i != myOutputDevices.end(); ++i) { 229 316359 : if (i->second == this) { 230 : myOutputDevices.erase(i); 231 : break; 232 : } 233 : } 234 226596 : MsgHandler::removeRetrieverFromAllInstances(this); 235 226596 : delete this; 236 226596 : } 237 : 238 : 239 : void 240 4020941 : OutputDevice::setPrecision(int precision) { 241 4020941 : getOStream() << std::setprecision(precision); 242 4020941 : } 243 : 244 : 245 : int 246 51785 : OutputDevice::precision() { 247 51785 : return (int)getOStream().precision(); 248 : } 249 : 250 : 251 : bool 252 48440 : OutputDevice::writeXMLHeader(const std::string& rootElement, 253 : const std::string& schemaFile, 254 : std::map<SumoXMLAttr, std::string> attrs, 255 : bool includeConfig) { 256 48440 : if (schemaFile != "") { 257 45338 : attrs[SUMO_ATTR_XMLNS] = "http://www.w3.org/2001/XMLSchema-instance"; 258 45338 : attrs[SUMO_ATTR_SCHEMA_LOCATION] = "http://sumo.dlr.de/xsd/" + schemaFile; 259 : } 260 48440 : return myFormatter->writeXMLHeader(getOStream(), rootElement, attrs, includeConfig); 261 : } 262 : 263 : 264 : OutputDevice& 265 13323632 : OutputDevice::openTag(const std::string& xmlElement) { 266 13323632 : myFormatter->openTag(getOStream(), xmlElement); 267 13323632 : return *this; 268 : } 269 : 270 : 271 : OutputDevice& 272 7291208 : OutputDevice::openTag(const SumoXMLTag& xmlElement) { 273 7291208 : myFormatter->openTag(getOStream(), xmlElement); 274 7291208 : return *this; 275 : } 276 : 277 : 278 : bool 279 20885138 : OutputDevice::closeTag(const std::string& comment) { 280 20885138 : if (myFormatter->closeTag(getOStream(), comment)) { 281 20658542 : postWriteHook(); 282 20658541 : return true; 283 : } 284 : return false; 285 : } 286 : 287 : 288 : void 289 86216717 : OutputDevice::postWriteHook() {} 290 : 291 : 292 : void 293 488616 : OutputDevice::inform(const std::string& msg, const char progress) { 294 488616 : if (progress != 0) { 295 58784 : getOStream() << msg << progress; 296 : } else { 297 918448 : getOStream() << msg << '\n'; 298 : } 299 488616 : postWriteHook(); 300 488616 : } 301 : 302 : 303 : /****************************************************************************/