#ifndef msgPrintControl_H #define msgPrintControl_H #include #include #include #include /** * @brief Class to identify a message print location. See the docstring of * `msgPrintControl` on how to use this key. * */ class msgPrintControlKey { public: std::string controller_; // -1 is a non-axis specific message int axisNo_; const char *functionName_; int line_; msgPrintControlKey(char *controller_, int axisNo, const char *fileName, int line); bool operator==(const msgPrintControlKey &other) const { return axisNo_ == other.axisNo_ && line_ == other.line_ && strcmp(functionName_, other.functionName_) == 0 && controller_ == other.controller_; } void format(char *buffer, size_t bufferSize); }; /** * @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 msgPrintControl { public: msgPrintControl(size_t maxRepetitions); /** * @brief Checks if the error message associated with "key" has been printed * more than "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); /** * @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); /** * @brief Maximum number of times a message is printed before it is * suppressed. * */ size_t maxRepetitions_; char *getSuffix(); private: std::unordered_map map_; char suffix_[200] = {0}; }; #endif