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(PROPERTY_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 if (newState != ON && newState != OFF) { 124 throw new IllegalArgumentException("cannot set state value " + newState); 125 } 126 127 // do the state change in the hardware 128 doNewState(mState, newState); // old state, new state 129 // change value and tell listeners 130 notifyStateChange(mState, newState); 131 } 132 133 /** 134 * Change the stored target intensity value and do notification, but don't 135 * change anything in the hardware. 136 * 137 * @param intensity intensity value 138 */ 139 @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "OK to compare floating point") 140 protected void notifyTargetIntensityChange(double intensity) { 141 double oldValue = mCurrentIntensity; 142 mCurrentIntensity = intensity; 143 if (oldValue != intensity) { 144 firePropertyChange(PROPERTY_TARGET_INTENSITY, oldValue, intensity); 145 } 146 } 147 148 /** 149 * Change the stored state value and do notification, but don't change 150 * anything in the hardware. 151 * 152 * @param oldState old value 153 * @param newState new value 154 */ 155 protected void notifyStateChange(int oldState, int newState) { 156 mState = newState; 157 if (oldState != newState) { 158 firePropertyChange(PROPERTY_KNOWN_STATE, oldState, newState); 159 } 160 } 161 162 /** 163 * Implement the specific change of state needed by hardware. 164 * 165 * @param oldState old state 166 * @param newState new state 167 */ 168 protected void doNewState(int oldState, int newState) { 169 } 170 171 @Override 172 public int getState() { 173 return mState; 174 } 175 176 /** 177 * Activate a light activating all its LightControl objects. 178 */ 179 @Override 180 public void activateLight() { 181 lightControlList.forEach(LightControl::activateLightControl); 182 mActive = true; // set flag for control listeners 183 } 184 185 /** 186 * Deactivate a light by deactivating each of its LightControl objects. 187 */ 188 @Override 189 public void deactivateLight() { 190 // skip if Light is not active 191 if (mActive) { // check if flag set for control listeners 192 lightControlList.forEach(LightControl::deactivateLightControl); 193 mActive = false; // unset flag for control listeners 194 } 195 } 196 197 /* 198 * LightControl management methods 199 */ 200 201 @Override 202 public void clearLightControls() { 203 // deactivate all Light Controls if any are active 204 deactivateLight(); 205 // clear all LightControls, if there are any 206 for (int i = lightControlList.size() - 1; i >= 0; i--) { 207 lightControlList.remove(i); 208 } 209 } 210 211 /** {@inheritDoc} 212 */ 213 @Override 214 public void addLightControl(LightControl c) { 215 if (lightControlList.contains(c)) { 216 log.debug("not adding duplicate LightControl {}", c); 217 return; 218 } 219 lightControlList.add(c); 220 } 221 222 @Override 223 public List<LightControl> getLightControlList() { 224 return new ArrayList<>(lightControlList); 225 } 226 227 @Override 228 public List<jmri.NamedBeanUsageReport> getUsageReport(jmri.NamedBean bean) { 229 List<jmri.NamedBeanUsageReport> report = new ArrayList<>(); 230 jmri.SensorManager sm = jmri.InstanceManager.getDefault(jmri.SensorManager.class); 231 jmri.TurnoutManager tm = jmri.InstanceManager.getDefault(jmri.TurnoutManager.class); 232 if (bean != null) { 233 getLightControlList().forEach( control -> { 234 String descText = control.getDescriptionText(""); 235 if (bean.equals(sm.getSensor(control.getControlSensorName()))) { 236 report.add(new jmri.NamedBeanUsageReport("LightControlSensor1", descText)); // NOI18N 237 } 238 if (bean.equals(sm.getSensor(control.getControlSensor2Name()))) { 239 report.add(new jmri.NamedBeanUsageReport("LightControlSensor2", descText)); // NOI18N 240 } 241 if (bean.equals(sm.getSensor(control.getControlTimedOnSensorName()))) { 242 report.add(new jmri.NamedBeanUsageReport("LightControlSensorTimed", descText)); // NOI18N 243 } 244 if (bean.equals(tm.getTurnout(control.getControlTurnoutName()))) { 245 report.add(new jmri.NamedBeanUsageReport("LightControlTurnout", descText)); // NOI18N 246 } 247 }); 248 } 249 return report; 250 } 251 252 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractLight.class); 253 254}