001package jmri.jmrix.bachrus.speedmatcher; 002 003import java.awt.event.ActionListener; 004 005import javax.swing.JLabel; 006import javax.swing.JButton; 007import javax.swing.Timer; 008 009import jmri.*; 010 011/** 012 * Abstract class defining the basic operations of a speed matcher. All speed 013 * matcher implementations must extend this class. 014 * 015 * @author Todd Wegter Copyright (C) 2024 016 */ 017public abstract class SpeedMatcher implements ThrottleListener, ProgListener { 018 019 //<editor-fold defaultstate="collapsed" desc="Constants"> 020 protected final int INITIAL_MOMENTUM = 0; 021 protected final int REVERSE_TRIM_MAX = 255; 022 protected final int REVERSE_TRIM_MIN = 1; 023 024 protected final float ALLOWED_SPEED_MATCH_ERROR = 0.75f; 025 //</editor-fold> 026 027 //<editor-fold defaultstate="collapsed" desc="Enums"> 028 public enum SpeedTableStep { 029 STEP1(1) { 030 @Override 031 public SpeedTableStep getPrevious() { 032 return null; 033 } 034 035 @Override 036 public SpeedTableStep getNext() { 037 return STEP2; 038 } 039 }, 040 STEP2(2) { 041 @Override 042 public SpeedTableStep getPrevious() { 043 return STEP1; 044 } 045 046 @Override 047 public SpeedTableStep getNext() { 048 return STEP3; 049 } 050 }, 051 STEP3(3) { 052 @Override 053 public SpeedTableStep getPrevious() { 054 return STEP2; 055 } 056 057 @Override 058 public SpeedTableStep getNext() { 059 return STEP4; 060 } 061 }, 062 STEP4(4) { 063 @Override 064 public SpeedTableStep getPrevious() { 065 return STEP3; 066 } 067 068 @Override 069 public SpeedTableStep getNext() { 070 return STEP5; 071 } 072 }, 073 STEP5(5) { 074 @Override 075 public SpeedTableStep getPrevious() { 076 return STEP4; 077 } 078 079 @Override 080 public SpeedTableStep getNext() { 081 return STEP6; 082 } 083 }, 084 STEP6(6) { 085 @Override 086 public SpeedTableStep getPrevious() { 087 return STEP5; 088 } 089 090 @Override 091 public SpeedTableStep getNext() { 092 return STEP7; 093 } 094 }, 095 STEP7(7) { 096 @Override 097 public SpeedTableStep getPrevious() { 098 return STEP6; 099 } 100 101 @Override 102 public SpeedTableStep getNext() { 103 return STEP8; 104 } 105 }, 106 STEP8(8) { 107 @Override 108 public SpeedTableStep getPrevious() { 109 return STEP7; 110 } 111 112 @Override 113 public SpeedTableStep getNext() { 114 return STEP9; 115 } 116 }, 117 STEP9(9) { 118 @Override 119 public SpeedTableStep getPrevious() { 120 return STEP8; 121 } 122 123 @Override 124 public SpeedTableStep getNext() { 125 return STEP10; 126 } 127 }, 128 STEP10(10) { 129 @Override 130 public SpeedTableStep getPrevious() { 131 return STEP9; 132 } 133 134 @Override 135 public SpeedTableStep getNext() { 136 return STEP11; 137 } 138 }, 139 STEP11(11) { 140 @Override 141 public SpeedTableStep getPrevious() { 142 return STEP10; 143 } 144 145 @Override 146 public SpeedTableStep getNext() { 147 return STEP12; 148 } 149 }, 150 STEP12(12) { 151 @Override 152 public SpeedTableStep getPrevious() { 153 return STEP11; 154 } 155 156 @Override 157 public SpeedTableStep getNext() { 158 return STEP13; 159 } 160 }, 161 STEP13(13) { 162 @Override 163 public SpeedTableStep getPrevious() { 164 return STEP12; 165 } 166 167 @Override 168 public SpeedTableStep getNext() { 169 return STEP14; 170 } 171 }, 172 STEP14(14) { 173 @Override 174 public SpeedTableStep getPrevious() { 175 return STEP13; 176 } 177 178 @Override 179 public SpeedTableStep getNext() { 180 return STEP15; 181 } 182 }, 183 STEP15(15) { 184 @Override 185 public SpeedTableStep getPrevious() { 186 return STEP14; 187 } 188 189 @Override 190 public SpeedTableStep getNext() { 191 return STEP16; 192 } 193 }, 194 STEP16(16) { 195 @Override 196 public SpeedTableStep getPrevious() { 197 return STEP15; 198 } 199 200 @Override 201 public SpeedTableStep getNext() { 202 return STEP17; 203 } 204 }, 205 STEP17(17) { 206 @Override 207 public SpeedTableStep getPrevious() { 208 return STEP16; 209 } 210 211 @Override 212 public SpeedTableStep getNext() { 213 return STEP18; 214 } 215 }, 216 STEP18(18) { 217 @Override 218 public SpeedTableStep getPrevious() { 219 return STEP17; 220 } 221 222 @Override 223 public SpeedTableStep getNext() { 224 return STEP19; 225 } 226 }, 227 STEP19(19) { 228 @Override 229 public SpeedTableStep getPrevious() { 230 return STEP18; 231 } 232 233 @Override 234 public SpeedTableStep getNext() { 235 return STEP20; 236 } 237 }, 238 STEP20(20) { 239 @Override 240 public SpeedTableStep getPrevious() { 241 return STEP19; 242 } 243 244 @Override 245 public SpeedTableStep getNext() { 246 return STEP21; 247 } 248 }, 249 STEP21(21) { 250 @Override 251 public SpeedTableStep getPrevious() { 252 return STEP20; 253 } 254 255 @Override 256 public SpeedTableStep getNext() { 257 return STEP22; 258 } 259 }, 260 STEP22(22) { 261 @Override 262 public SpeedTableStep getPrevious() { 263 return STEP21; 264 } 265 266 @Override 267 public SpeedTableStep getNext() { 268 return STEP23; 269 } 270 }, 271 STEP23(23) { 272 @Override 273 public SpeedTableStep getPrevious() { 274 return STEP22; 275 } 276 277 @Override 278 public SpeedTableStep getNext() { 279 return STEP24; 280 } 281 }, 282 STEP24(24) { 283 @Override 284 public SpeedTableStep getPrevious() { 285 return STEP23; 286 } 287 288 @Override 289 public SpeedTableStep getNext() { 290 return STEP25; 291 } 292 }, 293 STEP25(25) { 294 @Override 295 public SpeedTableStep getPrevious() { 296 return STEP24; 297 } 298 299 @Override 300 public SpeedTableStep getNext() { 301 return STEP26; 302 } 303 }, 304 STEP26(26) { 305 @Override 306 public SpeedTableStep getPrevious() { 307 return STEP25; 308 } 309 310 @Override 311 public SpeedTableStep getNext() { 312 return STEP27; 313 } 314 }, 315 STEP27(27) { 316 @Override 317 public SpeedTableStep getPrevious() { 318 return STEP26; 319 } 320 321 @Override 322 public SpeedTableStep getNext() { 323 return STEP28; 324 } 325 }, 326 STEP28(28) { 327 @Override 328 public SpeedTableStep getPrevious() { 329 return STEP27; 330 } 331 332 @Override 333 public SpeedTableStep getNext() { 334 return null; 335 } 336 }; 337 338 private final int speedStep; 339 private final String cv; 340 341 private SpeedTableStep(int speedStep) { 342 this.speedStep = speedStep; 343 this.cv = String.valueOf(speedStep + 66); 344 } 345 346 /** 347 * Gets the speed step as an int 348 * 349 * @return int speed step 350 */ 351 public int getSpeedStep() { 352 return this.speedStep; 353 } 354 355 /** 356 * Gets the string CV of the SpeedTableStep 357 * 358 * @return string CV 359 */ 360 public String getCV() { 361 return this.cv; 362 } 363 364 /** 365 * Gets the next SpeedTableStep 366 * 367 * @return next SpeedTableStep 368 */ 369 public abstract SpeedTableStep getNext(); 370 371 /** 372 * Gets the previous SpeedTableStep 373 * 374 * @return previous SpeedTableStep 375 */ 376 public abstract SpeedTableStep getPrevious(); 377 } 378 379 protected enum SpeedMatcherCV { 380 VSTART(2, Bundle.getMessage("CVVStart")), 381 VMID(6, Bundle.getMessage("CVVMid")), 382 VHIGH(5, Bundle.getMessage("CVVHigh")), 383 ACCEL(3, Bundle.getMessage("CVAccel")), 384 DECEL(4, Bundle.getMessage("CVDecel")), 385 FORWARDTRIM(66, Bundle.getMessage("CVFwdTrim")), 386 REVERSETRIM(95, Bundle.getMessage("CVReverseTrim")); 387 388 private final String name; 389 private final String cv; 390 391 private SpeedMatcherCV(int cv, String name) { 392 this.cv = String.valueOf(cv); 393 this.name = name; 394 } 395 396 /** 397 * Gets the string CV value for the SpeedMatcherCV 398 * 399 * @return string CV value 400 */ 401 public String getCV() { 402 return this.cv; 403 } 404 405 /** 406 * Gets the string name of the SpeedMatcherCV 407 * 408 * @return string name 409 */ 410 public String getName() { 411 return this.name; 412 } 413 414 /** 415 * Gets the string display name of the SpeedMatcherCV 416 * 417 * @return string display name 418 */ 419 public String getCVDisplayName() { 420 return Bundle.getMessage("CVDisplayName", cv, name); 421 } 422 } 423 424 protected enum ProgrammerState { 425 IDLE, 426 WRITE2, 427 WRITE3, 428 WRITE4, 429 WRITE5, 430 WRITE6, 431 WRITE66, 432 WRITE95, 433 WRITE_SPEED_TABLE_STEP, 434 } 435 436 //</editor-fold> 437 438 //<editor-fold defaultstate="collapsed" desc="Instance Variables"> 439 440 protected int attempt = 0; 441 protected float speedMatchError = 0; 442 protected int speedMatcherValueDelta; 443 444 protected boolean trimReverseSpeed; 445 446 protected int warmUpForwardSeconds = 240; 447 protected int warmUpReverseSeconds = 120; 448 449 protected int stepDuration = 0; 450 protected float currentSpeedKPH = 0; 451 452 protected DccLocoAddress dccLocoAddress; 453 454 protected AddressedProgrammer opsModeProgrammer = null; 455 protected PowerManager powerManager = null; 456 457 protected JLabel statusLabel; 458 protected JButton startStopButton; 459 460 protected ProgrammerState programmerState = ProgrammerState.IDLE; 461 462 private DccThrottle throttle = null; 463 private float throttleIncrement; 464 465 private Timer speedMatchStateTimer; 466 //</editor-fold> 467 468 /** 469 * Constructor for the abstract SpeedMatcher at the core of any Speed 470 * Matcher 471 * 472 * @param config SpeedMatcherConfig for initializing the SpeedMatcher 473 */ 474 public SpeedMatcher(SpeedMatcherConfig config) { 475 this.dccLocoAddress = config.dccLocoAddress; 476 this.powerManager = config.powerManager; 477 478 this.trimReverseSpeed = config.trimReverseSpeed; 479 480 this.warmUpForwardSeconds = config.warmUpForwardSeconds; 481 this.warmUpReverseSeconds = config.warmUpReverseSeconds; 482 483 this.statusLabel = config.statusLabel; 484 this.startStopButton = config.startStopButton; 485 } 486 487 //<editor-fold defaultstate="collapsed" desc="Public APIs"> 488 /** 489 * Starts the speed matching process 490 * 491 * @return true if speed matching started successfully, false otherwise 492 */ 493 public abstract boolean startSpeedMatcher(); 494 495 /** 496 * Stops the speed matching process 497 */ 498 public abstract void stopSpeedMatcher(); 499 500 /** 501 * Indicates if the speed matcher is idle (not currently speed matching) 502 * 503 * @return true if idle, false otherwise 504 */ 505 public abstract boolean isSpeedMatcherIdle(); 506 507 /** 508 * Updates the locomotive's current speed in the speed matcher 509 * 510 * @param currentSpeedKPH the locomotive's current speed in KPH 511 */ 512 public void updateCurrentSpeed(float currentSpeedKPH) { 513 this.currentSpeedKPH = currentSpeedKPH; 514 } 515 //</editor-fold> 516 517 //<editor-fold defaultstate="collapsed" desc="Protected APIs"> 518 /** 519 * Validates the speed matcher's configuration 520 * 521 * @return true if the configuration is valid, false otherwise 522 */ 523 protected abstract boolean validate(); 524 525 /** 526 * Cleans up the speed matcher when speed matching is stopped or is finished 527 */ 528 protected void cleanUpSpeedMatcher() { 529 //stop the timer 530 if (speedMatchStateTimer != null) { 531 speedMatchStateTimer.stop(); 532 } 533 534 //release throttle 535 if (throttle != null) { 536 throttle.setSpeedSetting(0.0F); 537 InstanceManager.throttleManagerInstance().releaseThrottle(throttle, this); 538 throttle = null; 539 } 540 541 //release ops mode programmer 542 if (opsModeProgrammer != null) { 543 InstanceManager.getDefault(AddressedProgrammerManager.class).releaseAddressedProgrammer(opsModeProgrammer); 544 opsModeProgrammer = null; 545 } 546 547 startStopButton.setText(Bundle.getMessage("SpeedMatchStartBtn")); 548 } 549 550 /** 551 * Shared code to initialize the speed matcher's programmer and throttle and 552 * start the speed matching timer. Expected to be called in an implementing 553 * speed matcher's Start function. 554 * 555 * @param timerActionListener callback to fire when the timer times out 556 * @return true if initialization and start is successful, false otherwise 557 */ 558 protected boolean initializeAndStartSpeedMatcher(ActionListener timerActionListener) { 559 //Setup speed match timer 560 speedMatchStateTimer = new javax.swing.Timer(4000, timerActionListener); 561 speedMatchStateTimer.setRepeats(false); //timer is used without repeats to improve time accuracy when changing the delay 562 563 if (!getOpsModeProgrammer()) { 564 return false; 565 } 566 567 statusLabel.setText(Bundle.getMessage("StatRequestingThrottle")); 568 logger.info("Requesting Throttle"); 569 speedMatchStateTimer.start(); 570 boolean throttleRequestOK = InstanceManager.throttleManagerInstance().requestThrottle(dccLocoAddress, this, true); 571 if (!throttleRequestOK) { 572 logger.error("Loco Address in use, throttle request failed."); 573 statusLabel.setText(Bundle.getMessage("StatThrottleReqFailed")); 574 } 575 return throttleRequestOK; 576 } 577 578 /** 579 * Starts the speed match state timer 580 */ 581 protected void startSpeedMatchStateTimer() { 582 if (speedMatchStateTimer != null) { 583 speedMatchStateTimer.start(); 584 } 585 } 586 587 /** 588 * Stops the speed match state timer 589 */ 590 protected void stopSpeedMatchStateTimer() { 591 if (speedMatchStateTimer != null) { 592 speedMatchStateTimer.stop(); 593 } 594 } 595 596 /** 597 * Sets the duration for the speed match timer 598 * 599 * @param timerDuration timer duration in milliseconds 600 */ 601 protected void setSpeedMatchStateTimerDuration(int timerDuration) { 602 if (speedMatchStateTimer != null) { 603 speedMatchStateTimer.setInitialDelay(timerDuration); 604 } 605 } 606 607 /** 608 * Sets the speed matcher's throttle direction and speed safely within 609 * timers to protect against executing a throttle change to close to setting 610 * a CV 611 * 612 * @param isForward true for forward, false for revers 613 * @param speedStep 0-28 or 0-128 depending on mode 614 */ 615 protected void setThrottle(boolean isForward, int speedStep) { 616 try { 617 Thread.sleep(500); 618 } catch (InterruptedException e) { 619 Thread.currentThread().interrupt(); 620 } 621 622 logger.info("Set throttle to {} speed step {}", isForward ? "forward" : "reverse", speedStep); 623 624 throttle.setIsForward(isForward); 625 throttle.setSpeedSetting(speedStep * throttleIncrement); 626 627 try { 628 Thread.sleep(500); 629 } catch (InterruptedException e) { 630 Thread.currentThread().interrupt(); 631 } 632 } 633 634 /** 635 * Sets the current speed match error 636 * 637 * @param speedTarget - target speed in KPH 638 */ 639 protected void setSpeedMatchError(float speedTarget) { 640 speedMatchError = speedTarget - currentSpeedKPH; 641 } 642 643 /** 644 * Gets the next value to try for speed matching 645 * 646 * @param lastValue the last speed match CV value tried 647 * @param max the maximum value 648 * @param min the minimum value 649 * @return the next value to try for speed matching [min:max] 650 */ 651 protected int getNextSpeedMatchValue(int lastValue, int max, int min) { 652 attempt++; 653 654 if ((speedMatchError < 0 && speedMatcherValueDelta >= 0) || (speedMatchError >= 0 && speedMatcherValueDelta < 0)) { 655 speedMatcherValueDelta = -speedMatcherValueDelta; 656 if (attempt > 1) { 657 if (speedMatcherValueDelta >= 0) 658 { 659 speedMatcherValueDelta = Math.max(1, speedMatcherValueDelta/3); 660 } 661 else 662 { 663 speedMatcherValueDelta = Math.min(-1, speedMatcherValueDelta/3); 664 } 665 } 666 } 667 668 int value = lastValue + speedMatcherValueDelta; 669 670 if (value > max) { 671 value = max; 672 } else if (value < min) { 673 value = min; 674 } 675 676 return value; 677 } 678 679 /** 680 * Resets the speed matcher with the given value delta 681 * @param initialValueDelta the value delta to use for the next use of the speed matcher 682 */ 683 protected void resetSpeedMatcher(int initialValueDelta) { 684 attempt = 0; 685 speedMatcherValueDelta = initialValueDelta; 686 speedMatchError = 0; 687 } 688 //</editor-fold> 689 690 //<editor-fold defaultstate="collapsed" desc="Programmer"> 691 /** 692 * Starts writing vStart (CV 2) using the ops mode programmer 693 * 694 * @param value vStart value (0-255 inclusive) 695 */ 696 protected synchronized void writeVStart(int value) { 697 programmerState = ProgrammerState.WRITE2; 698 writeCV(SpeedMatcherCV.VSTART, value); 699 } 700 701 /** 702 * Starts writing vMid (CV 6) using the ops mode programmer 703 * 704 * @param value vMid value (0-255 inclusive) 705 */ 706 protected synchronized void writeVMid(int value) { 707 programmerState = ProgrammerState.WRITE6; 708 writeCV(SpeedMatcherCV.VMID, value); 709 } 710 711 /** 712 * Starts writing vHigh (CV 5) using the ops mode programmer 713 * 714 * @param value vHigh value (0-255 inclusive) 715 */ 716 protected synchronized void writeVHigh(int value) { 717 programmerState = ProgrammerState.WRITE5; 718 writeCV(SpeedMatcherCV.VHIGH, value); 719 } 720 721 /** 722 * Starts writing acceleration momentum (CV 3) using the ops mode programmer 723 * 724 * @param value acceleration value (0-255 inclusive) 725 */ 726 protected synchronized void writeMomentumAccel(int value) { 727 programmerState = ProgrammerState.WRITE3; 728 writeCV(SpeedMatcherCV.ACCEL, value); 729 } 730 731 /** 732 * Starts writing deceleration momentum (CV 4) using the ops mode programmer 733 * 734 * @param value deceleration value (0-255 inclusive) 735 */ 736 protected synchronized void writeMomentumDecel(int value) { 737 programmerState = ProgrammerState.WRITE4; 738 writeCV(SpeedMatcherCV.DECEL, value); 739 } 740 741 /** 742 * Starts writing forward trim (CV 66) using the ops mode programmer 743 * 744 * @param value forward trim value (0-255 inclusive) 745 */ 746 protected synchronized void writeForwardTrim(int value) { 747 programmerState = ProgrammerState.WRITE66; 748 writeCV(SpeedMatcherCV.FORWARDTRIM, value); 749 } 750 751 /** 752 * Starts writing reverse trim (CV 95) using the ops mode programmer 753 * 754 * @param value reverse trim value (0-255 inclusive) 755 */ 756 protected synchronized void writeReverseTrim(int value) { 757 programmerState = ProgrammerState.WRITE95; 758 writeCV(SpeedMatcherCV.REVERSETRIM, value); 759 } 760 761 /** 762 * Starts writing a Speed Table Step CV (CV 67-94) using the ops mode 763 * programmer 764 * 765 * @param step the SpeedTableStep to set 766 * @param value speed table step value (0-255 inclusive) 767 */ 768 protected synchronized void writeSpeedTableStep(SpeedTableStep step, int value) { 769 programmerState = ProgrammerState.WRITE_SPEED_TABLE_STEP; 770 statusLabel.setText(Bundle.getMessage("ProgSetCV", step.getCV() + " (Speed Step " + String.valueOf(step.getSpeedStep()) + ")", value)); 771 startOpsModeWrite(step.getCV(), value); 772 } 773 774 /** 775 * Starts writing a CV using the ops mode programmer and sets the status 776 * label 777 * 778 * @param cv CV to write to 779 * @param value value to write (0-255 inclusive) 780 */ 781 private synchronized void writeCV(SpeedMatcherCV cv, int value) { 782 statusLabel.setText(Bundle.getMessage("ProgSetCV", cv.getCVDisplayName(), value)); 783 startOpsModeWrite(cv.getCV(), value); 784 } 785 786 /** 787 * Starts writing a CV using the ops mode programmer 788 * 789 * @param cv string CV to write to 790 * @param value value to write (0-255 inclusive) 791 */ 792 private void startOpsModeWrite(String cv, int value) { 793 try { 794 logger.info("Setting CV {} to {}", cv, value); 795 opsModeProgrammer.writeCV(cv, value, this); 796 } catch (ProgrammerException e) { 797 logger.error("Exception writing CV {} {}", cv, e.toString()); 798 } 799 } 800 801 //<editor-fold defaultstate="collapsed" desc="ProgListener Overrides"> 802 /** 803 * Called when the programmer has completed its operation 804 * 805 * @param value value from a read operation, or value written on a write 806 * @param status denotes the completion code. Note that this is a bitwise 807 * combination of the various states codes defined in this 808 * interface. (see ProgListener.java for possible values) 809 */ 810 @Override 811 public void programmingOpReply(int value, int status) { 812 if (status == 0) { //OK 813 switch (programmerState) { 814 case IDLE: 815 logger.debug("unexpected reply in IDLE state"); 816 break; 817 818 case WRITE2: 819 case WRITE3: 820 case WRITE4: 821 case WRITE5: 822 case WRITE6: 823 case WRITE66: 824 case WRITE95: 825 case WRITE_SPEED_TABLE_STEP: 826 programmerState = ProgrammerState.IDLE; 827 break; 828 829 default: 830 programmerState = ProgrammerState.IDLE; 831 logger.warn("Unhandled programmer state: {}", programmerState); 832 break; 833 } 834 } else { 835 // Error during programming 836 logger.error("Status not OK during {}: {}", programmerState.toString(), status); 837 statusLabel.setText("Error using programmer"); 838 programmerState = ProgrammerState.IDLE; 839 cleanUpSpeedMatcher(); 840 } 841 } 842 //</editor-fold> 843 //</editor-fold> 844 845 //<editor-fold defaultstate="collapsed" desc="Helper Functions"> 846 /** 847 * Acquires an ops mode programmer for use in the speed matcher 848 * 849 * @return true if the ops mode programmer was successfully acquired, false 850 * otherwise 851 */ 852 private boolean getOpsModeProgrammer() { 853 logger.info("Requesting Programmer"); 854 //get OPS MODE Programmer 855 if (InstanceManager.getNullableDefault(AddressedProgrammerManager.class) != null) { 856 if (InstanceManager.getDefault(AddressedProgrammerManager.class).isAddressedModePossible(dccLocoAddress)) { 857 opsModeProgrammer = InstanceManager.getDefault(AddressedProgrammerManager.class).getAddressedProgrammer(dccLocoAddress); 858 } 859 } 860 861 if (opsModeProgrammer != null) { 862 return true; 863 } else { 864 logger.error("Programmer request failed."); 865 statusLabel.setText(Bundle.getMessage("StatProgrammerReqFailed")); 866 return false; 867 } 868 } 869 //</editor-fold> 870 871 //<editor-fold defaultstate="collapsed" desc="ThrottleListener Overrides"> 872 /** 873 * Called when a throttle is found Implementers must override, call super, 874 * and start speed matcher in implementation 875 * 876 * @param t the requested DccThrottle 877 */ 878 @Override 879 public void notifyThrottleFound(DccThrottle t) { 880 stopSpeedMatchStateTimer(); 881 882 throttle = t; 883 logger.info("Throttle acquired"); 884 throttle.setSpeedStepMode(SpeedStepMode.NMRA_DCC_28); 885 if (throttle.getSpeedStepMode() != SpeedStepMode.NMRA_DCC_28) { 886 logger.error("Failed to set 28 step mode"); 887 statusLabel.setText(Bundle.getMessage("ThrottleError28")); 888 InstanceManager.throttleManagerInstance().releaseThrottle(throttle, this); 889 return; 890 } 891 892 // turn on power 893 try { 894 powerManager.setPower(PowerManager.ON); 895 } catch (JmriException e) { 896 logger.error("Exception during power on: {}", e.toString()); 897 return; 898 } 899 900 throttleIncrement = throttle.getSpeedIncrement(); 901 } 902 903 /** 904 * Called when we must decide whether to steal the throttle for the 905 * requested address. This is an automatically stealing implementation, so 906 * the throttle will be automatically stolen 907 * 908 * @param address the requested address 909 * @param question the question being asked, steal / cancel, share / cancel, 910 * steal / share / cancel 911 */ 912 @Override 913 public void notifyDecisionRequired(LocoAddress address, DecisionType question) { 914 InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.STEAL); 915 } 916 917 /** 918 * Called when a throttle could not be obtained 919 * 920 * @param address the requested address 921 * @param reason the reason the throttle could not be obtained 922 */ 923 @Override 924 public void notifyFailedThrottleRequest(jmri.LocoAddress address, String reason) { 925 } 926 //</editor-fold> 927 928 //debugging logger 929 private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SpeedMatcher.class); 930}