/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2003-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file MsgHandler.h14/// @author Daniel Krajzewicz15/// @author Michael Behrisch16/// @author Jakob Erdmann17/// @author Mirko Barthauer18/// @date Tue, 17 Jun 200319///20// Retrieves messages about the process and gives them further to output21/****************************************************************************/22#pragma once23#include <config.h>24#include <string>25#include <vector>26#include <map>27#include <utils/common/StringUtils.h>28#include <utils/common/Translation.h>29#include <utils/iodevices/OutputDevice.h>303132// ===========================================================================33// class definitions34// ===========================================================================35/**36* MsgHandler37*/38class MsgHandler {39public:40/**41* @enum MsgType42* An enumeration to differ between different types of messages43* (errors, warning and information)44*/45enum class MsgType {46/// The message is only something to show47MT_MESSAGE,48/// The message is a warning49MT_WARNING,50/// The message is an error51MT_ERROR,52/// The message is debug output53MT_DEBUG,54/// The message is GL debug output55MT_GLDEBUG56};5758private:59typedef MsgHandler* (*Factory)(MsgType);6061public:62/// @brief Sets the factory function to use for new MsgHandlers63static void setFactory(Factory func) {64// clean old instances65cleanupOnEnd();66myFactory = func;67}6869/// @brief Returns the instance to add normal messages to70static MsgHandler* getMessageInstance();7172/// @brief Returns the instance to add warnings to73static MsgHandler* getWarningInstance();7475/// @brief Returns the instance to add errors to76static MsgHandler* getErrorInstance();7778/// @brief enable/disable debug messages79static void enableDebugMessages(bool enable);8081/// @brief enable/disable gl-debug messages82static void enableDebugGLMessages(bool enable);8384/// @brief check whether to enable/disable debug messages85static inline bool writeDebugMessages() {86return myWriteDebugMessages;87}8889/// @brief check whether to enable/disable gl-debug messages90static inline bool writeDebugGLMessages() {91return myWriteDebugGLMessages;92}9394/// @brief reformats a long string to contain newline after a certain line length in px (depending on the current font)95static std::string insertLineBreaks(std::string msg, int lineWidth);9697/// @brief ensure that that given output device is no longer used as retriever by any instance98static void removeRetrieverFromAllInstances(OutputDevice* out);99100///@brief set up gettext stuff101static void setupI18n(const std::string& locale = "");102103///@brief init output options104static void initOutputOptions();105106/// @brief Removes pending handler107static void cleanupOnEnd();108109/// @brief adds a new error to the list110virtual void inform(std::string msg, bool addType = true);111112/// @brief adds a new formatted message113// variadic function114template<typename T, typename... Targs>115void informf(const std::string& format, T value, Targs... Fargs) {116if (!aggregationThresholdReached(format)) {117inform(StringUtils::format(format, value, Fargs...), true);118}119}120121/** @brief Begins a process information122*123* When a longer action is started, this method should be used to inform the user about it.124* There will be no newline printed, but the message handler will be informed that125* a process message has been begun. If an error occurs, a newline will be printed.126* After the action has been performed, use endProcessMsg to inform the user about it.127*/128virtual void beginProcessMsg(std::string msg, bool addType = true);129130/// @brief Ends a process information with predefined messages131virtual void endProcessMsg2(bool success, long duration = -1);132133/// @brief Ends a process information134virtual void endProcessMsg(std::string msg);135136/// @brief Clears information whether an error occurred previously and print aggregated message summary137virtual void clear(bool resetInformed = true);138139/// @brief Adds a further retriever to the instance responsible for a certain msg type140virtual void addRetriever(OutputDevice* retriever);141142/// @brief Removes the retriever from the handler143virtual void removeRetriever(OutputDevice* retriever);144145/// @brief Returns whether the given output device retrieves messages from the handler146bool isRetriever(OutputDevice* retriever) const;147148/// @brief Returns the information whether any messages were added149bool wasInformed() const;150151/** @brief Generic output operator152* @return The MsgHandler for further processing153*/154template <class T>155MsgHandler& operator<<(const T& t) {156// inform all other receivers157for (OutputDevice* o : myRetrievers) {158(*o) << t;159}160return *this;161}162163void setAggregationThreshold(const int thresh) {164myAggregationThreshold = thresh;165}166167int getAggregationThreshold() const {168return myAggregationThreshold;169}170171protected:172173std::string buildProcessIdPrefix() const;174175/// @brief Builds the string which includes the mml-message type176inline std::string build(const std::string& msg, bool addType) {177std::string prefix;178if (myWriteTimestamps) {179prefix += "[" + StringUtils::isoTimeString() + "] ";180}181if (myWriteProcessId) {182prefix += buildProcessIdPrefix();183}184if (addType) {185switch (myType) {186case MsgType::MT_MESSAGE:187break;188case MsgType::MT_WARNING:189prefix += myWarningPrefix;190break;191case MsgType::MT_ERROR:192prefix += myErrorPrefix;193break;194case MsgType::MT_DEBUG:195prefix += "Debug: ";196break;197case MsgType::MT_GLDEBUG:198prefix += "GLDebug: ";199break;200default:201break;202}203}204return prefix + msg;205}206207virtual bool aggregationThresholdReached(const std::string& format) {208return myAggregationThreshold >= 0 && myAggregationCount[format]++ >= myAggregationThreshold;209}210211/// @brief standard constructor212MsgHandler(MsgType type);213214/// @brief destructor215virtual ~MsgHandler();216217private:218/// @brief The function to call for new MsgHandlers, nullptr means use default constructor219static Factory myFactory;220221/// @brief The instance to handle errors222static MsgHandler* myErrorInstance;223224/// @brief The instance to handle warnings225static MsgHandler* myWarningInstance;226227/// @brief The instance to handle normal messages228static MsgHandler* myMessageInstance;229230/// @brief Information whether a process information is printed to cout231static bool myAmProcessingProcess;232233private:234/// @brief The type of the instance235MsgType myType;236237/// @brief information whether an output occurred at all238bool myWasInformed;239240/// @brief do not output more messages of the same type if the count exceeds this threshold241int myAggregationThreshold;242243/// @brief count for messages of the same type244std::map<const std::string, int> myAggregationCount;245246/// @brief The list of retrievers that shall be informed about new messages or errors247std::vector<OutputDevice*> myRetrievers;248249/// @brief storage for initial messages250std::vector<std::string> myInitialMessages;251252/** @brief Flag to enable or disable debug output253*254* This value is used to show more internal information through warning messages about certain operations255*/256static bool myWriteDebugMessages;257258/// @brief Flag to enable or disable GL specific debug output259static bool myWriteDebugGLMessages;260261/// @brief Whether to prefix every message with a time stamp262static bool myWriteTimestamps;263264/// @brief Whether to prefix every message with the process id265static bool myWriteProcessId;266267/// @brief The possibly translated error prefix (mainly for speedup)268static std::string myErrorPrefix;269270/// @brief The possibly translated warning prefix (mainly for speedup)271static std::string myWarningPrefix;272273private:274/// @brief invalid copy constructor275MsgHandler(const MsgHandler& s) = delete;276277/// @brief invalid assignment operator278MsgHandler& operator=(const MsgHandler& s) = delete;279};280281282// ===========================================================================283// global definitions284// ===========================================================================285#define WRITE_WARNING(msg) MsgHandler::getWarningInstance()->inform(msg);286#define WRITE_WARNINGF(...) MsgHandler::getWarningInstance()->informf(__VA_ARGS__);287#define WRITE_MESSAGE(msg) MsgHandler::getMessageInstance()->inform(msg);288#define WRITE_MESSAGEF(...) MsgHandler::getMessageInstance()->informf(__VA_ARGS__);289#define PROGRESS_BEGIN_MESSAGE(msg) MsgHandler::getMessageInstance()->beginProcessMsg((msg) + std::string(" ..."));290#define PROGRESS_DONE_MESSAGE() MsgHandler::getMessageInstance()->endProcessMsg2(true);291#define PROGRESS_BEGIN_TIME_MESSAGE(msg) SysUtils::getCurrentMillis(); MsgHandler::getMessageInstance()->beginProcessMsg((msg) + std::string(" ..."));292#define PROGRESS_TIME_MESSAGE(before) MsgHandler::getMessageInstance()->endProcessMsg2(true, SysUtils::getCurrentMillis() - before);293#define PROGRESS_FAILED_MESSAGE() MsgHandler::getMessageInstance()->endProcessMsg2(false);294#define WRITE_ERROR(msg) MsgHandler::getErrorInstance()->inform(msg);295#define WRITE_ERRORF(...) MsgHandler::getErrorInstance()->informf(__VA_ARGS__);296#ifdef HAVE_INTL297// basic translation298#define TL(string) gettext(string)299// complex translation ("This % an %", "is", "example")300#define TLF(string, ...) StringUtils::format(gettext(string), __VA_ARGS__)301#else302// basic translation303#define TL(string) (string)304// complex translation ("This % an %", "is", "example")305#define TLF(string, ...) StringUtils::format(string, __VA_ARGS__)306#endif307308309