001package jmri.implementation; 002 003import java.util.*; 004 005import jmri.NamedBean; 006import jmri.NamedBeanUsageReport; 007 008/** 009 * Default implementation of the basic logic of the SignalHead interface. 010 * 011 * This class only claims support for the Red, Yellow and Green appearances, and 012 * their corresponding flashing forms. Support for Lunar is deferred to 013 * DefaultLunarSignalHead or an extended class. 014 * 015 * @author Bob Jacobsen Copyright (C) 2001, 2009 016 */ 017public abstract class DefaultSignalHead extends AbstractSignalHead { 018 019 public DefaultSignalHead(String systemName, String userName) { 020 super(systemName, userName); 021 } 022 023 public DefaultSignalHead(String systemName) { 024 super(systemName); 025 } 026 027 @Override 028 public void setAppearance(int newAppearance) { 029 int oldAppearance = mAppearance; // store the current appearance 030 mAppearance = newAppearance; 031 appearanceSetsFlashTimer(newAppearance); 032 033 /* there are circumstances (admittedly rare) where signals and turnouts can get out of sync 034 * allow 'newAppearance' to be set to resync these cases - P Cressman 035 * if (oldAppearance != newAppearance) */ 036 updateOutput(); 037 038 // notify listeners, if any 039 firePropertyChange("Appearance", oldAppearance, newAppearance); 040 } 041 042 /** 043 * Call to set timer when updating the appearance. 044 * 045 * @param newAppearance the new appearance 046 */ 047 protected void appearanceSetsFlashTimer(int newAppearance) { 048 if (mLit && ((newAppearance == FLASHGREEN) 049 || (newAppearance == FLASHYELLOW) 050 || (newAppearance == FLASHRED) 051 || (newAppearance == FLASHLUNAR))) { 052 startFlash(); 053 } 054 if ((!mLit) || ((newAppearance != FLASHGREEN) 055 && (newAppearance != FLASHYELLOW) 056 && (newAppearance != FLASHRED) 057 && (newAppearance != FLASHLUNAR))) { 058 stopFlash(); 059 } 060 } 061 062 @Override 063 public void setLit(boolean newLit) { 064 boolean oldLit = mLit; 065 mLit = newLit; 066 if (oldLit != newLit) { 067 if (mLit && ((mAppearance == FLASHGREEN) 068 || (mAppearance == FLASHYELLOW) 069 || (mAppearance == FLASHRED) 070 || (mAppearance == FLASHLUNAR))) { 071 startFlash(); 072 } 073 if (!mLit) { 074 stopFlash(); 075 } 076 updateOutput(); 077 // notify listeners, if any 078 firePropertyChange("Lit", oldLit, newLit); 079 } 080 } 081 082 /** 083 * Set the held parameter. 084 * <p> 085 * Note that this does not directly effect the output on the layout; the 086 * held parameter is a local variable which effects the aspect only via 087 * higher-level logic. 088 * 089 * @param newHeld new Held state, true if Held, to be compared with current 090 * Held state 091 */ 092 @Override 093 public void setHeld(boolean newHeld) { 094 boolean oldHeld = mHeld; 095 mHeld = newHeld; 096 if (oldHeld != newHeld) { 097 // notify listeners, if any 098 firePropertyChange("Held", oldHeld, newHeld); 099 } 100 101 } 102 103 /** 104 * Type-specific routine to handle output to the layout hardware. 105 * <p> 106 * Does not notify listeners of changes; that's done elsewhere. Should use 107 * the following variables to determine what to send: 108 * <ul> 109 * <li>mAppearance 110 * <li>mLit 111 * <li>mFlashOn 112 * </ul> 113 */ 114 abstract protected void updateOutput(); 115 116 /** 117 * Should a flashing signal be on (lit) now? 118 */ 119 protected boolean mFlashOn = true; 120 121 javax.swing.Timer timer = null; 122 /** 123 * On or off time of flashing signal. 124 * Public so that it can be overridden by 125 * scripting (before first use) 126 */ 127 public int delay = masterDelay; 128 129 public static int masterDelay = 750; 130 131 /** 132 * Start the timer that controls flashing. 133 */ 134 protected void startFlash() { 135 // note that we don't force mFlashOn to be true at the start 136 // of this; that way a flash in process isn't disturbed. 137 if (timer == null) { 138 timer = new javax.swing.Timer(delay, (java.awt.event.ActionEvent e) -> { 139 timeout(); 140 }); 141 timer.setInitialDelay(delay); 142 timer.setRepeats(true); 143 } 144 timer.start(); 145 } 146 147 private void timeout() { 148 mFlashOn = !mFlashOn; 149 150 updateOutput(); 151 } 152 153 /* 154 * Stop the timer that controls flashing. 155 * <p> 156 * This is only a resource-saver; the actual use of 157 * flashing happens elsewhere. 158 */ 159 protected void stopFlash() { 160 if (timer != null) { 161 timer.stop(); 162 } 163 mFlashOn = true; 164 } 165 166 final static private int[] VALID_STATES = new int[]{ 167 DARK, 168 RED, 169 YELLOW, 170 GREEN, 171 FLASHRED, 172 FLASHYELLOW, 173 FLASHGREEN, 174 }; // No int for Lunar 175 176 final static private String[] VALID_STATE_KEYS = new String[]{ 177 "SignalHeadStateDark", 178 "SignalHeadStateRed", 179 "SignalHeadStateYellow", 180 "SignalHeadStateGreen", 181 "SignalHeadStateFlashingRed", 182 "SignalHeadStateFlashingYellow", 183 "SignalHeadStateFlashingGreen", 184 }; // Lunar not included 185 186 /** 187 * {@inheritDoc} 188 */ 189 @Override 190 public int[] getValidStates() { 191 return Arrays.copyOf(VALID_STATES, VALID_STATES.length); 192 } 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 public String[] getValidStateKeys() { 199 return Arrays.copyOf(VALID_STATE_KEYS, VALID_STATE_KEYS.length); 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override 206 public String[] getValidStateNames() { 207 String[] stateNames = new String[VALID_STATE_KEYS.length]; 208 int i = 0; 209 for (String stateKey : VALID_STATE_KEYS) { 210 stateNames[i++] = Bundle.getMessage(stateKey); 211 } 212 return stateNames; 213 } 214 215 @Override 216 public boolean isTurnoutUsed(jmri.Turnout t) { 217 return false; 218 } 219 220 @Override 221 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 222 List<NamedBeanUsageReport> report = new ArrayList<>(); 223 if (bean != null && bean instanceof jmri.Turnout) { 224 var t = (jmri.Turnout) bean; 225 if (isTurnoutUsed(t)) { 226 report.add(new NamedBeanUsageReport("SignalHeadTurnout")); // NOI18N 227 } 228 } 229 return report; 230 } 231}