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