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