001package jmri.implementation; 002 003import java.util.*; 004import javax.annotation.*; 005 006import jmri.NamedBean; 007import jmri.NamedBeanHandle; 008import jmri.NamedBeanUsageReport; 009 010import jmri.InstanceManager; 011import jmri.SignalAppearanceMap; 012import jmri.SignalMast; 013import jmri.SignalSystem; 014 015/** 016 * Abstract class providing the basic logic of the SignalMast interface. 017 * 018 * @author Bob Jacobsen Copyright (C) 2009 019 */ 020public abstract class AbstractSignalMast extends AbstractNamedBean 021 implements SignalMast, java.beans.VetoableChangeListener { 022 023 public AbstractSignalMast(String systemName, String userName) { 024 super(systemName, userName); 025 } 026 027 public AbstractSignalMast(String systemName) { 028 super(systemName); 029 } 030 031 @Override 032 public void setAspect(@Nonnull String aspect) { 033 String oldAspect = this.aspect; 034 this.aspect = aspect; 035 this.speed = (String) getSignalSystem().getProperty(aspect, "speed"); 036 firePropertyChange(PROPERTY_ASPECT, oldAspect, aspect); 037 } 038 039 @Override 040 public String getAspect() { 041 return aspect; 042 } 043 protected String aspect = null; 044 045 public String getSpeed() { 046 return speed; 047 } 048 protected String speed = null; 049 050 /** 051 * The state is the index of the current aspect in the list of possible 052 * aspects. 053 */ 054 @Override 055 public int getState() { 056 return -1; 057 } 058 059 @Override 060 public void setState(int i) { 061 } 062 063 /** 064 * By default, signals are lit. 065 */ 066 private boolean mLit = true; 067 068 /** 069 * Default behavior for "lit" property is to track value and return it. 070 */ 071 @Override 072 public boolean getLit() { 073 return mLit; 074 } 075 076 /** 077 * By default, signals are not held. 078 */ 079 private boolean mHeld = false; 080 081 /** 082 * "Held" property is just tracked and notified. 083 */ 084 @Override 085 public boolean getHeld() { 086 return mHeld; 087 } 088 089 /** 090 * Set the lit property. 091 * <p> 092 * This acts on all the SignalHeads included in this SignalMast 093 * 094 * @param newLit the new value of lit 095 */ 096 @Override 097 public void setLit(boolean newLit) { 098 boolean oldLit = mLit; 099 mLit = newLit; 100 if (oldLit != newLit) { 101 firePropertyChange(PROPERTY_LIT, oldLit, newLit); 102 } 103 } 104 105 /** 106 * Set the held property of the signal mast. 107 * <p> 108 * Note that this does not directly effect the output on the layout; the 109 * held property is a local variable which effects the aspect only via 110 * higher-level logic. 111 * 112 * @param newHeld the new value of the help property 113 */ 114 @Override 115 public void setHeld(boolean newHeld) { 116 boolean oldHeld = mHeld; 117 mHeld = newHeld; 118 if (oldHeld != newHeld) { 119 firePropertyChange(PROPERTY_HELD, oldHeld, newHeld); 120 } 121 } 122 123 @Override 124 public boolean isAtStop() { 125 // should this also include DANGER? 126 return "0".equals(speed); 127 } 128 129 @Override 130 public boolean isShowingRestricting() { 131 String displayedAspect = getAspect(); 132 if ( displayedAspect != null && displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE))) return true; 133 return false; 134 } 135 136 @Override 137 public boolean isCleared() { 138 String displayedAspect = getAspect(); 139 if ( displayedAspect == null ) { 140 return false; 141 } 142 if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE))) return false; 143 if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD))) return false; 144 if (displayedAspect.equals(getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER))) return false; 145 return true; 146 } 147 148 protected DefaultSignalAppearanceMap map; 149 SignalSystem systemDefn; 150 151 boolean disablePermissiveSignalMastLogic = false; 152 153 @Override 154 public void setPermissiveSmlDisabled(boolean disabled) { 155 disablePermissiveSignalMastLogic = disabled; 156 firePropertyChange(PROPERTY_PERMISSIVE_SML_DISABLED, null, disabled); 157 } 158 159 /** 160 * {@inheritDoc } 161 */ 162 @Override 163 public boolean isPermissiveSmlDisabled() { 164 return disablePermissiveSignalMastLogic; 165 } 166 167 protected void configureSignalSystemDefinition(String name) { 168 systemDefn = InstanceManager.getDefault(jmri.SignalSystemManager.class).getSystem(name); 169 if (systemDefn == null) { 170 log.error("Did not find signal definition: {}", name); 171 throw new IllegalArgumentException("Signal definition not found: " + name); 172 } 173 } 174 175 protected void configureAspectTable(String signalSystemName, String aspectMapName) { 176 map = DefaultSignalAppearanceMap.getMap(signalSystemName, aspectMapName); 177 } 178 179 @Override 180 public SignalSystem getSignalSystem() { 181 return systemDefn; 182 } 183 184 @Override 185 public SignalAppearanceMap getAppearanceMap() { 186 return map; 187 } 188 189 protected ArrayList<String> disabledAspects = new ArrayList<>(1); 190 191 @Override 192 @Nonnull 193 public Vector<String> getValidAspects() { 194 java.util.Enumeration<String> e = map.getAspects(); 195 // copy List to Vector 196 Vector<String> v = new Vector<>(); 197 while (e.hasMoreElements()) { 198 String a = e.nextElement(); 199 if (!disabledAspects.contains(a)) { 200 v.add(a); 201 } 202 } 203 return v; 204 } 205 206 /** 207 * {@inheritDoc } 208 */ 209 @Override 210 public String getMastType() { 211 return mastType; 212 } 213 214 @Override 215 public void setMastType(@Nonnull String type) { 216 Objects.requireNonNull(type, "MastType cannot be null"); 217 mastType = type; 218 } 219 String mastType; 220 221 /** 222 * Get a list of all the known aspects for this mast, including those that 223 * have been disabled. 224 * 225 * @return list of known aspects; may be empty 226 */ 227 public Vector<String> getAllKnownAspects() { 228 java.util.Enumeration<String> e = map.getAspects(); 229 Vector<String> v = new Vector<>(); 230 while (e.hasMoreElements()) { 231 v.add(e.nextElement()); 232 } 233 return v; 234 } 235 236 public void setAspectDisabled(String aspect) { 237 if (aspect == null || aspect.isEmpty()) { 238 return; 239 } 240 if (!map.checkAspect(aspect)) { 241 log.warn("attempting to disable an aspect: {} that is not on the mast {}", aspect, getDisplayName()); 242 return; 243 } 244 if (!disabledAspects.contains(aspect)) { 245 disabledAspects.add(aspect); 246 firePropertyChange(PROPERTY_ASPECT_DISABLED, null, aspect); 247 } 248 } 249 250 public void setAspectEnabled(String aspect) { 251 if (aspect == null || aspect.isEmpty()) { 252 return; 253 } 254 if (!map.checkAspect(aspect)) { 255 log.warn("attempting to disable an aspect: {} that is not on the mast {}", aspect, getDisplayName()); 256 return; 257 } 258 if (disabledAspects.contains(aspect)) { 259 disabledAspects.remove(aspect); 260 firePropertyChange(PROPERTY_ASPECT_ENABLED, null, aspect); 261 } 262 } 263 264 public List<String> getDisabledAspects() { 265 return disabledAspects; 266 } 267 268 @Override 269 public boolean isAspectDisabled(String aspect) { 270 return disabledAspects.contains(aspect); 271 } 272 273 private boolean allowUnLit = true; 274 275 @Override 276 public void setAllowUnLit(boolean boo) { 277 allowUnLit = boo; 278 } 279 280 @Override 281 public boolean allowUnLit() { 282 return allowUnLit; 283 } 284 285 @Override 286 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 287 } 288 289 @Override 290 public String getBeanType() { 291 return Bundle.getMessage("BeanNameSignalMast"); 292 } 293 294 @Override 295 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 296 List<NamedBeanUsageReport> report = new ArrayList<>(); 297 if (bean != null) { 298 if (bean instanceof jmri.Turnout) { 299 if (this instanceof jmri.implementation.TurnoutSignalMast) { 300 var m = (jmri.implementation.TurnoutSignalMast) this; 301 var t = (jmri.Turnout) bean; 302 if (m.isTurnoutUsed(t)) { 303 report.add(new NamedBeanUsageReport("SignalMastTurnout")); // NOI18N 304 } 305 } else if (this instanceof jmri.implementation.MatrixSignalMast) { 306 var m = (jmri.implementation.MatrixSignalMast) this; 307 var t = (jmri.Turnout) bean; 308 if (m.isTurnoutUsed(t)) { 309 report.add(new NamedBeanUsageReport("SignalMastTurnout")); // NOI18N 310 } 311 } 312 } else if (bean instanceof jmri.SignalHead) { 313 if (this instanceof jmri.implementation.SignalHeadSignalMast) { 314 var m = (jmri.implementation.SignalHeadSignalMast) this; 315 var h = (jmri.SignalHead) bean; 316 for (NamedBeanHandle<jmri.SignalHead> handle : m.getHeadsUsed()) { 317 if (h.equals(handle.getBean())) { 318 report.add(new NamedBeanUsageReport("SignalMastSignalHead")); // NOI18N 319 } 320 } 321 } 322 } 323 } 324 return report; 325 } 326 327 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSignalMast.class); 328 329}