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