Skip to content
Snippets Groups Projects
StatusMonitor.h 12.1 KiB
Newer Older
#ifndef CHIMERATK_STATUS_MONITOR_H
#define CHIMERATK_STATUS_MONITOR_H

Nadeem Shehzad's avatar
Nadeem Shehzad committed
/*!
 * \author Nadeem Shehzad (DESY)
 * \date 15.05.2019
 * \page statusmonitordoc Status Monitor
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 *
 * To monitor a status of a varaible in an appplicaiton this group of
 * modules provides different possiblites.
 * It includes
 *  - MaxMonitor to monitor a value depending upon two MAX thresholds for warning and error.
 *  - MinMonitor to monitor a value depending upon two MIN thresholds for warning and error.
 *  - RangeMonitor to monitor a value depending upon two ranges of thresholds for warning and error.
 *  - ExactMonitor to monitor a value which should be exactly same as required value.
 *  - StateMonitor to monitor On/Off state.
 * Depending upon the value and condition on of the four states are reported.
 *  -  OFF, OK, WARNING, ERROR.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 *
 * Checkout the status monitor example to see in detail how it works.
 * \include demoStatusMonitor.cc
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 */

/**
For more info see \ref statusmonitordoc
\example demoStatusMonitor.cc
*/

Nadeem Shehzad's avatar
Nadeem Shehzad committed
/** Generic modules for status monitoring.
 * Each module monitors an input variable and depending upon the
 * conditions reports four different states.
*/
#include "ApplicationCore.h"
Nadeem Shehzad's avatar
Nadeem Shehzad committed

  /** There are four states that can be reported*/
  enum States { OFF, OK, WARNING, ERROR };

  /** Common base for StatusMonitors
   *
   *  This holds common process variables that are not dependant on the
   *  type of the variable to be monitored. A non-template base class
   *  facilitates checking for the type in the StatusAggregator, which
   *  needs to identify any StatusMonitor.
   */
  struct StatusMonitor : public ApplicationModule {
    StatusMonitor(EntityOwner* owner, const std::string& name, const std::string& description, const std::string& input,
        const std::string& output, HierarchyModifier modifier, const std::unordered_set<std::string>& outputTags = {},
        const std::unordered_set<std::string>& parameterTags = {}, const std::unordered_set<std::string>& tags = {})
    : ApplicationModule(owner, name, description, modifier, tags), _parameterTags(parameterTags), _input(input),
      status(this, output, "", "", outputTags) {}

    StatusMonitor(StatusMonitor&&) = default;

    ~StatusMonitor() override {}
    void prepare() override {}
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /**Tags for parameters. This makes it easier to connect them to e.g, to control system*/
    std::unordered_set<std::string> _parameterTags;

    const std::string _input;
    /**One of four possible states to be reported*/
    ScalarOutput<uint16_t> status;

    /** Disable the monitor. The status will always be OFF. You don't have to connect this input.
     *  When there is no feeder, ApplicationCore will connect it to a constant feeder with value 0, hence the monitor is always enabled.
     */
    ScalarPushInput<int> disable{this, "disable", "", "Disable the status monitor"};
  /** Common template base class for StatusMonitors
   *
   *  This provides a ScalarPushInput for the variable to be monitored, which
   *  can be specified by the input parameter of the constructor.
   */
  template<typename T>
  struct StatusMonitorImpl : public StatusMonitor {
    /** Number of convience constructors for ease of use.
     * The input and output variable names can be given by user which
     * should be mapped with the variables of module to be watched.
     */
    StatusMonitorImpl(EntityOwner* owner, const std::string& name, const std::string& description,
        const std::string& input, const std::string& output, HierarchyModifier modifier,
        const std::unordered_set<std::string>& outputTags = {},
        const std::unordered_set<std::string>& parameterTags = {}, const std::unordered_set<std::string>& tags = {})
    : StatusMonitor(owner, name, description, input, output, modifier, outputTags, parameterTags, tags),
      oneUp(this, input) {}

    StatusMonitorImpl() { throw logic_error("Default constructor unusable. Just exists to work around gcc bug."); }
    StatusMonitorImpl(StatusMonitorImpl&&) = default;

    ~StatusMonitorImpl() override {}
    void prepare() override {}

    /**Input value that should be monitored. It is moved one level up, so it's parallel to this monitor object.*/
    struct OneUp : public VariableGroup {
      OneUp(EntityOwner* owner, const std::string& watchName)
      : VariableGroup(owner, "hidden", "", HierarchyModifier::oneUpAndHide), watch(this, watchName, "", "") {}
      ScalarPushInput<T> watch;
    } oneUp;
  };

  /** Module for status monitoring depending on a maximum threshold value*/
  template<typename T>
  struct MaxMonitor : public StatusMonitorImpl<T> {
    using StatusMonitorImpl<T>::StatusMonitorImpl;
    /** WARNING state to be reported if threshold is reached or exceeded*/
    ScalarPushInput<T> warning{this, "upperWarningThreshold", "", "", StatusMonitor::_parameterTags};
    /** ERROR state to be reported if threshold is reached or exceeded*/
    ScalarPushInput<T> error{this, "upperErrorThreshold", "", "", StatusMonitor::_parameterTags};
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /**This is where state evaluation is done*/
      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warning, error};
Martin Killenberg's avatar
Martin Killenberg committed
        // evaluate and publish first, then read and wait. This takes care of the publishing the initial variables
        if(StatusMonitorImpl<T>::disable != 0) {
          StatusMonitorImpl<T>::status = OFF;
        else if(StatusMonitorImpl<T>::oneUp.watch >= error) {
          StatusMonitorImpl<T>::status = ERROR;
        else if(StatusMonitorImpl<T>::oneUp.watch >= warning) {
          StatusMonitorImpl<T>::status = WARNING;
          StatusMonitorImpl<T>::status = OK;
        StatusMonitorImpl<T>::status.write();
Martin Killenberg's avatar
Martin Killenberg committed
        group.readAny();
Nadeem Shehzad's avatar
Nadeem Shehzad committed

  /** Module for status monitoring depending on a minimum threshold value*/
  template<typename T>
  struct MinMonitor : public StatusMonitorImpl<T> {
    using StatusMonitorImpl<T>::StatusMonitorImpl;
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /** WARNING state to be reported if threshold is crossed*/
    ScalarPushInput<T> warning{this, "lowerWarningThreshold", "", "", StatusMonitor::_parameterTags};
    /** ERROR state to be reported if threshold is crossed*/
    ScalarPushInput<T> error{this, "lowerErrorThreshold", "", "", StatusMonitor::_parameterTags};
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /**This is where state evaluation is done*/
      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warning, error};
        if(StatusMonitorImpl<T>::disable != 0) {
          StatusMonitorImpl<T>::status = OFF;
        else if(StatusMonitorImpl<T>::oneUp.watch <= error) {
          StatusMonitorImpl<T>::status = ERROR;
        else if(StatusMonitorImpl<T>::oneUp.watch <= warning) {
          StatusMonitorImpl<T>::status = WARNING;
          StatusMonitorImpl<T>::status = OK;
        StatusMonitorImpl<T>::status.write();
Martin Killenberg's avatar
Martin Killenberg committed
        group.readAny();
Nadeem Shehzad's avatar
Nadeem Shehzad committed

  /** Module for status monitoring depending on range of threshold values.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 * As long as a monitored value is in the range defined by user it goes
 * to error or warning state. If the monitored value exceeds the upper limmit
 * or goes under the lowerthreshold the state reported will be always OK.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 * IMPORTANT: This module does not check for ill logic, so make sure to
 * set the ranges correctly to issue warning or error.
 */
  struct RangeMonitor : public StatusMonitorImpl<T> {
    using StatusMonitorImpl<T>::StatusMonitorImpl;
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /** WARNING state to be reported if value is in between the upper and
 * lower threshold including the start and end of thresholds.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 */
    ScalarPushInput<T> warningUpperThreshold{this, "upperWarningThreshold", "", "", StatusMonitor::_parameterTags};
    ScalarPushInput<T> warningLowerThreshold{this, "lowerWarningThreshold", "", "", StatusMonitor::_parameterTags};
Martin Killenberg's avatar
Martin Killenberg committed
    /** ERROR state to be reported if value is in between the upper and
 * lower threshold including the start and end of thresholds.
    ScalarPushInput<T> errorUpperThreshold{this, "upperErrorThreshold", "", "", StatusMonitor::_parameterTags};
    ScalarPushInput<T> errorLowerThreshold{this, "lowerErrorThreshold", "", "", StatusMonitor::_parameterTags};

    /**This is where state evaluation is done*/
      /** If there is a change either in value monitored or in thershold values, the status is re-evaluated*/
      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, warningUpperThreshold,
          warningLowerThreshold, errorUpperThreshold, errorLowerThreshold};
        if(StatusMonitorImpl<T>::disable != 0) {
          StatusMonitorImpl<T>::status = OFF;
Martin Killenberg's avatar
Martin Killenberg committed
        // Check for error limits first. Like this they supersede the warning,
        // even if they are stricter then the warning limits (mis-configuration)
        else if(StatusMonitorImpl<T>::oneUp.watch <= errorLowerThreshold ||
            StatusMonitorImpl<T>::oneUp.watch >= errorUpperThreshold) {
          StatusMonitorImpl<T>::status = ERROR;
        else if(StatusMonitorImpl<T>::oneUp.watch <= warningLowerThreshold ||
            StatusMonitorImpl<T>::oneUp.watch >= warningUpperThreshold) {
          StatusMonitorImpl<T>::status = WARNING;
          StatusMonitorImpl<T>::status = OK;
        StatusMonitorImpl<T>::status.write();
Martin Killenberg's avatar
Martin Killenberg committed
        group.readAny();

  /** Module for status monitoring of an exact value.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 * If value monitored is not exactly the same. an error state will be
  struct ExactMonitor : public StatusMonitorImpl<T> {
    using StatusMonitorImpl<T>::StatusMonitorImpl;
    /**ERROR state if value is not equal to requiredValue*/
    ScalarPushInput<T> requiredValue{this, "requiredValue", "", "", StatusMonitor::_parameterTags};
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    /**This is where state evaluation is done*/
      /** If there is a change either in value monitored or in requiredValue, the status is re-evaluated*/
      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, requiredValue};
        if(StatusMonitorImpl<T>::disable != 0) {
          StatusMonitorImpl<T>::status = OFF;
        else if(StatusMonitorImpl<T>::oneUp.watch != requiredValue) {
          StatusMonitorImpl<T>::status = ERROR;
          StatusMonitorImpl<T>::status = OK;
        StatusMonitorImpl<T>::status.write();
Martin Killenberg's avatar
Martin Killenberg committed
        group.readAny();
Nadeem Shehzad's avatar
Nadeem Shehzad committed

  /** Module for On/off status monitoring.
Nadeem Shehzad's avatar
Nadeem Shehzad committed
 * If value monitored is different then desired state (on/off) an error
 * will be reported, otherwise OFF(0) or OK(1) depending on state.
 */
  struct StateMonitor : public StatusMonitorImpl<T> {
    using StatusMonitorImpl<T>::StatusMonitorImpl;
Nadeem Shehzad's avatar
Nadeem Shehzad committed

    ScalarPushInput<T> nominalState{this, "nominalState", "", "", StatusMonitor::_parameterTags};

    /**This is where state evaluation is done*/
      /** If there is a change either in value monitored or in state, the status is re-evaluated*/
      ReadAnyGroup group{StatusMonitorImpl<T>::oneUp.watch, StatusMonitorImpl<T>::disable, nominalState};
        if(StatusMonitorImpl<T>::disable != 0) {
          StatusMonitorImpl<T>::status = OFF;
        else if(StatusMonitorImpl<T>::oneUp.watch != nominalState) {
          StatusMonitorImpl<T>::status = ERROR;
        else if(nominalState == OK || nominalState == OFF) {
          StatusMonitorImpl<T>::status = nominalState;
Nadeem Shehzad's avatar
Nadeem Shehzad committed
        else {
          //no correct value
          StatusMonitorImpl<T>::status = ERROR;
Nadeem Shehzad's avatar
Nadeem Shehzad committed
        }
        StatusMonitorImpl<T>::status.write();
Martin Killenberg's avatar
Martin Killenberg committed
        group.readAny();

#endif // CHIMERATK_STATUS_MONITOR_H