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 * with additions and edits by 016 * @author Timothy Jump Copyright (C) 2025 017 */ 018public class SerialThrottle extends AbstractThrottle { 019 020 /** 021 * Constructor. 022 * 023 * @param memo the connected SerialTrafficController 024 * @param address Loco ID 025 */ 026 public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) { 027 super(memo, 69); // supports 69 functions 028 tc = memo.getTrafficController(); 029 030 // cache settings. It would be better to read the 031 // actual state, but I don't know how to do this 032 synchronized(this) { 033 this.speedSetting = 0; 034 } 035 // Functions default to false 036 this.address = address; 037 this.isForward = true; 038 this.speedStepMode = SpeedStepMode.TMCC1_32; 039 } 040 041 private final DccLocoAddress address; 042 private final SerialTrafficController tc; 043 044 /** 045 * {@inheritDoc} 046 */ 047 @Override 048 public LocoAddress getLocoAddress() { 049 return address; 050 } 051 052 // Relating SERIAL_FUNCTION_CODES_TMCC1 to SpeedStepMode.TMCC1_32 and TMCC1_100; 053 // and SERIAL_FUNCTION_CODES_TMCC2 to SpeedStepMode.TMCC2_32 and TMCC2_200. 054 private long getFnValue(int number) { 055 if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_100) { 056 if (number < SERIAL_FUNCTION_CODES_TMCC1.length) { 057 return SERIAL_FUNCTION_CODES_TMCC1[number]; 058 } else { 059 return 0; 060 } 061 } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_32) { 062 if (number < SERIAL_FUNCTION_CODES_TMCC2_32.length) { 063 return SERIAL_FUNCTION_CODES_TMCC2_32[number]; 064 } else { 065 return 0; 066 } 067 } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_200) { 068 if (number < SERIAL_FUNCTION_CODES_TMCC2_200.length) { 069 return SERIAL_FUNCTION_CODES_TMCC2_200[number]; 070 } else { 071 return 0; 072 } 073 } 074 return 0; 075 } 076 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public void setFunction(int func, boolean value) { 083 updateFunction(func, value); 084 if (func>=0 && func < SERIAL_FUNCTION_CODES_TMCC1.length) { 085 if ( getFnValue(func) > 0xFFFF ) { 086 // TMCC 2 format 087 if (getFnValue(func) > 0xFFFFFF ) { 088 int first = (int)(getFnValue(func) >> 24); 089 int second = (int)(getFnValue(func) & 0xFFFFFF); 090 // doubles are only sent once, not repeating 091 sendOneWordOnce(first + address.getNumber() * 512); 092 sendOneWordOnce(second + address.getNumber() * 512); 093 } else { 094 // single message 095 sendFnToLayout((int)getFnValue(func) + address.getNumber() * 512, func); 096 } 097 } else { 098 // TMCC 1 format 099 sendFnToLayout((int)getFnValue(func) + address.getNumber() * 128, func); 100 } 101 } else { 102 super.setFunction(func, value); 103 } 104 } 105 106 // the argument is a long containing 3 bytes. 107 // The first byte is the message opcode 108 private void sendOneWordOnce(int word) { 109 SerialMessage m = new SerialMessage(word); 110 tc.sendSerialMessage(m, null); 111 } 112 113 // TMCC 1 Function Keys to trigger with TMCC1_32 and TMCC1_100 speed steps. 114 private final static long[] SERIAL_FUNCTION_CODES_TMCC1 = new long[] { 115 116 // TMCC1 Remote - Buttons 117 0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */ 118 119 // TMCC1 Remote - KeyPad Buttons 120 0x000011, 0x000012, 0x000013, /* Fn5-7 */ 121 0x000014, 0x000015, 0x000016, /* Fn8-10 */ 122 0x000017, 0x000018, 0x000019, /* Fn11-13 */ 123 0x000010, /* Fn14 */ 124 125 // TMCC1 Remote - Buttons 126 0x000009, 0x00001E, 0x000004, 0x000007, 0x000028, /* Fn15-19 */ 127 0x000029, 0x00002A, 0x00002B, 0x00001F, /* 20-23 */ 128 129 // TMCC1 RR Speed FnKeys 130 0x000064, // Fn24 ( 4) 131 0x00006A, // Fn25 (10) 132 0x00006E, // Fn26 (14) 133 0x000072, // Fn27 (18) 134 0x000078, // Fn28 (24) 135 0x00007F, // Fn29 (31) 136 137 // TMCC1 Aux FnKeys 138 0x00002E, // Fn30 139 0x00002E, // Fn31 140 0x00002E, // Fn32 141 0x00002E, // Fn33 142 0x00002E, // Fn34 143 0x00002E, // Fn35 144 145 // TMCC1 Unused FnKeys 146 0x00002E, // Fn36 147 0x00002E, // Fn37 148 0x00002E, // Fn38 149 0x00002E, // Fn39 150 0x00002E, // Fn40 151 0x00002E, // Fn41 152 0x00002E, // Fn42 153 0x00002E, // Fn43 154 0x00002E, // Fn44 155 0x00002E, // Fn45 156 0x00002E, // Fn46 157 0x00002E, // Fn47 158 0x00002E, // Fn48 159 0x00002E, // Fn49 160 0x00002E, // Fn50 161 0x00002E, // Fn51 162 0x00002E, // Fn52 163 0x00002E, // Fn53 164 0x00002E, // Fn54 165 0x00002E, // Fn55 166 0x00002E, // Fn56 167 0x00002E, // Fn57 168 0x00002E, // Fn58 169 0x00002E, // Fn59 170 0x00002E, // Fn60 171 0x00002E, // Fn61 172 0x00002E, // Fn62 173 0x00002E, // Fn63 174 0x00002E, // Fn64 175 0x00002E, // Fn65 176 0x00002E, // Fn66 177 0x00002E, // Fn67 178 0x00002E, // Fn68 179 }; 180 181 // Translate TMCC1 function numbers to line characters. 182 // If the upper byte is zero, it will be replaced by 0xF8 183 // and the address will be set in the low position. 184 // If the upper byte is non-zero, that value will be sent, 185 // and the address will be set in the upper (TMCC2) position. 186 // If six bytes are specified (with the upper one non-zero), 187 // this will be interpreted as two commands to be sequentially sent, 188 // with the upper bytes sent first. 189 190 // TMCC 2 Legacy Function Keys to trigger with TMCC2_32 speed steps. 191 private final static long[] SERIAL_FUNCTION_CODES_TMCC2_32 = new long[] { 192 193 // TMCC2_32 Remote - Buttons 194 0xF8010D, 0xF8011D, 0xF8011C, 0xF80105, 0xF80106, /* Fn0-4 */ 195 196 // TMCC2_32 Remote - Keypad Buttons 197 0xF80111, 0xF80112, 0xF80113, /* Fn5-7 */ 198 0xF80114, 0xF80115, 0xF80116, /* Fn8-10 */ 199 0xF80117, 0xF80118, 0xF80119, /* Fn11-13 */ 200 0xF80110, /* Fn14 */ 201 202 // TMCC2_32 Remote - Buttons 203 0xF80109, 0xF8011E, 0xF80104, 0xF80107, 0xF80128, /* Fn15-19 */ 204 0xF80129, 0xF8012A, 0xF8012B, 0xF8011F,/* 20-23 */ 205 206 // TMCC2_32 RR Speed FnKeys 207 0xF80164, // Fn24 208 0xF8016A, // Fn25 209 0xF8016E, // Fn26 210 0xF80172, // Fn27 211 0xF80178, // Fn28 212 0xF8017F, // Fn29 213 214 // TMCC2_32 Extended Lighting FnKeys 215 216 //0xF8017DFB01F2FB0189L, // Fn35 Set Cab Light Auto 217 218 219 // Extended Sound Effects FnKeys 220 //0xF801FBF801FCL, // Fn35 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up) 221 //0xF801FC, // Fn36 Start Up Sequence 2 (Immediate Start Up) 222 //0xF801FDF801FEL, // Fn37 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down) 223 //0xF801FE, // Fn38 Shut down Sequence 2 (Immediate Shut Down) 224 225 226 // TRMCC2_32 Aux FnKeys 227 0xF80108, // Fn30 228 0xF8010A, // Fn31 229 0xF8010B, // Fn32 230 0xF8010C, // Fn33 231 0xF8010E, // Fn34 232 0xF8010F, // Fn35 233 234 // TRMCC2_32 Unused FnKeys 235 0xF8012E, // Fn36 236 0xF8012E, // Fn37 237 0xF8012E, // Fn38 238 0xF8012E, // Fn39 239 0xF8012E, // Fn40 240 0xF8012E, // Fn41 241 0xF8012E, // Fn42 242 0xF8012E, // Fn43 243 0xF8012E, // Fn44 244 0xF8012E, // Fn45 245 0xF8012E, // Fn46 246 0xF8012E, // Fn47 247 0xF8012E, // Fn48 248 0xF8012E, // Fn49 249 0xF8012E, // Fn50 250 0xF8012E, // Fn51 251 0xF8012E, // Fn52 252 0xF8012E, // Fn53 253 0xF8012E, // Fn54 254 0xF8012E, // Fn55 255 0xF8012E, // Fn56 256 0xF8012E, // Fn57 257 0xF8012E, // Fn58 258 0xF8012E, // Fn59 259 0xF8012E, // Fn60 260 0xF8012E, // Fn61 261 0xF8012E, // Fn62 262 0xF8012E, // Fn63 263 }; 264 265 // TMCC 2 Legacy Function Keys to trigger with TMCC2_200 speed steps. 266 private final static long[] SERIAL_FUNCTION_CODES_TMCC2_200 = new long[] { 267 268 // TMCC2_200 Remote - Buttons 269 0xF8010D, 0xF8011D, 0xF8011C, 0xF80105, 0xF80106, /* Fn0-4 */ 270 271 // TMCC2_200 Remote - Keypad Buttons 272 0xF80111, 0xF80112, 0xF80113, /* Fn5-7 */ 273 0xF80114, 0xF80115, 0xF80116, /* Fn8-10 */ 274 0xF80117, 0xF80118, 0xF80119, /* Fn11-13 */ 275 0xF80110, /* Fn14 */ 276 277 // TMCC2_200 Remote - Buttons 278 0xF80109, 0xF8011E, 0xF80104, 0xF80107, 0xF80128, /* Fn15-19 */ 279 0xF80129, 0xF8012A, 0xF8012B, 0xF8011F,/* 20-23 */ 280 281 // TMCC2_200 RR Speed FnKeys 282 0xF8000A, // Fn24 283 0xF80028, // Fn25 284 0xF80046, // Fn26 285 0xF80064, // Fn27 286 0xF8008C, // Fn28 287 0xF800C7, // Fn29 288 289 // TMCC2_200 Extended Lighting FnKeys 290 291 //0xF8017DFB01F2FB0189L, // Fn35 Set Cab Light Auto 292 293 294 // Extended Sound Effects FnKeys 295 //0xF801FBF801FCL, // Fn35 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up) 296 //0xF801FC, // Fn36 Start Up Sequence 2 (Immediate Start Up) 297 //0xF801FDF801FEL, // Fn37 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down) 298 //0xF801FE, // Fn38 Shut down Sequence 2 (Immediate Shut Down) 299 300 301 // TMCC2_200 Aux FnKeys 302 0xF80108, // Fn30 303 0xF8010A, // Fn31 304 0xF8010B, // Fn32 305 0xF8010C, // Fn33 306 0xF8010E, // Fn34 307 0xF8010F, // Fn35 308 309 // TMCC2_200 Unused FnKeys 310 0xF8012E, // Fn36 311 0xF8012E, // Fn37 312 0xF8012E, // Fn38 313 0xF8012E, // Fn39 314 0xF8012E, // Fn40 315 0xF8012E, // Fn41 316 0xF8012E, // Fn42 317 0xF8012E, // Fn43 318 0xF8012E, // Fn44 319 0xF8012E, // Fn45 320 0xF8012E, // Fn46 321 0xF8012E, // Fn47 322 0xF8012E, // Fn48 323 0xF8012E, // Fn49 324 0xF8012E, // Fn50 325 0xF8012E, // Fn51 326 0xF8012E, // Fn52 327 0xF8012E, // Fn53 328 0xF8012E, // Fn54 329 0xF8012E, // Fn55 330 0xF8012E, // Fn56 331 0xF8012E, // Fn57 332 0xF8012E, // Fn58 333 0xF8012E, // Fn59 334 0xF8012E, // Fn60 335 0xF8012E, // Fn61 336 0xF8012E, // Fn62 337 0xF8012E, // Fn63 338 }; 339 340 /** 341 * Set the speed. 342 * 343 * @param speed Number from 0 to 1; less than zero is emergency stop 344 */ 345 @Override 346 public void setSpeedSetting(float speed) { 347 float oldSpeed; 348 synchronized(this) { 349 oldSpeed = this.speedSetting; 350 this.speedSetting = speed; 351 } 352 353 // send to layout option 200 speed steps 354 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 355 356 // TMCC2 Legacy 200 speed step mode 357 int value = (int) (199 * speed); // max value to send is 199 in 200 step mode 358 if (value > 199) { 359 // max possible speed 360 value = 199; 361 } 362 SerialMessage m = new SerialMessage(); 363 m.setOpCode(0xF8); 364 365 if (value < 0) { 366 // System HALT (immediate stop; ALL) 367 m.putAsWord(0xFF8B); 368 } else { 369 // normal speed setting 370 m.putAsWord(0x0000 + (address.getNumber() << 9) + value); 371 } 372 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 373 tc.sendSerialMessage(m, null); 374 tc.sendSerialMessage(m, null); 375 } 376 377 // send to layout option 100 speed steps 378 if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 379 380 /** 381 * TMCC1 ERR 100 speed step mode 382 * purpose is to increase resolution of 32 bits 383 * across 100 throttle 'clicks' by dividing value by 3 384 * and setting top speed at 32 385 */ 386 int value = (int) (99 * speed); // max value to send is 99 in 100 step mode 387 if (value > 93) { 388 // max possible speed step 389 value = 93; 390 } 391 SerialMessage m = new SerialMessage(); 392 m.setOpCode(0xFE); 393 394 if (value < 0) { 395 // System HALT (immediate stop; ALL) 396 m.putAsWord(0xFFFF); 397 } 398 if (value >= 0) { 399 // normal speed step setting 400 m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3); 401 } 402 403 // send to command station (send once for maximum efficiency; add extra sends if layout not responding with only one send) 404 tc.sendSerialMessage(m, null); 405 tc.sendSerialMessage(m, null); 406 } 407 408 // send to layout option TMCC2 32 speed steps 409 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 410 411 // TMCC2 Legacy 32 speed step mode 412 int value = (int) (32 * speed); 413 if (value > 31) { 414 // max possible speed 415 value = 31; 416 } 417 SerialMessage m = new SerialMessage(); 418 m.setOpCode(0xF8); 419 420 if (value < 0) { 421 // System HALT (immediate stop; ALL) 422 m.putAsWord(0xFF8B); 423 } else { 424 // normal speed setting 425 m.putAsWord(0x0160 + address.getNumber() * 512 + value); 426 } 427 428 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 429 tc.sendSerialMessage(m, null); 430 tc.sendSerialMessage(m, null); 431 } 432 433 // send to layout option TMCC1 32 speed steps 434 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) { 435 436 // TMCC1 32 speed step mode 437 int value = (int) (32 * speed); 438 if (value > 31) { 439 // max possible speed 440 value = 31; 441 } 442 SerialMessage m = new SerialMessage(); 443 m.setOpCode(0xFE); 444 445 if (value < 0) { 446 // System HALT (immediate stop; ALL) 447 m.putAsWord(0xFFFF); 448 } else { 449 // normal speed setting 450 m.putAsWord(0x0060 + address.getNumber() * 128 + value); 451 } 452 453 // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency) 454 tc.sendSerialMessage(m, null); 455 tc.sendSerialMessage(m, null); 456 } 457 458 synchronized(this) { 459 firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting); 460 } 461 record(speed); 462 } 463 464 /** 465 * {@inheritDoc} 466 */ 467 @Override 468 public void setIsForward(boolean forward) { 469 boolean old = isForward; 470 isForward = forward; 471 472 // notify layout 473 SerialMessage m = new SerialMessage(); 474 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100) { 475 m.setOpCode(0xFE); 476 if (forward) { 477 m.putAsWord(0x0000 + address.getNumber() * 128); 478 setSpeedSetting(0.0f); 479 } else { 480 m.putAsWord(0x0003 + address.getNumber() * 128); 481 setSpeedSetting(0.0f); 482 } 483 } 484 485 if (speedStepMode == jmri.SpeedStepMode.TMCC2_32 || speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 486 m.setOpCode(0xF8); 487 if (forward) { 488 m.putAsWord(0x0100 + address.getNumber() * 512); 489 setSpeedSetting(0.0f); 490 } else { 491 m.putAsWord(0x0103 + address.getNumber() * 512); 492 setSpeedSetting(0.0f); 493 } 494 } 495 496 tc.sendSerialMessage(m, null); 497 tc.sendSerialMessage(m, null); 498 // tc.sendSerialMessage(m, null); 499 // tc.sendSerialMessage(m, null); 500 501 firePropertyChange(ISFORWARD, old, isForward); 502 } 503 504 /** 505 * Send these messages to the layout and repeat 506 * while button is on. 507 * @param value Content of message to be sent in three bytes 508 * @param func The number of the function being addressed 509 */ 510 protected void sendFnToLayout(int value, int func) { 511 512 if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) { 513 if (func == 24) { 514 setSpeedSetting(0.055f); 515 } 516 if (func == 25) { 517 setSpeedSetting(0.205f); 518 } 519 if (func == 26) { 520 setSpeedSetting(0.355f); 521 } 522 if (func == 27) { 523 setSpeedSetting(0.505f); 524 } 525 if (func == 28) { 526 setSpeedSetting(0.705f); 527 } 528 if (func == 29) { 529 setSpeedSetting(1.0f); 530 } 531 } 532 533 if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100 || speedStepMode == jmri.SpeedStepMode.TMCC2_32) { 534 if (func == 24) { 535 setSpeedSetting(0.130f); 536 } 537 if (func == 25) { 538 setSpeedSetting(0.320f); 539 } 540 if (func == 26) { 541 setSpeedSetting(0.450f); 542 } 543 if (func == 27) { 544 setSpeedSetting(0.580f); 545 } 546 if (func == 28) { 547 setSpeedSetting(0.775f); 548 } 549 if (func == 29) { 550 setSpeedSetting(1.0f); 551 } 552 } 553 554 /** 555 * Commenting out these repeat send lines in case it is 556 * necessary to reinstate them after testing. These are 557 * holdovers from the original "repeat 4 times to make 558 * sure they're accepted" instructions. 559 */ 560 561 // tc.sendSerialMessage(new SerialMessage(value), null); 562 // tc.sendSerialMessage(new SerialMessage(value), null); 563 // tc.sendSerialMessage(new SerialMessage(value), null); 564 565 repeatFunctionSendWhileOn(value, func); // 4th send is here 566 } 567 568 static final int REPEAT_TIME = 150; 569 570 protected void repeatFunctionSendWhileOn(int value, int func) { 571 // Send again if function is still on and repeat in a short while 572 if (getFunction(func)) { 573 tc.sendSerialMessage(new SerialMessage(value), null); 574 jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> { 575 repeatFunctionSendWhileOn(value, func); 576 }, REPEAT_TIME); 577 } 578 } 579 580 /* 581 * Set the speed step value. 582 * <p> 583 * Only 32 steps is available 584 * 585 * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed 586 */ 587 @Override 588 public void setSpeedStepMode(jmri.SpeedStepMode mode) { 589 if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) { 590 super.setSpeedStepMode(mode); 591 } 592 } 593 594 /** 595 * {@inheritDoc} 596 */ 597 @Override 598 public void throttleDispose() { 599 finishRecord(); 600 } 601 602}