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.TMCC1_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 // Relating SERIAL_FUNCTION_CODES_TMCC1 to SpeedStepMode.TMCC1_32 and TMCC1_100; 051 // and SERIAL_FUNCTION_CODES_TMCC2 to SpeedStepMode.TMCC2_32 and TMCC2_200. 052 private long getFnValue(int number) { 053 if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_100) { 054 if (number < SERIAL_FUNCTION_CODES_TMCC1.length) { 055 return SERIAL_FUNCTION_CODES_TMCC1[number]; 056 } else { 057 return 0; 058 } 059 } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_200) { 060 if (number < SERIAL_FUNCTION_CODES_TMCC2.length) { 061 return SERIAL_FUNCTION_CODES_TMCC2[number]; 062 } else { 063 return 0; 064 } 065 } 066 return 0; 067 } 068 069 070 /** 071 * {@inheritDoc} 072 */ 073 @Override 074 public void setFunction(int func, boolean value) { 075 updateFunction(func, value); 076 if (func>=0 && func < SERIAL_FUNCTION_CODES_TMCC1.length) { 077 if ( getFnValue(func) > 0xFFFF ) { 078 // TMCC 2 format 079 if (getFnValue(func) > 0xFFFFFF ) { 080 int first = (int)(getFnValue(func) >> 24); 081 int second = (int)(getFnValue(func) & 0xFFFFFF); 082 // doubles are only sent once, not repeating 083 sendOneWordOnce(first + address.getNumber() * 512); 084 sendOneWordOnce(second + address.getNumber() * 512); 085 } else { 086 // single message 087 sendFnToLayout((int)getFnValue(func) + address.getNumber() * 512, func); 088 } 089 } else { 090 // TMCC 1 format 091 sendFnToLayout((int)getFnValue(func) + address.getNumber() * 128, func); 092 } 093 } else { 094 super.setFunction(func, value); 095 } 096 } 097 098 // the argument is a long containing 3 bytes. 099 // The first byte is the message opcode 100 private void sendOneWordOnce(int word) { 101 SerialMessage m = new SerialMessage(word); 102 tc.sendSerialMessage(m, null); 103 } 104 105 // TMCC 1 Function Keys to trigger with TMCC1_32 and TMCC1_100 speed steps. 106 private final static long[] SERIAL_FUNCTION_CODES_TMCC1 = new long[] { 107 0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */ 108 0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */ 109 0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */ 110 0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */ 111 0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */ 112 0x000029, 0x00002A, 0x00002B, 0x00001F, /* 25-28 */ 113 114 0xF901FF, // Fn29 115 0xF901FF, // Fn30 116 0xF901FF, // Fn31 117 0xF901FF, // Fn32 118 0xF901FF, // Fn33 119 0xF901FF, // Fn34 120 0xF901FF, // Fn35 121 0xF901FF, // Fn36 122 0xF901FF, // Fn37 123 0xF901FF, // Fn38 124 0xF901FF, // Fn39 125 0xF901FF, // Fn40 126 0xF901FF, // Fn41 127 0xF901FF, // Fn42 128 0xF901FF, // Fn43 129 0xF901FF, // Fn44 130 0xF901FF, // Fn45 131 0xF901FF, // Fn46 132 0xF901FF, // Fn47 133 0xF901FF, // Fn48 134 0xF901FF, // Fn49 135 0xF901FF, // Fn50 136 0xF901FF, // Fn51 137 0xF901FF, // Fn52 138 0xF901FF, // Fn53 139 0xF901FF, // Fn54 140 0xF901FF, // Fn55 141 0xF901FF, // Fn56 142 0xF901FF, // Fn57 143 0xF901FF, // Fn58 144 0xF901FF, // Fn59 145 0xF901FF, // Fn60 146 0xF901FF, // Fn61 147 0xF901FF, // Fn62 148 0xF901FF, // Fn63 149 0xF901FF, // Fn64 150 0xF901FF, // Fn65 151 0xF901FF, // Fn66 152 0xF901FF, // Fn67 153 0xF901FF, // Fn68 154 }; 155 156 // Translate TMCC1 function numbers to line characters. 157 // If the upper byte is zero, it will be replaced by 0xF8 158 // and the address will be set in the low position. 159 // If the upper byte is non-zero, that value will be sent, 160 // and the address will be set in the upper (TMCC2) position. 161 // If six bytes are specified (with the upper one non-zero), 162 // this will be interpreted as two commands to be sequentially sent, 163 // with the upper bytes sent first. 164 165 // TMCC 2 Legacy Function Keys to trigger with TMCC2_32 and TMCC2_200 speed steps. 166 private final static long[] SERIAL_FUNCTION_CODES_TMCC2 = new long[] { 167 0xF8010D, 0xF8011D, 0xF8011C, 0xF80105, 0xF80106, /* Fn0-4 */ 168 0xF80110, 0xF80111, 0xF80112, 0xF80113, 0xF80114, /* Fn5-9 */ 169 0xF80115, 0xF80116, 0xF80117, 0xF80118, 0xF80119, /* Fn10-14 */ 170 0xF80109, 0xF8011E, 0xF80100, 0xF80103, 0xF80101, /* Fn15-19 */ 171 0xF80104, 0xF80107, 0xF80147, 0xF80142, 0xF80128, /* Fn20-24 */ 172 0xF80129, 0xF8012A, 0xF8012B, 0xF8011F, /* 25-28 */ 173 174 0xF80108, // Fn29 175 0xF8010A, // Fn30 176 0xF8010B, // Fn31 177 0xF8010C, // Fn32 178 0xF8010E, // Fn33 179 0xF8010F, // Fn34 180 181 0xF801FBF801FCL, // Fn35 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up) 182 0xF801FC, // Fn36 Start Up Sequence 2 (Immediate Start Up) 183 0xF801FDF801FEL, // Fn37 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down) 184 0xF801FE, // Fn38 Shut down Sequence 2 (Immediate Shut Down) 185 186 0xF901FF, // Fn39 187 0xF901FF, // Fn40 188 0xF901FF, // Fn41 189 0xF901FF, // Fn42 190 0xF901FF, // Fn43 191 0xF901FF, // Fn44 192 0xF901FF, // Fn45 193 0xF901FF, // Fn46 194 0xF901FF, // Fn47 195 0xF901FF, // Fn48 196 0xF901FF, // Fn49 197 0xF901FF, // Fn50 198 0xF901FF, // Fn51 199 0xF901FF, // Fn52 200 0xF901FF, // Fn53 201 0xF901FF, // Fn54 202 0xF901FF, // Fn55 203 0xF901FF, // Fn56 204 0xF901FF, // Fn57 205 0xF901FF, // Fn58 206 0xF901FF, // Fn59 207 0xF901FF, // Fn60 208 0xF901FF, // Fn61 209 0xF901FF, // Fn62 210 0xF901FF, // Fn63 211 0xF901FF, // Fn64 212 0xF901FF, // Fn65 213 0xF901FF, // Fn66 214 0xF901FF, // Fn67 215 0xF901FF, // Fn68 216 }; 217 218 /** 219 * Set the speed. 220 * 221 * @param speed Number from 0 to 1; less than zero is emergency stop 222 */ 223 @Override 224 public void setSpeedSetting(float speed) { 225 float oldSpeed; 226 synchronized(this) { 227 oldSpeed = this.speedSetting; 228 this.speedSetting = speed; 229 } 230 231 // send to layout option 200 speed steps 232 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 233 234 // TMCC2 Legacy 200 speed step mode 235 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 236 if (value > 199) { 237 // max possible speed 238 value = 199; 239 } 240 SerialMessage m = new SerialMessage(); 241 m.setOpCode(0xF8); 242 243 if (value < 1) { 244 // immediate stop 245 m.putAsWord(0x0000 + (address.getNumber() << 9) + 0); 246 } else { 247 // normal speed setting 248 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 249 } 250 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 251 tc.sendSerialMessage(m, null); 252 tc.sendSerialMessage(m, null); 253 } 254 255 // send to layout option 100 speed steps 256 if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 257 258 /** 259 * TMCC1 ERR 100 speed step mode 260 * purpose is to increase resolution of 32 bits 261 * across 100 throttle 'clicks' by dividing value by 3 262 * and setting top speed at 32 263 */ 264 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 265 if (value > 93) { 266 // max possible speed step 267 value = 93; 268 } 269 SerialMessage m = new SerialMessage(); 270 m.setOpCode(0xFE); 271 272 if (value < 1) { 273 // immediate stop 274 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 275 } 276 if (value > 0) { 277 // normal speed step setting 278 m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3); 279 } 280 281 // send to command station (send once for maximum efficiency; add extra sends if layout not responding with only one send) 282 tc.sendSerialMessage(m, null); 283 } 284 285 // send to layout option TMCC2 32 speed steps 286 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 287 288 // TMCC2 Legacy 32 speed step mode 289 int value = (int) (32 * speed); 290 if (value > 31) { 291 // max possible speed 292 value = 31; 293 } 294 SerialMessage m = new SerialMessage(); 295 m.setOpCode(0xF8); 296 297 if (value < 1) { 298 // immediate stop 299 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 300 } else { 301 // normal speed setting 302 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 303 } 304 305 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 306 tc.sendSerialMessage(m, null); 307 tc.sendSerialMessage(m, null); 308 } 309 310 // send to layout option TMCC1 32 speed steps 311 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) { 312 313 // TMCC1 32 speed step mode 314 int value = (int) (32 * speed); 315 if (value > 31) { 316 // max possible speed 317 value = 31; 318 } 319 SerialMessage m = new SerialMessage(); 320 m.setOpCode(0xFE); 321 322 if (value < 1) { 323 // immediate stop 324 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 325 } else { 326 // normal speed setting 327 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 328 } 329 330 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 331 tc.sendSerialMessage(m, null); 332 tc.sendSerialMessage(m, null); 333 } 334 335 synchronized(this) { 336 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 337 } 338 record(speed); 339 } 340 341 /** 342 * {@inheritDoc} 343 */ 344 @Override 345 public void setIsForward(boolean forward) { 346 boolean old = isForward; 347 isForward = forward; 348 349 // notify layout 350 SerialMessage m = new SerialMessage(); 351 if (forward) { 352 m.putAsWord(0x0000 + address.getNumber() * 128); 353 } else { 354 m.putAsWord(0x0003 + address.getNumber() * 128); 355 } 356 tc.sendSerialMessage(m, null); 357 tc.sendSerialMessage(m, null); 358 tc.sendSerialMessage(m, null); 359 tc.sendSerialMessage(m, null); 360 firePropertyChange(ISFORWARD, old, isForward); 361 } 362 363 /** 364 * Send these messages to the layout and repeat 365 * while button is on. 366 * @param value Content of message to be sent in three bytes 367 * @param func The number of the function being addressed 368 */ 369 protected void sendFnToLayout(int value, int func) { 370 /** 371 * Commenting out these repeat send lines in case it is 372 * necessary to reinstate them after testing. These are 373 * holdovers from the original "repeat 4 times to make 374 * sure they're accepted" instructions. 375 */ 376 // tc.sendSerialMessage(new SerialMessage(value), null); 377 // tc.sendSerialMessage(new SerialMessage(value), null); 378 // tc.sendSerialMessage(new SerialMessage(value), null); 379 380 repeatFunctionSendWhileOn(value, func); // 4th send is here 381 } 382 383 static final int REPEAT_TIME = 150; 384 385 protected void repeatFunctionSendWhileOn(int value, int func) { 386 // Send again if function is still on and repeat in a short while 387 if (getFunction(func)) { 388 tc.sendSerialMessage(new SerialMessage(value), null); 389 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 390 repeatFunctionSendWhileOn(value, func); 391 }, REPEAT_TIME); 392 } 393 } 394 395 /* 396 * Set the speed step value. 397 * <p> 398 * Only 32 steps is available 399 * 400 * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed 401 */ 402 @Override 403 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 404 if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) { 405 super.setSpeedStepMode(mode); 406 } 407 } 408 409 /** 410 * {@inheritDoc} 411 */ 412 @Override 413 public void throttleDispose() { 414 finishRecord(); 415 } 416 417}