001package jmri.implementation; 002 003import jmri.InstanceManager; 004import jmri.NamedBeanHandle; 005import jmri.Turnout; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Extend jmri.SignalHead for signals implemented by an SE8C. 011 * <p> 012 * This implementation writes out to the physical signal when it's commanded to 013 * change appearance, and updates its internal state when it hears commands from 014 * other places. 015 * <p> 016 * To get a complete set of aspects, we assume that the SE8C board has been 017 * configured such that the 4th aspect is "dark". We then do flashing aspects by 018 * commanding the lit appearance to change. 019 * <p> 020 * We can't assume any form of numbering for Turnouts to address the digits, so 021 * we take two turnout names as arguments. As a convenience, we manage the user 022 * names if they're not already set. 023 * <p> 024 * Only the DARK, RED, GREEN and YELLOW appearances will be properly tracked 025 * when they occur on the LocoNet. The FLASHING aspects won't be, nor will the 026 * Held or Lit states. 027 * <p> 028 * The algorithms in this class are a collaborative effort of Digitrax, Inc and 029 * Bob Jacobsen. 030 * 031 * @author Bob Jacobsen Copyright (C) 2002, 2010, 2014 032 */ 033public class SE8cSignalHead extends DefaultSignalHead { 034 035 /** 036 * Primary ctor. 037 * 038 * @param lowTO lower-numbered Turnout reference 039 * @param highTO higher-numbered Turnout reference 040 * @param userName user name for mast 041 */ 042 public SE8cSignalHead(NamedBeanHandle<Turnout> lowTO, 043 NamedBeanHandle<Turnout> highTO, 044 String userName) { 045 // create systemname 046 super(makeSystemName(lowTO, highTO), userName); 047 this.lowTurnout = lowTO; 048 this.highTurnout = highTO; 049 init(); 050 } 051 052 /** 053 * Primary ctor without user name. 054 * 055 * @param lowTO lower-numbered Turnout reference 056 * @param highTO higher-numbered Turnout reference 057 */ 058 public SE8cSignalHead(NamedBeanHandle<Turnout> lowTO, 059 NamedBeanHandle<Turnout> highTO) { 060 // create systemname 061 super(makeSystemName(lowTO, highTO)); 062 this.lowTurnout = lowTO; 063 this.highTurnout = highTO; 064 init(); 065 } 066 067 /** 068 * Ctor specifying system name and user name. 069 * 070 * @param sname system name for mast 071 * @param lowTO lower-numbered Turnout reference 072 * @param highTO higher-numbered Turnout reference 073 * @param userName user name for mast 074 */ 075 public SE8cSignalHead(String sname, NamedBeanHandle<Turnout> lowTO, 076 NamedBeanHandle<Turnout> highTO, 077 String userName) { 078 // create systemname 079 super(sname, userName); 080 this.lowTurnout = lowTO; 081 this.highTurnout = highTO; 082 init(); 083 } 084 085 /** 086 * Ctor specifying system name. 087 * 088 * @param sname system name for mast 089 * @param lowTO lower-numbered Turnout reference 090 * @param highTO higher-numbered Turnout reference 091 */ 092 public SE8cSignalHead(String sname, NamedBeanHandle<Turnout> lowTO, 093 NamedBeanHandle<Turnout> highTO) { 094 // create systemname 095 super(sname); 096 this.lowTurnout = lowTO; 097 this.highTurnout = highTO; 098 init(); 099 } 100 101 /** 102 * Compatibility ctor. 103 * 104 * @param pNumber number (address) of low turnout 105 * @param userName name to use for this signal head 106 */ 107 public SE8cSignalHead(int pNumber, String userName) { 108 super("LH" + pNumber, userName); 109 this.lowTurnout = makeHandle(pNumber); 110 this.highTurnout = makeHandle(pNumber + 1); 111 init(); 112 } 113 114 /** 115 * Implement convention for making a system name. 116 * <p> 117 * Must pass arguments, as it is used before object is complete. 118 * 119 * @param lowTO lower-numbered Turnout reference 120 * @param highTO higher-numbered Turnout reference 121 * @return system name with fixed elements, i.e. IH:SE8c:to1\to2 122 */ 123 static String makeSystemName(NamedBeanHandle<Turnout> lowTO, 124 NamedBeanHandle<Turnout> highTO) { 125 return ("IH:SE8c:\"" + lowTO.getName() + "\";\"" + highTO.getName() + "\""); 126 } 127 128 /** 129 * Create a handle from a raw number. 130 * <p> 131 * Static, so can be referenced before ctor complete. 132 * 133 * @param i index number (address) of a turnout on the signal head 134 * @return NamedBeanHandle<Turnout> object to use as output for head 135 * @throws IllegalArgumentException when creation from i fails 136 */ 137 static NamedBeanHandle<Turnout> makeHandle(int i) throws IllegalArgumentException { 138 String number = "" + i; 139 return jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle( 140 number, 141 InstanceManager.turnoutManagerInstance().provideTurnout(number) 142 ); 143 } 144 145 /** 146 * Compatibility ctor 147 * 148 * @param pNumber number (address) of low turnout 149 */ 150 public SE8cSignalHead(int pNumber) { 151 super("LH" + pNumber); 152 this.lowTurnout = makeHandle(pNumber); 153 this.highTurnout = makeHandle(pNumber + 1); 154 init(); 155 } 156 157 NamedBeanHandle<Turnout> lowTurnout; 158 NamedBeanHandle<Turnout> highTurnout; 159 160 void init() { 161 // basic operation, nothing but ON messages needed 162 lowTurnout.getBean().setBinaryOutput(true); 163 highTurnout.getBean().setBinaryOutput(true); 164 165 // ensure default appearance 166 mAppearance = DARK; // start turned off 167 updateOutput(); 168 } 169 170 /** 171 * Type-specific routine to handle output to the layout hardware. 172 * Implemented to handle a request to change state by sending a LocoNet 173 * command. 174 * <p> 175 * Does not notify listeners of changes; that's done elsewhere. Should 176 * consider the following variables to determine what to send: 177 * <ul> 178 * <li>mAppearance 179 * <li>mLit 180 * <li>mFlashOn 181 * </ul> 182 */ 183 @Override 184 protected void updateOutput() { 185 if (!mLit) { 186 highTurnout.getBean().setCommandedState(Turnout.CLOSED); 187 } else if (!mFlashOn 188 && ((mAppearance == FLASHGREEN) 189 || (mAppearance == FLASHYELLOW) 190 || (mAppearance == FLASHRED))) { 191 // flash says to make output dark; 192 // flashing-but-lit is handled below 193 highTurnout.getBean().setCommandedState(Turnout.CLOSED); 194 } else { 195 // which of the four states? 196 switch (mAppearance) { 197 case FLASHRED: 198 case RED: 199 lowTurnout.getBean().setCommandedState(Turnout.THROWN); 200 break; 201 case FLASHYELLOW: 202 case YELLOW: 203 highTurnout.getBean().setCommandedState(Turnout.THROWN); 204 break; 205 case FLASHGREEN: 206 case GREEN: 207 lowTurnout.getBean().setCommandedState(Turnout.CLOSED); 208 break; 209 case DARK: 210 highTurnout.getBean().setCommandedState(Turnout.CLOSED); 211 break; 212 default: 213 log.error("Invalid state request: {}", mAppearance); 214 } 215 } 216 } 217 218 public NamedBeanHandle<Turnout> getLow() { 219 return lowTurnout; 220 } 221 222 public NamedBeanHandle<Turnout> getHigh() { 223 return highTurnout; 224 } 225 226 @Override 227 public boolean isTurnoutUsed(Turnout t) { 228 return (getLow() != null && t.equals(getLow().getBean())) 229 || (getHigh() != null && t.equals(getHigh().getBean())); 230 } 231 232 private final static Logger log = LoggerFactory.getLogger(SE8cSignalHead.class); 233}