001package jmri.jmrit.ctc; 002 003import java.beans.PropertyChangeListener; 004import jmri.InstanceManager; 005import jmri.NamedBeanHandle; 006import jmri.NamedBeanHandleManager; 007import jmri.SignalAppearanceMap; 008import jmri.SignalMast; 009import jmri.SignalHead; 010import jmri.jmrit.ctc.ctcserialdata.*; 011 012/** 013 * Provide access to both signal masts and signal heads for the CTC system. 014 * <p> 015 * This class combines the NBHAbstractSignalCommon, NBHSignalMast and NBHSignalHead 016 * classes. OtherData _mSignalSystemType determines whether masts or heads are enabled. 017 * @author Dave Sand Copyright (C) 2020 018 */ 019public class NBHSignal { 020 021// Standard sane return values for the types indicated: 022 public static final Object DEFAULT_OBJECT_RV = null; // For any function that returns something derived from Java's Object. 023 public static final boolean DEFAULT_BOOLEAN_RV = false; // For any function that returns boolean. 024 public static final int DEFAULT_INT_RV = 0; // For any function that returns int. 025 public static final String DEFAULT_STRING_RV = "UNKNOWN"; // NOI18N For any function that returns String. 026 027// The "things" we're protecting: 028 private final NamedBeanHandle<SignalMast> _mNamedBeanHandleSignalMast; 029 private final NamedBeanHandle<SignalHead> _mNamedBeanHandleSignalHead; 030 031 private final boolean isSignalMast; // True for signal mast, false for signal head 032 private final String _mDangerAppearance; // The string to determine "Is the Signal all Red": 033 034 /** 035 * Create the named bean handle for either a signal mast or signal head. 036 * @param signal The signal name. 037 */ 038 public NBHSignal(String signal) { 039 isSignalMast = setSignalType(); 040 if (!ProjectsCommonSubs.isNullOrEmptyString(signal)) { 041 if (isSignalMast) { 042 // Cannot use a constant Instance manager reference due to the dynamic nature of tests. 043 SignalMast signalMast = InstanceManager.getDefault(jmri.SignalMastManager.class).getSignalMast(signal); 044 if (signalMast != null) { 045 _mNamedBeanHandleSignalMast = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(signal, signalMast); 046 String temp = getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER); 047 if (temp == null) temp = "Stop"; // NOI18N // Safety 048 _mDangerAppearance = temp; 049 _mNamedBeanHandleSignalHead = null; 050 if (valid()) InstanceManager.getDefault(CtcManager.class).putNBHSignal(signal, this); 051 return; 052 } 053 } else { 054 // Cannot use a constant Instance manager reference due to the dynamic nature of tests. 055 SignalHead signalHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(signal); 056 if (signalHead != null) { 057 _mNamedBeanHandleSignalHead = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(signal, signalHead); 058 _mDangerAppearance = "Stop"; // NOI18N // Never used, just required for "final" 059 _mNamedBeanHandleSignalMast = null; 060 if (valid()) InstanceManager.getDefault(CtcManager.class).putNBHSignal(signal, this); 061 return; 062 } 063 } 064 } 065 _mDangerAppearance = "Stop"; // NOI18N // Never used, just required for "final" 066 _mNamedBeanHandleSignalMast = null; 067 _mNamedBeanHandleSignalHead = null; 068 } 069 070 /** 071 * Set signal type using {@link OtherData#_mSignalSystemType}. 072 * @return true for mast, false if head. 073 */ 074 private boolean setSignalType() { 075 OtherData otherData = InstanceManager.getDefault(CtcManager.class).getOtherData(); 076 return otherData._mSignalSystemType == OtherData.SIGNAL_SYSTEM_TYPE.SIGNALMAST ? true : false; 077 078 } 079 080 public boolean valid() { 081 return _mNamedBeanHandleSignalMast != null || _mNamedBeanHandleSignalHead != null; 082 } // For those that want to know the internal state. 083 084 public Object getBean() { 085 if (!valid()) return null; 086 return isSignalMast ? _mNamedBeanHandleSignalMast.getBean() : _mNamedBeanHandleSignalHead.getBean(); 087 } 088 089 public Object getBeanHandle() { 090 if (!valid()) return null; 091 return isSignalMast ? _mNamedBeanHandleSignalMast : _mNamedBeanHandleSignalHead; 092 } 093 094 /** 095 * @return The signal's handle name. 096 */ 097 public String getHandleName() { 098 if (!valid()) return null; 099 return isSignalMast ? _mNamedBeanHandleSignalMast.getName() : _mNamedBeanHandleSignalHead.getName(); 100 } 101 102 public String getDisplayName() { 103 if (isSignalMast) { 104 if (_mNamedBeanHandleSignalMast == null) return DEFAULT_STRING_RV; 105 return _mNamedBeanHandleSignalMast.getBean().getDisplayName(); 106 } else { 107 if (_mNamedBeanHandleSignalHead == null) return DEFAULT_STRING_RV; 108 return _mNamedBeanHandleSignalHead.getBean().getDisplayName(); 109 } 110 } 111 112 public boolean isDanger() { 113 if (getHeld()) return true; // Safety. Problem in signal head, maybe same problem here? 114 return isSignalMast ? getAspect().equals(_mDangerAppearance) : SignalHead.RED == getAppearance(); 115 } 116 117 public void setCTCHeld(boolean held) { 118 setHeld(held); 119 } 120 121 public boolean getHeld() { 122 if (isSignalMast) { 123 if (_mNamedBeanHandleSignalMast == null) return DEFAULT_BOOLEAN_RV; 124 return _mNamedBeanHandleSignalMast.getBean().getHeld(); 125 } else { 126 if (_mNamedBeanHandleSignalHead == null) return DEFAULT_BOOLEAN_RV; 127 return _mNamedBeanHandleSignalHead.getBean().getHeld(); 128 } 129 } 130 131 public void setHeld(boolean newHeld) { 132 if (isSignalMast) { 133 if (_mNamedBeanHandleSignalMast == null) return; 134 _mNamedBeanHandleSignalMast.getBean().setHeld(newHeld); 135 if (newHeld) { 136 _mNamedBeanHandleSignalMast.getBean().setPermissiveSmlDisabled(true); 137 } 138 } else { 139 if (_mNamedBeanHandleSignalHead == null) return; 140 _mNamedBeanHandleSignalHead.getBean().setHeld(newHeld); 141 } 142 } 143 144 public void allowPermissiveSML() { 145 _mNamedBeanHandleSignalMast.getBean().setPermissiveSmlDisabled(false); 146 } 147 148 public void addPropertyChangeListener(PropertyChangeListener l) { 149 if (isSignalMast) { 150 if (_mNamedBeanHandleSignalMast == null) return; 151 _mNamedBeanHandleSignalMast.getBean().addPropertyChangeListener(l); 152 } else { 153 if (_mNamedBeanHandleSignalHead == null) return; 154 _mNamedBeanHandleSignalHead.getBean().addPropertyChangeListener(l); 155 } 156 } 157 158 public void removePropertyChangeListener(PropertyChangeListener l) { 159 if (isSignalMast) { 160 if (_mNamedBeanHandleSignalMast == null) return; 161 _mNamedBeanHandleSignalMast.getBean().removePropertyChangeListener(l); 162 } else { 163 if (_mNamedBeanHandleSignalHead == null) return; 164 _mNamedBeanHandleSignalHead.getBean().removePropertyChangeListener(l); 165 } 166 } 167 168 /** 169 * 170 * Function to insure that a non null aspect value is always returned to the caller. 171 * 172 * Background (regarding the value contained in "_mDangerAppearance"): 173 * In this objects constructor, "_mDangerAppearance" is set to getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER). 174 * If "...getSpecificAppearance..." returns "null" (undocumented in JMRI documents as of 9/18/2019), 175 * "_mDangerAppearance" is set to "Stop" for safety. 176 * So "_mDangerAppearance" will NEVER be null for use as follows: 177 * 178 * SignalMast.getAspect() can return "null" (undocumented in JMRI documents as of 9/18/2019) if (for instance) the signal has no 179 * rules (i.e. no "Discover" done yet, or the signal is shown on the screen as a big red "X"). 180 * In this case, we return "_mDangerAppearance". 181 * 182 * @return Return a guaranteed non null aspect name. 183 */ 184 public String getAspect() { 185 if (!isSignalMast) log.info("NBHSignal: getAspect called by signal head", new Exception("traceback")); 186 if (_mNamedBeanHandleSignalMast == null) return DEFAULT_STRING_RV; 187 String returnAspect = _mNamedBeanHandleSignalMast.getBean().getAspect(); 188 if (returnAspect == null) return _mDangerAppearance; // Safety 189 return returnAspect; 190 } 191 192 public SignalAppearanceMap getAppearanceMap() { 193 if (!isSignalMast) log.info("NBHSignal: getAppearanceMap called by signal head", new Exception("traceback")); 194 if (_mNamedBeanHandleSignalMast == null) return null; 195 return _mNamedBeanHandleSignalMast.getBean().getAppearanceMap(); 196 } 197 198 public int getAppearance() { 199 if (isSignalMast) log.info("NBHSignal: getAppearance called by signal mast", new Exception("traceback")); 200 if (_mNamedBeanHandleSignalHead == null) return DEFAULT_INT_RV; 201 return _mNamedBeanHandleSignalHead.getBean().getAppearance(); 202 } 203 204 public void setAppearance(int newAppearance) { 205 if (isSignalMast) log.info("NBHSignal: setAppearance called by signal mast", new Exception("traceback")); 206 if (_mNamedBeanHandleSignalHead == null) return; 207 _mNamedBeanHandleSignalHead.getBean().setAppearance(newAppearance); 208 } 209 210 /** 211 * Get an array of appearance indexes valid for the mast type. 212 * 213 * @return array of appearance state values available on this mast type 214 */ 215 public int[] getValidStates() { 216 if (isSignalMast) log.info("NBHSignal: getValidStates called by signal mast", new Exception("traceback")); 217 if (_mNamedBeanHandleSignalHead == null) return new int[0]; 218 return _mNamedBeanHandleSignalHead.getBean().getValidStates(); 219 } 220 221 /** 222 * Get an array of non-localized appearance keys valid for the mast type. 223 * For GUI application consider using (capitalized) {@link #getValidStateNames()} 224 * 225 * @return array of translated appearance names available on this mast type 226 */ 227 public String[] getValidStateKeys() { 228 if (isSignalMast) log.info("NBHSignal: getValidStateKeys called by signal mast", new Exception("traceback")); 229 if (_mNamedBeanHandleSignalHead == null) return new String[0]; 230 return _mNamedBeanHandleSignalHead.getBean().getValidStateKeys(); 231 } 232 233 /** 234 * Get an array of localized appearance descriptions valid for the mast type. 235 * For persistance and comparison consider using {@link #getValidStateKeys()} 236 * 237 * @return array of translated appearance names 238 */ 239 public String[] getValidStateNames() { 240 if (isSignalMast) log.info("NBHSignal: getValidStateNames called by signal mast", new Exception("traceback")); 241 if (_mNamedBeanHandleSignalHead == null) return new String[0]; 242 return _mNamedBeanHandleSignalHead.getBean().getValidStateNames(); 243 } 244 245 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NBHSignal.class); 246} 247