diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5054990..73b0a2f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ default: image: docker.psi.ch:5000/sinqdev/sinqepics:latest - + stages: - lint - build @@ -24,16 +24,6 @@ formatting: tags: - sinq -# clangtidy: -# stage: lint -# script: -# - curl https://docker.psi.ch:5000/v2/_catalog -# # - dnf update -y -# # - dnf install -y clang-tools-extra -# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-* -# # tags: -# # - sinq - build_module: stage: build script: @@ -48,4 +38,4 @@ build_module: expire_in: 1 week when: always tags: - - sinq \ No newline at end of file + - sinq diff --git a/src/msgPrintControl.cpp b/src/msgPrintControl.cpp new file mode 100644 index 0000000..fae5c67 --- /dev/null +++ b/src/msgPrintControl.cpp @@ -0,0 +1,100 @@ +#include "msgPrintControl.h" +#include + +msgPrintControlKey::msgPrintControlKey(char *controller, int axisNo, + const char *functionName, int line) { + controller_ = controller; + axisNo_ = axisNo; + line_ = line; + functionName_ = functionName; +} + +void msgPrintControlKey::format(char *buffer, size_t bufferSize) { + snprintf(buffer, bufferSize, "controller %s, axis %d, function %s, line %d", + controller_.c_str(), axisNo_, functionName_, line_); +} + +// ============================================================================= + +msgPrintControl::msgPrintControl(size_t maxRepetitions) { + maxRepetitions_ = maxRepetitions; +} + +bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint, + asynUser *pasynUser) { + + // Reset the suffix + suffix_[0] = 0; + + if (wantToPrint) { + /* + We want to print the message associated with key -> Check if the number + of allowed repetitions is exceeded. If true, inform the user that + further output is suppressed. + */ + if (map_.find(key) != map_.end()) { + size_t repetitions = map_[key]; + if (repetitions < maxRepetitions_) { + // Number of allowed repetitions not exceeded -> Printing the + // message is ok. + map_[key] = repetitions + 1; + return true; + } else if (repetitions == maxRepetitions_) { + // Reached number of allowed repetitions -> Printing the message + // is ok, but further trys are rejected. + char formattedKey[100] = {0}; + key.format(formattedKey, sizeof(formattedKey)); + snprintf(suffix_, sizeof(suffix_), + " Further repetition of this error message (key " + "\"%s\") is suppressed.", + formattedKey); + map_[key] = repetitions + 1; + return true; + } else { + // Exceeded number of allowed repetitions -> Do not print the + // message + return false; + } + } else { + // Message is not yet in map -> create an entry so it is watched in + // the future. + map_[key] = 1; + return true; + } + + } else { + /* + We do not want to print the message associated with key -> If the key is + part of the map, set the counter back to zero. + */ + if (map_.find(key) != map_.end()) { + if (map_[key] != 0) { + char formattedKey[100] = {0}; + key.format(formattedKey, sizeof(formattedKey)); + asynPrint(pasynUser, ASYN_TRACE_ERROR, + "Controller \"%s\", axis %d => %s, line %d\nError " + "associated with key \"%s\" has been resolved.\n", + key.controller_.c_str(), key.axisNo_, + key.functionName_, key.line_, formattedKey); + map_[key] = 0; + } + } + return false; + } +} + +bool msgPrintControl::shouldBePrinted(char *portName, int axisNo, + const char *functionName, int line, + bool wantToPrint, asynUser *pasynUser) { + msgPrintControlKey key = + msgPrintControlKey(portName, axisNo, functionName, __LINE__); + return shouldBePrinted(key, wantToPrint, pasynUser); +} + +void msgPrintControl::resetCount(msgPrintControlKey &key) { + if (map_.find(key) != map_.end()) { + map_[key] = 0; + } +} + +char *msgPrintControl::getSuffix() { return suffix_; } \ No newline at end of file diff --git a/src/msgPrintControl.h b/src/msgPrintControl.h new file mode 100644 index 0000000..13211d8 --- /dev/null +++ b/src/msgPrintControl.h @@ -0,0 +1,133 @@ +#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. + * @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 + * @return true + * @return false + */ + bool shouldBePrinted(char *controller, int axisNo, const char *functionName, + int line, bool wantToPrint, asynUser *pasynUser); + + void resetCount(msgPrintControlKey &key); + + /** + * @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 \ No newline at end of file