/* * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package interactivespaces.event.trigger; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * A simple trigger which watches for the change of a value and will signal * rising or falling states. * * <p> * The trigger supports hysteresis. * * @author Keith M. Hughes */ public class SimpleThresholdTrigger implements ResettableTrigger { /** * The current value of the trigger. */ private int value; /** * The threshold when the trigger will trigger. */ private int threshold; /** * The hysteresis of the trigger. */ private int hysteresis; /** * The current state of the trigger. */ private TriggerState state; /** * The previous state of the trigger. */ private TriggerState previousState; /** * Collection of listeners for trigger point events. */ private List<TriggerListener> listeners = new CopyOnWriteArrayList<TriggerListener>(); public SimpleThresholdTrigger() { threshold = 0; hysteresis = 0; value = 0; state = previousState = TriggerState.NOT_TRIGGERED; } /** * Set the threshold parameter. * * @param threshold * The new value of the triggering threshold. * @param hysteresis * The new value of the hysteresis. */ public SimpleThresholdTrigger setThreshold(int threshold) { this.threshold = threshold; return this; } /** * Set the triggering hysteresis parameter. * * @param hysteresis * The new value of the hysteresis. */ public SimpleThresholdTrigger setHysteresis(int hysteresis) { this.hysteresis = hysteresis; return this; } /** * Update the value, potentially triggering and notifying listeners. * * @param newValue * The new value. */ public void update(int newValue) { TriggerState newState, lastState; synchronized (this) { if (newValue < (threshold - hysteresis)) { newState = TriggerState.NOT_TRIGGERED; lastState = changeState(newValue, newState); } else if (newValue > (threshold + hysteresis)) { newState = TriggerState.TRIGGERED; lastState = changeState(newValue, newState); } else { return; } } if ((lastState == TriggerState.NOT_TRIGGERED) && (newState == TriggerState.TRIGGERED)) { for (TriggerListener listener : listeners) { listener.onTrigger(this, newState, TriggerEventType.RISING); } } else if ((previousState == TriggerState.TRIGGERED) && (state == TriggerState.NOT_TRIGGERED)) { for (TriggerListener listener : listeners) { listener.onTrigger(this, newState, TriggerEventType.FALLING); } } } /** * Get the current value of the trigger. * * @return */ public int getValue() { return value; } @Override public TriggerState getState() { return state; } @Override public void reset() { TriggerState lastState = changeState(0, TriggerState.NOT_TRIGGERED); if (!lastState.equals(TriggerState.NOT_TRIGGERED)) { for (TriggerListener listener : listeners) { listener.onTrigger(this, TriggerState.NOT_TRIGGERED, TriggerEventType.FALLING); } } } /** * Change the state of the trigger in a thread safe way. * * @param newValue * the new value for the trigger * @param newState * the new state of the trigger * * @return the old value of the trigger */ private synchronized TriggerState changeState(int newValue, TriggerState newState) { value = newValue; previousState = state; state = newState; return previousState; } /** * Get the previous state of the trigger. * * @return */ public TriggerState getPreviousState() { return previousState; } @Override public void addListener(TriggerListener listener) { listeners.add(listener); } @Override public void removeListener(TriggerListener listener) { listeners.remove(listener); } }