// SPDX-License-Identifier: GPL-3.0-only #ifndef msgPrintControl_H #define msgPrintControl_H #define DefaultMaxRepetitions 4 // The EPICS libaries do not follow -Weffc++ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #include "asynDriver.h" #include "macros.h" #pragma GCC diagnostic pop #include #include #include /** * @brief Class to identify a message print location. See the docstring of * `msgPrintControl` on how to use this key. * */ class HIDDEN msgPrintControlKey { public: msgPrintControlKey(char *controller_, int axisNo, const char *fileName, int line, size_t maxRepetitions = DefaultMaxRepetitions); bool operator==(const msgPrintControlKey &other) const { return axisNo_ == other.axisNo_ && line_ == other.line_ && functionName_ == other.functionName_ && controller_ == other.controller_; } void format(char *buffer, size_t bufferSize); std::string controller_; // -1 indicates a non-axis specific message int axisNo_; std::string functionName_; int line_; /** * @brief Maximum number of times a message is printed before it is * suppressed. This number is not used as part of the hash. * */ size_t maxRepetitions_; }; /** * @brief Implementation of the hash functionality for `msgPrintControlKey` * */ namespace std { template <> struct hash { size_t operator()(const msgPrintControlKey &obj) const { // Combine the hashes of the members (x and y) size_t h1 = std::hash{}(obj.controller_); size_t h2 = hash{}(obj.axisNo_); size_t h3 = std::hash{}(obj.functionName_); size_t h4 = hash{}(obj.line_); // Combine the hashes (simple XOR and shifting technique) return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3); } }; } // namespace std /** * @brief Class to control the number of repetitions of error messages * * This class is used to prevent excessive repetitions of identical error * messages. For example, if the communication between a controller and an * axis fails, a corresponding error message is created in each poll. This * could "flood" the IOC shell with noise. To prevent this, this class keeps * track of the number of subsequent error message repetition. Each message is * uniquely identified by `msgPrintControlKey`. The function `shouldBePrinted` * can be used in order to see if a message should be printed or not: * * ``` * const char* controller = "MCU" // Name of the controller * int axisNo = 0; // Number of the axis * bool wantToPrint = evaluateConditions(...); * * if (msgPrintControl.shouldBePrinted(controller, axisNo, __PRETTY_FUNCTION__, * __LINE__, wantToPrint)) { asynPrint(...) * } * ``` */ class HIDDEN msgPrintControl { public: /** * @brief Construct a new msgPrintControl object * */ msgPrintControl(); /** * @brief Destroy the msgPrintControl object * */ ~msgPrintControl(); /** * @brief Checks if the error message associated with "key" has been printed * more than `this->maxRepetitions_` times in a row. If yes, returns false, * otherwise true. Counter is reset if `wantToPrint` is false. * * If the conditions for printing a message are met, `wantToPrint` must be * set to true. The function then checks if `maxRepetitions_` has been * exceeded. If yes, the function returns no, indicating that the message * should not be printed. If no, the number of repetitions stored in the map * is incremented and the function returns true, indicating that the message * should be printed. * * If the conditions for printing a message are not met, `wantToPrint` must * be set to false. This resets the map entry. * * @param key Key associated with the message, used to * identify individual messages * @param wantToPrint If the message associated with key should be * printed, this value should be true, otherwise false. * @param pasynUser If the problem has been resolved (wantToPrint = * false), a corresponding status message is printed using the given * asynUser. If this pointer is a nullptr, no message is printed. * @return bool If true, the message should be printed, if * false, it should not. */ bool shouldBePrinted(msgPrintControlKey &key, bool wantToPrint, asynUser *pasynUser); /** * @brief Like `shouldBePrinted(msgPrintControlKey key, bool wantToPrint)`, * but constructs the key from the first four arguments. * * @param controller_ * @param axisNo * @param fileName * @param line * @param wantToPrint * @param pasynUser */ bool shouldBePrinted(char *controller, int axisNo, const char *functionName, int line, bool wantToPrint, asynUser *pasynUser, size_t maxRepetitions = DefaultMaxRepetitions); /** * @brief Reset the error message count incremented in `shouldBePrinted` for * the given key * * @param key Key associated with the message, used to * identify individual messages * @param pasynUser If the problem has been resolved (`wantToPrint = * false`), a corresponding status message is printed using the given * asynUser. If this pointer is a nullptr, no message is printed. */ void resetCount(msgPrintControlKey &key, asynUser *pasynUser); char *getSuffix(); private: std::unordered_map map_; char suffix_[300] = {0}; }; #endif