001package jmri.jmrix.tmcc; 002 003import jmri.DccLocoAddress; 004import jmri.LocoAddress; 005import jmri.SpeedStepMode; 006import jmri.jmrix.AbstractThrottle; 007 008/** 009 * An implementation of DccThrottle. 010 * <p> 011 * Addresses of 99 and below are considered short addresses, and over 100 are 012 * considered long addresses. 013 * 014 * @author Bob Jacobsen Copyright (C) 2001, 2006 015 */ 016public class SerialThrottle extends AbstractThrottle { 017 018 /** 019 * Constructor. 020 * 021 * @param memo the connected SerialTrafficController 022 * @param address Loco ID 023 */ 024 public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) { 025 super(memo, 69); // supports 69 functions 026 tc = memo.getTrafficController(); 027 028 // cache settings. It would be better to read the 029 // actual state, but I don't know how to do this 030 synchronized(this) { 031 this.speedSetting = 0; 032 } 033 // Functions default to false 034 this.address = address; 035 this.isForward = true; 036 this.speedStepMode = SpeedStepMode.TMCC_32; 037 } 038 039 private final DccLocoAddress address; 040 private final SerialTrafficController tc; 041 042 /** 043 * {@inheritDoc} 044 */ 045 @Override 046 public LocoAddress getLocoAddress() { 047 return address; 048 } 049 050 /** 051 * {@inheritDoc} 052 */ 053 @Override 054 public void setFunction(int func, boolean value) { 055 updateFunction(func, value); 056 if (func>=0 && func < SERIAL_FUNCTION_CODES.length) { 057 if ( SERIAL_FUNCTION_CODES[func] > 0xFFFF ) { 058 // TMCC 2 format 059 if (SERIAL_FUNCTION_CODES[func] > 0xFFFFFF ) { 060 int first = (int)(SERIAL_FUNCTION_CODES[func] >> 24); 061 int second = (int)(SERIAL_FUNCTION_CODES[func] & 0xFFFFFF); 062 // doubles are only sent once, not repeating 063 sendOneWordOnce(first + address.getNumber() * 512); 064 sendOneWordOnce(second + address.getNumber() * 512); 065 } else { 066 // single message 067 sendFnToLayout((int)SERIAL_FUNCTION_CODES[func] + address.getNumber() * 512, func); 068 } 069 } else { 070 // TMCC 1 format 071 sendFnToLayout((int)SERIAL_FUNCTION_CODES[func] + address.getNumber() * 128, func); 072 } 073 } 074 else { 075 super.setFunction(func, value); 076 } 077 } 078 079 // the argument is a long containing 3 bytes. 080 // The first byte is the message opcode 081 private void sendOneWordOnce(int word) { 082 SerialMessage m = new SerialMessage(word); 083 tc.sendSerialMessage(m, null); 084 } 085 086 // Translate function number to line characters. 087 // If the upper byte is zero, it will be replaces by 0xF8 088 // and the address will be set in the low position. 089 // If the upper byte is non-zero, that value will be sent, 090 // and the address will be set in the upper (TMCC2) position. 091 // If six bytes are specified (with the upper one non-zero), 092 // this will be interpreted as two commands to be sequentially sent, 093 // with the upper bytes sent first. 094 private final static long[] SERIAL_FUNCTION_CODES = new long[] { 095 0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */ 096 0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */ 097 0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */ 098 0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */ 099 0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */ 100 0x000029, 0x00002A, 0x00002B, /* 25-27 */ 101 // start of TMCC 2 functions 102 0xF801FBF801FCL, // Fn28 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up) 103 0xF801FC, // Fn29 Start Up Sequence 2 (Immediate Start Up) 104 0xF801FDF801FEL, // Fn30 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down) 105 0xF801FE, // Fn31 Shut down Sequence 2 (Immediate Shut Down) 106 0xF90000, // Fn32 107 0xF90000, // Fn33 108 0xF90000, // Fn34 109 0xF90000, // Fn35 110 0xF90000, // Fn36 111 0xF90000, // Fn37 112 0xF90000, // Fn38 113 0xF90000, // Fn39 114 0xF90000, // Fn40 115 0xF90000, // Fn41 116 0xF90000, // Fn42 117 0xF90000, // Fn43 118 0xF90000, // Fn44 119 0xF90000, // Fn45 120 0xF90000, // Fn46 121 0xF90000, // Fn47 122 0xF90000, // Fn48 123 0xF90000, // Fn49 124 0xF90000, // Fn50 125 0xF90000, // Fn51 126 0xF90000, // Fn52 127 0xF90000, // Fn53 128 0xF90000, // Fn54 129 0xF90000, // Fn55 130 0xF90000, // Fn56 131 0xF90000, // Fn57 132 0xF90000, // Fn58 133 0xF90000, // Fn59 134 0xF90000, // Fn60 135 0xF90000, // Fn61 136 0xF90000, // Fn62 137 0xF90000, // Fn63 138 0xF90000, // Fn64 139 0xF90000, // Fn65 140 0xF90000, // Fn66 141 0xF90000, // Fn67 142 }; 143 144 /** 145 * Set the speed. 146 * 147 * @param speed Number from 0 to 1; less than zero is emergency stop 148 */ 149 @Override 150 public void setSpeedSetting(float speed) { 151 float oldSpeed; 152 synchronized(this) { 153 oldSpeed = this.speedSetting; 154 this.speedSetting = speed; 155 } 156 157 // send to layout option 200 speed steps 158 if (speedStepMode == jmri.SpeedStepMode.TMCC_200) { 159 160 // TMCC2 Legacy 200 step mode 161 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 162 if (value > 199) { 163 value = 199; // max possible speed 164 } 165 SerialMessage m = new SerialMessage(); 166 m.setOpCode(0xF8); 167 168 if (value < 0) { 169 // immediate stop 170 m.putAsWord(0x0000 + (address.getNumber() << 9) + 0); 171 } else { 172 // normal speed setting 173 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 174 } 175 // only send twice to advanced command station 176 tc.sendSerialMessage(m, null); 177 tc.sendSerialMessage(m, null); 178 179 // send to layout option 100 speed steps 180 } else if (speedStepMode == jmri.SpeedStepMode.TMCC_100) { 181 182 // TMCC1 ERR 100 speed step mode 183 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 184 if (value > 99) { 185 value = 99; // max possible speed 186 } 187 SerialMessage m = new SerialMessage(); 188 m.setOpCode(0xFE); 189 190 if (value < 0) { 191 // immediate stop 192 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 193 } else { 194 // normal speed setting 195 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 196 } 197 // only send twice to advanced command station 198 tc.sendSerialMessage(m, null); 199 tc.sendSerialMessage(m, null); 200 201 // Send to layout 32 speed steps 202 } else { 203 204 // assume TMCC 32 step mode 205 int value = (int) (32 * speed); 206 if (value > 31) { 207 value = 31; // max possible speed 208 } 209 SerialMessage m = new SerialMessage(); 210 211 if (value < 0) { 212 // immediate stop 213 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 214 } else { 215 // normal speed setting 216 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 217 } 218 219 tc.sendSerialMessage(m, null); 220 tc.sendSerialMessage(m, null); 221 tc.sendSerialMessage(m, null); 222 tc.sendSerialMessage(m, null); 223 } 224 225 synchronized(this) { 226 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 227 } 228 record(speed); 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override 235 public void setIsForward(boolean forward) { 236 boolean old = isForward; 237 isForward = forward; 238 239 // notify layout 240 SerialMessage m = new SerialMessage(); 241 if (forward) { 242 m.putAsWord(0x0000 + address.getNumber() * 128); 243 } else { 244 m.putAsWord(0x0003 + address.getNumber() * 128); 245 } 246 tc.sendSerialMessage(m, null); 247 tc.sendSerialMessage(m, null); 248 tc.sendSerialMessage(m, null); 249 tc.sendSerialMessage(m, null); 250 firePropertyChange(ISFORWARD, old, isForward); 251 } 252 253 /** 254 * Send these messages to the layout and repeat 255 * while button is on. 256 * @param value Content of message to be sent in three bytes 257 * @param func The number of the function being addressed 258 */ 259 protected void sendFnToLayout(int value, int func) { 260 tc.sendSerialMessage(new SerialMessage(value), null); 261 262 /** 263 * Commenting out these repeat send lines in case it is 264 * necessary to reinstate them after testing. These are 265 * holdovers from the original "repeat 4 times to make 266 * sure they're accepted" instructions. 267 */ 268 // tc.sendSerialMessage(new SerialMessage(value), null); 269 // tc.sendSerialMessage(new SerialMessage(value), null); 270 271 repeatFunctionSendWhileOn(value, func); // 4th send is here 272 } 273 274 static final int REPEAT_TIME = 150; 275 276 protected void repeatFunctionSendWhileOn(int value, int func) { 277 // Send again if function is still on and repeat in a short while 278 if (getFunction(func)) { 279 tc.sendSerialMessage(new SerialMessage(value), null); 280 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 281 repeatFunctionSendWhileOn(value, func); 282 }, REPEAT_TIME); 283 } 284 } 285 286 /* 287 * Set the speed step value. 288 * <p> 289 * Only 32 steps is available 290 * 291 * @param mode only TMCC 32, TMCC 100 and TMCC 200 are allowed 292 */ 293 @Override 294 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 295 if (mode == jmri.SpeedStepMode.TMCC_32 || mode == jmri.SpeedStepMode.TMCC_100 || mode == jmri.SpeedStepMode.TMCC_200) { 296 super.setSpeedStepMode(mode); 297 } 298 } 299 300 /** 301 * {@inheritDoc} 302 */ 303 @Override 304 public void throttleDispose() { 305 finishRecord(); 306 } 307 308}