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}