/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2008-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.threshd;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.xml.event.Event;
import org.springframework.util.Assert;
/**
* This works similar to <tt>absoluteChange</tt>; however, the <tt>trigger</tt> value
* is used to re-arm the event after so many iterations with an unchanged delta.
*
* @author bdymek
* @version $Id: $
*/
public class ThresholdEvaluatorRearmingAbsoluteChange implements ThresholdEvaluator {
private static final String TYPE = "rearmingAbsoluteChange";
/** {@inheritDoc} */
public ThresholdEvaluatorState getThresholdEvaluatorState(BaseThresholdDefConfigWrapper threshold) {
return new ThresholdEvaluatorStateRearmingAbsoluteChange(threshold);
}
/** {@inheritDoc} */
public boolean supportsType(String type) {
return TYPE.equals(type);
}
public static class ThresholdEvaluatorStateRearmingAbsoluteChange extends AbstractThresholdEvaluatorState {
private BaseThresholdDefConfigWrapper m_thresholdConfig;
private double m_lastSample = Double.NaN;
private double m_previousTriggeringSample = Double.NaN;
private int m_triggerCount = 0;
public ThresholdEvaluatorStateRearmingAbsoluteChange(BaseThresholdDefConfigWrapper threshold) {
Assert.notNull(threshold, "threshold argument cannot be null");
setThresholdConfig(threshold);
}
public String getType() {
return getThresholdConfig().getType().toString();
}
public void setThresholdConfig(BaseThresholdDefConfigWrapper thresholdConfig) {
Assert.notNull(thresholdConfig.getType(), "threshold must have a 'type' value set");
Assert.notNull(thresholdConfig.getDatasourceExpression(), "threshold must have a 'ds-name' value set");
Assert.notNull(thresholdConfig.getDsType(), "threshold must have a 'ds-type' value set");
Assert.isTrue(thresholdConfig.hasValue(), "threshold must have a 'value' value set");
Assert.isTrue(thresholdConfig.hasRearm(), "threshold must have a 'rearm' value set");
Assert.isTrue(thresholdConfig.hasTrigger(), "threshold must have a 'trigger' value set");
Assert.isTrue(TYPE.equals(thresholdConfig.getType()), "threshold for ds-name '" + thresholdConfig.getDatasourceExpression() + "' has type of '" + thresholdConfig.getType() + "', but this evaluator only supports thresholds with a 'type' value of '" + TYPE + "'");
Assert.isTrue(!Double.isNaN(thresholdConfig.getValue()), "threshold must have a 'value' value that is a number");
Assert.isTrue(thresholdConfig.getValue() != Double.POSITIVE_INFINITY && thresholdConfig.getValue() != Double.NEGATIVE_INFINITY, "threshold must have a 'value' value that is not positive or negative infinity");
m_thresholdConfig = thresholdConfig;
}
public BaseThresholdDefConfigWrapper getThresholdConfig() {
return m_thresholdConfig;
}
public Status evaluate(double dsValue) {
// log().debug(TYPE + " threshold evaluating, sample value="+dsValue);
try {
if(!Double.valueOf(getPreviousTriggeringSample()).isNaN()) {
++m_triggerCount;
if(!wasTriggered(dsValue) && (m_triggerCount >= getThresholdConfig().getTrigger())) {
setPreviousTriggeringSample(Double.NaN);
m_triggerCount = 0;
log().debug(TYPE + " threshold rearmed, sample value="+dsValue);
return Status.RE_ARMED;
}
} else if (wasTriggered(dsValue)) {
setPreviousTriggeringSample(getLastSample());
m_triggerCount = 0;
log().debug(TYPE + " threshold triggered, sample value="+dsValue);
return Status.TRIGGERED;
}
} finally {
setLastSample(dsValue);
}
return Status.NO_CHANGE;
}
private boolean wasTriggered(double dsValue) {
// Test Code
// if(Double.valueOf(getPreviousTriggeringSample()).isNaN()) {
// log().debug(TYPE + " threshold evaluate trigger, sample value="+dsValue);
// return true;
// }
if(Double.valueOf(dsValue).isNaN())
return false;
if(Double.valueOf(getLastSample()).isNaN())
return false;
double threshold = Math.abs(getLastSample() - dsValue);
// Test Code
// log().debug(TYPE + " threshold evaluate trigger, sample value="+dsValue+",prev value="+getLastSample()+",thresh="+threshold+",trigger="+getThresholdConfig().getValue());
return threshold >= getThresholdConfig().getValue();
}
public Double getLastSample() {
return m_lastSample;
}
public void setLastSample(double lastSample) {
m_lastSample = lastSample;
}
public Event getEventForState(Status status, Date date, double dsValue, CollectionResourceWrapper resource) {
if (status == Status.TRIGGERED) {
String uei=getThresholdConfig().getTriggeredUEI();
if(uei==null || "".equals(uei)) {
uei=EventConstants.REARMING_ABSOLUTE_CHANGE_EXCEEDED_EVENT_UEI;
}
return createBasicEvent(uei, date, dsValue, resource);
}
if (status == Status.RE_ARMED) {
String uei=getThresholdConfig().getRearmedUEI();
if(uei==null || "".equals(uei)) {
uei=EventConstants.REARMING_ABSOLUTE_CHANGE_REARM_EVENT_UEI;
}
return createBasicEvent(uei, date, dsValue, resource);
}
return null;
}
private Event createBasicEvent(String uei, Date date, double dsValue, CollectionResourceWrapper resource) {
Map<String,String> params = new HashMap<String,String>();
params.put("previousValue", formatValue(getPreviousTriggeringSample()));
params.put("threshold", Double.toString(getThresholdConfig().getValue()));
params.put("trigger", Integer.toString(getThresholdConfig().getTrigger()));
// params.put("rearm", Double.toString(getThresholdConfig().getRearm()));
return createBasicEvent(uei, date, dsValue, resource, params);
}
public double getPreviousTriggeringSample() {
return m_previousTriggeringSample;
}
public void setPreviousTriggeringSample(double previousTriggeringSample) {
m_previousTriggeringSample = previousTriggeringSample;
}
public ThresholdEvaluatorState getCleanClone() {
return new ThresholdEvaluatorStateRearmingAbsoluteChange(m_thresholdConfig);
}
public boolean isTriggered() {
return wasTriggered(m_previousTriggeringSample); // TODO Is that right ?
}
public void clearState() {
// Based on what evaluator does for rearmed state
m_lastSample = Double.NaN;
m_triggerCount = 0;
setPreviousTriggeringSample(Double.NaN);
}
}
}