001package jmri.implementation;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import java.util.ArrayList;
005import java.util.List;
006import javax.annotation.Nonnull;
007import jmri.Light;
008import jmri.LightControl;
009
010/**
011 * Abstract class providing partial implementation of the Light interface.
012 * <p>
013 * Light objects require a number of instance variables. Since Light objects are
014 * created using the standard JMRI systemName/userName concept, accessor
015 * routines are provided for setting and editing these instance variables.
016 * <p>
017 * Each Light may have one or more control mechanisms, of the types defined in
018 * the Light interface. A Light may also not have any control mechanisms
019 * defined.
020 * <p>
021 * Information for each control mechanism is held in LightControl objects, which
022 * also implement the logic for control. A list of LightControls, if any, is
023 * kept here, and activation and deactivation of LightControls is through this
024 * module.
025 * <p>
026 * Instance variables are divided into system-independent and system dependent
027 * categories. System independent instance variables are defined here, and their
028 * accessor routines are implemented here.
029 * <p>
030 * This implementation provides a notional implementation of intensity and
031 * transitions. The user can set intensity so long as it's at least the max
032 * value (default 1.0) or no more than the minimum value (default 0.0). In that
033 * case, the setTargetIntensity operations become a setState to ON or OFF.
034 * Setting a target intensity between the min and max is an error, because this
035 * type of Light does not support a true analog intensity. Transitions never
036 * happen, and setting a TransitionTime greater than 0.0 gives an exception.
037 * <p>
038 * Since this form of Light does not do variable intensity nor transitions, it
039 * stores both CurrentIntensity and TargetIntensity in a single location,
040 * forcing them to be the same
041 *
042 * @author Dave Duchamp Copyright (C) 2004, 2010
043 * @author Ken Cameron Copyright (C) 2008
044 * @author Bob Jacobsen Copyright (C) 2008
045 */
046public abstract class AbstractLight extends AbstractNamedBean
047        implements Light {
048
049    public AbstractLight(String systemName, String userName) {
050        super(systemName, userName);
051    }
052
053    public AbstractLight(String systemName) {
054        super(systemName);
055    }
056
057    @Override
058    public String getBeanType() {
059        return Bundle.getMessage("BeanNameLight");
060    }
061
062    /**
063     * System independent instance variables (saved between runs).
064     */
065    protected List<LightControl> lightControlList = new ArrayList<>();
066    protected double mMaxIntensity = 1.0;
067    protected double mMinIntensity = 0.0;
068
069    /**
070     * System independent operational instance variables (not saved between
071     * runs).
072     */
073    protected double mCurrentIntensity = 0.0;
074    protected boolean mActive = false; // used to indicate if LightControls are active
075    protected boolean mEnabled = true;
076    protected int mState = OFF;
077
078    @Override
079    @Nonnull
080    public String describeState(int state) {
081        switch (state) {
082            case ON: return Bundle.getMessage("StateOn");
083            case OFF: return Bundle.getMessage("StateOff");
084            default: return super.describeState(state);
085        }
086    }
087
088    /**
089     * Get enabled status.
090     *
091     * @return enabled status
092     */
093    @Override
094    public boolean getEnabled() {
095        return mEnabled;
096    }
097
098    /**
099     * Set enabled status.
100     *
101     * @param v status to set
102     */
103    @Override
104    public void setEnabled(boolean v) {
105        boolean old = mEnabled;
106        mEnabled = v;
107        if (old != v) {
108            firePropertyChange("Enabled", old, v);
109        }
110    }
111
112    /**
113     * Handle a request for a state change. For these lights, ON and OFF just
114     * transition immediately between MinIntensity and MaxIntensity.
115     * Ignores any outputDelay setting for connection.
116     *
117     * @param newState new state
118     */
119    @Override
120    public void setState(int newState) {
121        log.debug("setState {} was {}", newState, mState);
122        
123        //int oldState = mState;
124        if (newState != ON && newState != OFF) {
125            throw new IllegalArgumentException("cannot set state value " + newState);
126        }
127        
128        // do the state change in the hardware
129        doNewState(mState, newState); // old state, new state
130        // change value and tell listeners
131        notifyStateChange(mState, newState);
132    }
133
134    /**
135     * Change the stored target intensity value and do notification, but don't
136     * change anything in the hardware.
137     *
138     * @param intensity intensity value
139     */
140    @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "OK to compare floating point")
141    protected void notifyTargetIntensityChange(double intensity) {
142        double oldValue = mCurrentIntensity;
143        mCurrentIntensity = intensity;
144        if (oldValue != intensity) {
145            firePropertyChange("TargetIntensity", oldValue, intensity);
146        }
147    }
148
149    /**
150     * Change the stored state value and do notification, but don't change
151     * anything in the hardware.
152     *
153     * @param oldState old value
154     * @param newState new value
155     */
156    protected void notifyStateChange(int oldState, int newState) {
157        mState = newState;
158        if (oldState != newState) {
159            firePropertyChange("KnownState", oldState, newState);
160        }
161    }
162
163    /**
164     * Implement the specific change of state needed by hardware.
165     *
166     * @param oldState old state
167     * @param newState new state
168     */
169    protected void doNewState(int oldState, int newState) {
170    }
171
172    @Override
173    public int getState() {
174        return mState;
175    }
176
177    /**
178     * Activate a light activating all its LightControl objects.
179     */
180    @Override
181    public void activateLight() {
182        lightControlList.stream().forEach((lc) -> {
183            lc.activateLightControl();
184        });
185        mActive = true; // set flag for control listeners
186    }
187
188    /**
189     * Deactivate a light by deactivating each of its LightControl objects.
190     */
191    @Override
192    public void deactivateLight() {
193        // skip if Light is not active
194        if (mActive) { // check if flag set for control listeners
195            lightControlList.stream().forEach((lc) -> {
196                lc.deactivateLightControl();
197            });
198            mActive = false; // unset flag for control listeners
199        }
200    }
201
202    /*
203     * LightControl management methods
204     */
205
206    @Override
207    public void clearLightControls() {
208        // deactivate all Light Controls if any are active
209        deactivateLight();
210        // clear all LightControls, if there are any
211        for (int i = lightControlList.size() - 1; i >= 0; i--) {
212            lightControlList.remove(i);
213        }
214    }
215
216    /** {@inheritDoc}
217     */
218    @Override
219    public void addLightControl(LightControl c) {
220        if (lightControlList.contains(c)) {
221            log.debug("not adding duplicate LightControl {}", c);
222            return;
223        }
224        lightControlList.add(c);
225    }
226
227    @Override
228    public List<LightControl> getLightControlList() {
229        List<LightControl> listCopy = new ArrayList<>();
230        lightControlList.stream().forEach((lightControlList1) -> {
231            listCopy.add(lightControlList1);
232        });
233        return listCopy;
234    }
235
236    @Override
237    public List<jmri.NamedBeanUsageReport> getUsageReport(jmri.NamedBean bean) {
238        List<jmri.NamedBeanUsageReport> report = new ArrayList<>();
239        jmri.SensorManager sm = jmri.InstanceManager.getDefault(jmri.SensorManager.class);
240        jmri.TurnoutManager tm = jmri.InstanceManager.getDefault(jmri.TurnoutManager.class);
241        if (bean != null) {
242            getLightControlList().forEach((control) -> {
243                String descText = control.getDescriptionText("");
244                if (bean.equals(sm.getSensor(control.getControlSensorName()))) {
245                    report.add(new jmri.NamedBeanUsageReport("LightControlSensor1", descText));  // NOI18N
246                }
247                if (bean.equals(sm.getSensor(control.getControlSensor2Name()))) {
248                    report.add(new jmri.NamedBeanUsageReport("LightControlSensor2", descText));  // NOI18N
249                }
250                if (bean.equals(sm.getSensor(control.getControlTimedOnSensorName()))) {
251                    report.add(new jmri.NamedBeanUsageReport("LightControlSensorTimed", descText));  // NOI18N
252                }
253                if (bean.equals(tm.getTurnout(control.getControlTurnoutName()))) {
254                    report.add(new jmri.NamedBeanUsageReport("LightControlTurnout", descText));  // NOI18N
255                }
256            });
257        }
258        return report;
259    }
260
261    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractLight.class);
262
263}