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}