/**
   * @param kpi
   * @param windowName
   * @param level
   * @return DESC: Create Loop and raise Event if Loop/check is true
   */
  public static Loop getLoop(KPI kpi, String windowName, String level) {

    NSM_VALUE curValue = METRIC(kpi.getFullname()).VALUE();

    NSM_RELATIONALEVENT event = RELATIONALEVENT(NSM_EVENTTYPE.PERFORMANCEALERT(kpi.getEventCode()));

    NSM_VALUE thres = LITERALVALUE(0);

    NSM_VALUE devMean = LITERALVALUE(0);

    int trigMethod = kpi.getTriggerMethod();

    // 140423/DL - Fix trigMethod value. Previously was if ('D' != trigMethod)
    if (2 != trigMethod) {

      thres = NAMEDTHRESHOLD(kpi.getFullname() + "_PerfThreshold");
    }

    // 140423/DL - Fix trigMethod value. Previously was if ('T' != trigMethod)
    if (1 != trigMethod) {

      NSM_VALUE deviationConstant = NAMEDTHRESHOLD("Deviation_Constant");

      devMean =
          DEVIATIONFROMMEAN(
                  kpi.getFullname(),
                  NAMEDWINDOW(windowName),
                  kpi.getCondition() == '>'
                      ? deviationConstant
                      : deviationConstant.MULTIPLIEDBY(-1))
              .VALUE();
    }

    // Use Top N to narrow down the number of tasks
    boolean descendingTopN = false;

    if (('g' == kpi.getCondition()) || ('>' == kpi.getCondition())) {

      descendingTopN = true;
    }

    NSM_ISINTOPN isTopN =
        ISINTOPN(NAMEDTHRESHOLD("KPI_TOPN"), METRICVALUE(kpi.getFullname()), descendingTopN);

    NSM_IS isChkCond = getCheckCondition(trigMethod, kpi.getCondition(), curValue, thres, devMean);

    // End

    Loop loop = new Loop(NEIGHBORS());

    Check checkLoop = loop.onEach().newCheck(isTopN).onTrue().newCheck(isChkCond);

    if (isDebugMode) {

      checkLoop
          .onTrue()
          .output(
              TOKENISEDSTRING(
                  "Loop = TRUE | KPI Value = '$1' | Trigger = '$2' | Condition = '$3' | Threshold = '$4' | Mean = '$5'",
                  curValue,
                  LITERALVALUE(trigMethod),
                  LITERALSTRING(Character.toString(kpi.getCondition())),
                  thres,
                  devMean));

      checkLoop
          .onFalse()
          .output(
              TOKENISEDSTRING(
                  "Loop = FALSE | KPI Value = '$1' | Trigger = '$2' | Condition = '$3' | Threshold = '$4' | Mean = '$5'",
                  curValue,
                  LITERALVALUE(trigMethod),
                  LITERALSTRING(Character.toString(kpi.getCondition())),
                  thres,
                  devMean));
    }

    checkLoop
        .onTrue()
        .newAction(
            RAISEEVENT(
                event,
                getDescRelational(kpi, trigMethod, curValue, thres, devMean, level),
                PROPERTY("KPI Value", TOKENISEDSTRING("$1" + kpi.getUnit(), ROUND(curValue, 2))),
                PROPERTY("KPI Name", kpi.getFullname()),
                PROPERTY("Source target", SOURCESTRING(ELEMENTNAME())),
                PROPERTY("Category", kpi.getCategory()),
                // 130503/DL -Added SiteName and ControllerName
                PROPERTY("Site Name", PARENTNAME("Site")),
                PROPERTY("Controller Name", PARENTNAME("Controller")),
                PROPERTY("Level", level),
                PROPERTY("Technology", ELEMENTCATEGORY()),
                PROPERTY("Check method", getCheckMethod(kpi.getTriggerMethod())),
                PROPERTY("Vendor", PARAMETERSTRING("Vendor"))));

    return loop;
  }
  /**
   * @param kpi
   * @param windowName
   * @param level
   * @return DESC: Create Check and raise Event if Check is true
   */
  public static Check getCheck(KPI kpi, String windowName, String level) {

    NSM_VALUE curValue = METRIC(kpi.getFullname()).VALUE();

    NSM_EVENT event = EVENT(NSM_EVENTTYPE.PERFORMANCEALERT(kpi.getEventCode()));

    NSM_VALUE thres = LITERALVALUE(0);

    NSM_VALUE devMean = LITERALVALUE(3.329);

    int trigMethod = kpi.getTriggerMethod();

    if (2 != trigMethod) { // if Trigger method is not "Deviation Mean only"

      thres = NAMEDTHRESHOLD(kpi.getFullname() + "_PerfThreshold");
    }

    if (1 != trigMethod) { // if Trigger method is not "Threshold only"

      NSM_VALUE deviationConstant = NAMEDTHRESHOLD("Deviation_Constant");

      devMean =
          DEVIATIONFROMMEAN(
                  kpi.getFullname(),
                  NAMEDWINDOW(windowName),
                  kpi.getCondition() == '>'
                      ? deviationConstant
                      : deviationConstant.MULTIPLIEDBY(-1))
              .VALUE();
    }

    // Use Top N to narrow down the number of tasks
    // Desc - Highest first
    // Asc - Lowest first

    boolean descendingTopN = false;

    if (('g' == kpi.getCondition()) || ('>' == kpi.getCondition())) {

      descendingTopN = true;
    }

    Check checkTopN =
        new Check(
            ISINTOPN(NAMEDTHRESHOLD("KPI_TOPN"), METRICVALUE(kpi.getFullname()), descendingTopN));

    // End

    Check check =
        new Check(getCheckCondition(trigMethod, kpi.getCondition(), curValue, thres, devMean));

    if (isDebugMode) {

      check
          .onTrue()
          .output(
              TOKENISEDSTRING(
                  "Check = TRUE | KPI Value = '$1' | Trigger = '$2' | Condition = '$3' | Threshold = '$4' | Mean = '$5'",
                  curValue,
                  LITERALVALUE(trigMethod),
                  LITERALSTRING(Character.toString(kpi.getCondition())),
                  thres,
                  devMean));

      check
          .onFalse()
          .output(
              TOKENISEDSTRING(
                  "Check = FALSE | KPI Value = '$1' | Trigger = '$2' | Condition = '$3' | Threshold = '$4' | Mean = '$5'",
                  curValue,
                  LITERALVALUE(trigMethod),
                  LITERALSTRING(Character.toString(kpi.getCondition())),
                  thres,
                  devMean));
    }

    check
        .onTrue()
        .newAction(
            RAISEEVENT(
                event,
                getDesc(kpi, trigMethod, curValue, thres, devMean, level),
                PROPERTY("KPI Value", TOKENISEDSTRING("$1" + kpi.getUnit(), ROUND(curValue, 2))),
                PROPERTY("KPI Name", kpi.getFullname()),
                PROPERTY("Category", kpi.getCategory()),
                // 130503/DL -Added SiteName and ControllerName
                PROPERTY("Site Name", PARENTNAME("Site")),
                PROPERTY("Controller Name", PARENTNAME("Controller")),
                PROPERTY("Level", level),
                PROPERTY("Element", ELEMENTNAME()),
                PROPERTY("Technology", ELEMENTCATEGORY()),
                PROPERTY("Check method", getCheckMethod(kpi.getTriggerMethod())),
                PROPERTY("Vendor", PARAMETERSTRING("Vendor"))));

    checkTopN.onTrue().addCheck(check);

    return checkTopN;
  }