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}