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 0xF8012E, // Fn29 115 0xF8012E, // Fn30 116 0xF8012E, // Fn31 117 0xF8012E, // Fn32 118 0xF8012E, // Fn33 119 0xF8012E, // Fn34 120 0xF8012E, // Fn35 121 0xF8012E, // Fn36 122 0xF8012E, // Fn37 123 0xF8012E, // Fn38 124 0xF8012E, // Fn39 125 0xF8012E, // Fn40 126 0xF8012E, // Fn41 127 0xF8012E, // Fn42 128 0xF8012E, // Fn43 129 0xF8012E, // Fn44 130 0xF8012E, // Fn45 131 0xF8012E, // Fn46 132 0xF8012E, // Fn47 133 0xF8012E, // Fn48 134 0xF8012E, // Fn49 135 0xF8012E, // Fn50 136 0xF8012E, // Fn51 137 0xF8012E, // Fn52 138 0xF8012E, // Fn53 139 0xF8012E, // Fn54 140 0xF8012E, // Fn55 141 0xF8012E, // Fn56 142 0xF8012E, // Fn57 143 0xF8012E, // Fn58 144 0xF8012E, // Fn59 145 0xF8012E, // Fn60 146 0xF8012E, // Fn61 147 0xF8012E, // Fn62 148 0xF8012E, // Fn63 149 0xF8012E, // Fn64 150 0xF8012E, // Fn65 151 0xF8012E, // Fn66 152 0xF8012E, // Fn67 153 0xF8012E, // 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 0xF8012E, // Fn35 187 0xF8012E, // Fn36 188 0xF8012E, // Fn37 189 0xF8012E, // Fn38 190 0xF8012E, // Fn39 191 0xF8012E, // Fn40 192 0xF8012E, // Fn41 193 0xF8012E, // Fn42 194 0xF8012E, // Fn43 195 0xF8012E, // Fn44 196 0xF8012E, // Fn45 197 0xF8012E, // Fn46 198 0xF8012E, // Fn47 199 0xF8012E, // Fn48 200 0xF8012E, // Fn49 201 0xF8012E, // Fn50 202 0xF8012E, // Fn51 203 0xF8012E, // Fn52 204 0xF8012E, // Fn53 205 0xF8012E, // Fn54 206 0xF8012E, // Fn55 207 0xF8012E, // Fn56 208 0xF8012E, // Fn57 209 0xF8012E, // Fn58 210 0xF8012E, // Fn59 211 0xF8012E, // Fn60 212 0xF8012E, // Fn61 213 0xF8012E, // Fn62 214 0xF8012E, // Fn63 215 0xF8012E, // Fn64 216 0xF8012E, // Fn65 217 0xF8012E, // Fn66 218 0xF8012E, // Fn67 219 0xF8012E, // Fn68 220 }; 221 222 /** 223 * Set the speed. 224 * 225 * @param speed Number from 0 to 1; less than zero is emergency stop 226 */ 227 @Override 228 public void setSpeedSetting(float speed) { 229 float oldSpeed; 230 synchronized(this) { 231 oldSpeed = this.speedSetting; 232 this.speedSetting = speed; 233 } 234 235 // send to layout option 200 speed steps 236 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 237 238 // TMCC2 Legacy 200 speed step mode 239 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 240 if (value > 199) { 241 // max possible speed 242 value = 199; 243 } 244 SerialMessage m = new SerialMessage(); 245 m.setOpCode(0xF8); 246 247 if (value < 1) { 248 // immediate stop 249 m.putAsWord(0x0000 + (address.getNumber() << 9) + 0); 250 } else { 251 // normal speed setting 252 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 253 } 254 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 255 tc.sendSerialMessage(m, null); 256 tc.sendSerialMessage(m, null); 257 } 258 259 // send to layout option 100 speed steps 260 if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 261 262 /** 263 * TMCC1 ERR 100 speed step mode 264 * purpose is to increase resolution of 32 bits 265 * across 100 throttle 'clicks' by dividing value by 3 266 * and setting top speed at 32 267 */ 268 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 269 if (value > 93) { 270 // max possible speed step 271 value = 93; 272 } 273 SerialMessage m = new SerialMessage(); 274 m.setOpCode(0xFE); 275 276 if (value < 1) { 277 // immediate stop 278 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 279 } 280 if (value > 0) { 281 // normal speed step setting 282 m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3); 283 } 284 285 // send to command station (send once for maximum efficiency; add extra sends if layout not responding with only one send) 286 tc.sendSerialMessage(m, null); 287 } 288 289 // send to layout option TMCC2 32 speed steps 290 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 291 292 // TMCC2 Legacy 32 speed step mode 293 int value = (int) (32 * speed); 294 if (value > 31) { 295 // max possible speed 296 value = 31; 297 } 298 SerialMessage m = new SerialMessage(); 299 m.setOpCode(0xF8); 300 301 if (value < 1) { 302 // immediate stop 303 m.putAsWord(0x0160 + address.getNumber() * 512 + 0); 304 } else { 305 // normal speed setting 306 m.putAsWord(0x0160 + address.getNumber() * 512 + value); 307 } 308 309 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 310 tc.sendSerialMessage(m, null); 311 tc.sendSerialMessage(m, null); 312 } 313 314 // send to layout option TMCC1 32 speed steps 315 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) { 316 317 // TMCC1 32 speed step mode 318 int value = (int) (32 * speed); 319 if (value > 31) { 320 // max possible speed 321 value = 31; 322 } 323 SerialMessage m = new SerialMessage(); 324 m.setOpCode(0xFE); 325 326 if (value < 1) { 327 // immediate stop 328 m.putAsWord(0x0060 + address.getNumber() * 128 + 0); 329 } else { 330 // normal speed setting 331 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 332 } 333 334 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 335 tc.sendSerialMessage(m, null); 336 tc.sendSerialMessage(m, null); 337 } 338 339 synchronized(this) { 340 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 341 } 342 record(speed); 343 } 344 345 /** 346 * {@inheritDoc} 347 */ 348 @Override 349 public void setIsForward(boolean forward) { 350 boolean old = isForward; 351 isForward = forward; 352 353 // notify layout 354 SerialMessage m = new SerialMessage(); 355 if (forward) { 356 m.putAsWord(0x0000 + address.getNumber() * 128); 357 } else { 358 m.putAsWord(0x0003 + address.getNumber() * 128); 359 } 360 tc.sendSerialMessage(m, null); 361 tc.sendSerialMessage(m, null); 362 tc.sendSerialMessage(m, null); 363 tc.sendSerialMessage(m, null); 364 firePropertyChange(ISFORWARD, old, isForward); 365 } 366 367 /** 368 * Send these messages to the layout and repeat 369 * while button is on. 370 * @param value Content of message to be sent in three bytes 371 * @param func The number of the function being addressed 372 */ 373 protected void sendFnToLayout(int value, int func) { 374 /** 375 * Commenting out these repeat send lines in case it is 376 * necessary to reinstate them after testing. These are 377 * holdovers from the original "repeat 4 times to make 378 * sure they're accepted" instructions. 379 */ 380 // tc.sendSerialMessage(new SerialMessage(value), null); 381 // tc.sendSerialMessage(new SerialMessage(value), null); 382 // tc.sendSerialMessage(new SerialMessage(value), null); 383 384 repeatFunctionSendWhileOn(value, func); // 4th send is here 385 } 386 387 static final int REPEAT_TIME = 150; 388 389 protected void repeatFunctionSendWhileOn(int value, int func) { 390 // Send again if function is still on and repeat in a short while 391 if (getFunction(func)) { 392 tc.sendSerialMessage(new SerialMessage(value), null); 393 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 394 repeatFunctionSendWhileOn(value, func); 395 }, REPEAT_TIME); 396 } 397 } 398 399 /* 400 * Set the speed step value. 401 * <p> 402 * Only 32 steps is available 403 * 404 * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed 405 */ 406 @Override 407 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 408 if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) { 409 super.setSpeedStepMode(mode); 410 } 411 } 412 413 /** 414 * {@inheritDoc} 415 */ 416 @Override 417 public void throttleDispose() { 418 finishRecord(); 419 } 420 421}