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, 85); // supports 85 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[] getFnValueArray(int number) {
055                
056            if (number < 0) return new long[]{};
057            
058            if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_100) {
059                if (number < SERIAL_FUNCTION_CODES_TMCC1.length) {
060                    return SERIAL_FUNCTION_CODES_TMCC1[number];
061                } else {
062                    return new long[]{};
063                }
064            } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_32) {
065                 if (number < SERIAL_FUNCTION_CODES_TMCC2_32.length) {
066                     return SERIAL_FUNCTION_CODES_TMCC2_32[number];
067                } else {
068                    return new long[]{};
069                }
070            } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_200) {
071                 if (number < SERIAL_FUNCTION_CODES_TMCC2_200.length) {
072                     return SERIAL_FUNCTION_CODES_TMCC2_200[number];
073                } else {
074                    return new long[]{};
075                }
076            }
077            return new long[]{};
078        }
079
080
081    /**
082     * {@inheritDoc}
083     */
084    @Override
085    public void setFunction(int func, boolean value) {
086        updateFunction(func, value);
087        
088        int numberOfTriple = 0; // ordinal number of triple being processed, e.g. 1, 2, 3, ...
089            checksumAccumulator = 0; // zeroes out accumulated checksum after each instance
090        
091        if (getFnValueArray(func).length >0) {
092            for (long triple : getFnValueArray(func)) {
093                numberOfTriple = numberOfTriple+1;
094                
095                // process each returned command
096                if (func>=0 && func < SERIAL_FUNCTION_CODES_TMCC1.length) {
097                    if ( triple > 0xFFFF ) {
098                        // TMCC 2 format
099                        if (triple > 0xFFFFFF ) {
100                            int first =  (int)(triple >> 24);
101                            int second = (int)(triple & 0xFFFFFF);
102                            accumulateChecksum(first);
103                            accumulateChecksum(second);
104                            
105                            // if this is the third triple, place the 
106                            // checksum in its lowest byte
107                            if (numberOfTriple == 3) {
108                                second = (second &0xFFFFFF00) | ( (~checksumAccumulator) & 0xFF);
109                            }
110                            
111                            // doubles are only sent once, not repeating
112                            sendOneWordOnce(first  + address.getNumber() * 512);
113                            sendOneWordOnce(second + address.getNumber() * 512);
114                            
115                        } else {
116                            // single message
117                            int content = (int)triple + address.getNumber() * 512;
118                            accumulateChecksum(content);
119                            
120                            // if this is the third triple, place the 
121                            // checksum in its lowest byte
122                            if (numberOfTriple == 3) {
123                                content = (content &0xFFFFFF00) | ( (~checksumAccumulator) & 0xFF);
124                            }
125
126                            sendFnToLayout(content, func);
127                        }
128                    } else {
129                        // TMCC 1 format
130                        sendFnToLayout((int)triple + address.getNumber() * 128, func);
131                    }
132                } else {
133                    super.setFunction(func, value);
134                }
135            }
136        } else {
137            super.setFunction(func, value);
138        }
139    }
140
141    // accumulate the checksum values into
142    // the checksumAccumulator variable
143    //
144    // Takes the two bottom bytes _only_ and adds them to the accumulation.
145    int checksumAccumulator = 0;
146    private void accumulateChecksum(int input) {
147        int byte1 = input&0xFF;
148        int byte2 = (input >> 8)&0xFF;
149        checksumAccumulator = (checksumAccumulator + byte1+byte2) & 0xFF;
150    }
151    
152    // the argument is a long containing 3 bytes. 
153    // The first byte is the message opcode
154    private void sendOneWordOnce(int word) {
155        SerialMessage m = new SerialMessage(word);
156        tc.sendSerialMessage(m, null);
157    }
158
159    // TMCC 1 Function Keys to trigger with TMCC1_32 and TMCC1_100 speed steps.
160    private final static long[][] SERIAL_FUNCTION_CODES_TMCC1 = new long[][] {
161
162        // TMCC1 Remote - Defined FnKeys
163        {0x00000D}, // Fn0 (Headlamp)
164        {0x00001D}, // Fn1 (Bell)
165        {0x00001C}, // Fn2 (Horn/Whistle)
166        {0x000005}, // Fn3 (F - Open Front Coupler)
167        {0x000006}, // Fn4 (R - Open Rear Coupler)
168
169        // TMCC1 Remote - Defined KeyPad FnKeys
170        {0x000011}, {0x000012}, {0x000013}, /* Fn5-7 */ // 1-2-3
171        {0x000014}, {0x000015}, {0x000016}, /* Fn8-10 */ // 4-5-6
172        {0x000017}, {0x000018}, {0x000019}, /* Fn11-13 */ // 7-8-9
173                    {0x000010},             /* Fn14 */ // 0
174
175        // TMCC1 Remote - Defined FnKeys
176        {0x000009}, // Fn15 (Aux1)
177        {0x00001E}, // Fn16 (Letoff Sound)
178        {0x000004}, // Fn17 (Boost)
179        {0x000007}, // Fn18 (Brake)
180        {0x000028}, // Fn19 (Momentum Low)
181        {0x000029}, // Fn20 (Momentum Medium)
182        {0x00002A}, // Fn21 (Momentum High)
183        {0x00002B}, // Fn22 (Set)
184        {0x00001F}, // Fn23 (Horn 2)
185
186        // TMCC1 RR Speed FnKeys
187        {0x000064}, // Fn24 ( 4)   5mph
188        {0x00006A}, // Fn25 (10)  20mph
189        {0x00006E}, // Fn26 (14)  35mph
190        {0x000072}, // Fn27 (18)  50mph
191        {0x000078}, // Fn28 (24)  70mph
192        {0x00007F}, // Fn29 (31)   Full
193
194        // TMCC1 ERR - Set SpeedSteps
195        {0x000009, 0x000010, 0x000009, 0x000010, 0x000004}, // Fn30 (Set ERR 100 SpeedSteps)
196        {0x000009, 0x000010, 0x000009, 0x000010, 0x000007}, // Fn31 (Set ERR  32 SpeedSteps)
197
198        // TMCC1 Acela/Subway FnKeys
199        {0x000009, 0x000005}, // Fn32 (Open Doors - Left)
200        {0x00000D, 0x000005, 0x00000D}, // Fn33 (Close Doors - Left)
201        {0x000009, 0x000006}, // Fn34 (Open Doors - Right)
202        {0x00000D, 0x000006, 0x00000D}, // Fn35 (Close Doors - Right)
203        {0x000009, 0x000013}, // Fn36 (Pantagraph - Automatic/Prototypical)
204        {0x000009, 0x000015}, // Fn37 (Pantagraph - Down)
205        {0x000009, 0x000015}, // Fn38 (Pantagraph - Manual Mode/Cycles Through Positions)
206        {0x000009, 0x00001C}, // Fn39 (Toggle Horn - City/Country)
207        {0x000009, 0x000018}, // Fn40 (Cab Light - Off)
208        {0x000009, 0x000019}, // Fn41 (Cab Light - On)
209        {0x000009, 0x00000D, 0x000018, 0x00000D}, // Fn42 (Interior Lights - Off)
210        {0x000009, 0x00000D, 0x000019, 0x00000D}, // Fn43 (Interior Lights - On)
211
212        // TMCC1 Break-Down Unit
213        {0x000009, 0x000004}, // Fn44 Start Breakdown Sequence
214        {0x000009, 0x000015}, // Fn45 Made It Back to the Yard
215        {0x000009, 0x000013}, // Fn46 Restart Unit/Repairs Complete
216
217        // TMCC1 Freight/Stock Cars
218        {0x000009, 0x000013}, // Fn47 Load
219        {0x000009, 0x000012}, // Fn48 Flat Wheel Sound
220
221        // TMCC1 Passenger Cars
222        {0x000009, 0x000017}, // Fn49 Station PA Arrival Dialog
223        {0x000009, 0x000012}, // Fn50 Conductor Arrival Dialog
224
225        // TMCC1 Crane/Boom Car
226        {0x000009, 0x000011, 0x000007}, // Fn51 Lower the Boom
227        {0x000009, 0x000011, 0x000004}, // Fn52 Raises the Boom
228        {0x000009, 0x000012, 0x000007}, // Fn53 Lowers Main/Large Hook
229        {0x000009, 0x000012, 0x000004}, // Fn54 Raises Main/Large Hook
230        {0x000017, 0x000013, 0x000007}, // Fn55 Lowers Small Hook
231        {0x000017, 0x000013, 0x000004}, // Fn56 Raises Small Hook
232        {0x000004, 0x000014}, // Fn57 Front Work Lights (Toggles On/Off)
233        {0x000004, 0x000015}, // Fn58 Rear Work Lights (Toggles On/Off)
234        {0x000004, 0x000016}, // Fn59 Launches Outriggers
235        {0x000009, 0x000017}, // Fn60 Crew Dialog Off
236        {0x000009, 0x000018}, // Fn61 All Sounds Off
237        {0x000009, 0x000019}, // Fn62 All Sounds On
238
239        // TMCC1 Unassigned FnKeys
240        {0x00002E}, // Fn63 Code to Trigger SerialMonFrame Message/Unassigned FnKey
241        {0x00002E}, // Fn64 Code to Trigger SerialMonFrame Message/Unassigned FnKey
242        {0x00002E}, // Fn65 Code to Trigger SerialMonFrame Message/Unassigned FnKey
243        {0x00002E}, // Fn66 Code to Trigger SerialMonFrame Message/Unassigned FnKey
244        {0x00002E}, // Fn67 Code to Trigger SerialMonFrame Message/Unassigned FnKey
245        {0x00002E}, // Fn68 Code to Trigger SerialMonFrame Message/Unassigned FnKey
246
247        // TMCC1 Aux FnKeys
248        {0x000008}, // Fnxx (Aux1 Off)
249        {0x000009}, // Fnxx (Aux1 Option 1 - On While Held)
250        {0x00000A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off)
251        {0x00000B}, // Fnxx (Aux1 On)
252        {0x00000C}, // Fnxx (Aux2 Off)
253        {0x00000D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off)
254        {0x00000E}, // Fnxx (Aux2 Option 2 - On While Held)
255        {0x00000F}, // Fnxx (Aux2 On)
256
257    };
258
259    /**
260    * Translate TMCC1 function numbers to line characters.
261    * If the upper byte is zero, it will be replaced by 0xF8
262    * and the address will be set in the low position.
263    * If the upper byte is non-zero, that value will be sent,
264    * and the address will be set in the upper (TMCC2) position.
265    * If six bytes are specified (with the upper one non-zero), 
266    * this will be interpreted as two commands to be sequentially sent,
267    * with the upper bytes sent first.
268    */
269
270    // TMCC 2 Legacy Function Keys to trigger with TMCC2_32 speed steps.
271    private final static long[][] SERIAL_FUNCTION_CODES_TMCC2_32 = new long[][] {
272
273        // TMCC2_32 Remote - Defined FnKeys
274        {0xF8010D}, // Fn0 (Headlamp)
275        {0xF8011D}, // Fn1 (Bell)
276        {0xF8011C}, // Fn2 (Horn/Whistle)
277        {0xF80105}, // Fn3 (F - Open Front Coupler)
278        {0xF80106}, // Fn4 (R - Open Rear Coupler)
279
280        // TMCC2_32 Remote - Defined KeyPad FnKeys
281        {0xF80111}, {0xF80112}, {0xF80113}, /* Fn5-7 */ // 1-2-3
282        {0xF80114}, {0xF80115}, {0xF80116}, /* Fn8-10 */ // 4-5-6
283        {0xF80117}, {0xF80118}, {0xF80119}, /* Fn11-13 */ // 7-8-9
284                    {0xF80110},             /* Fn14 */ // 0
285
286        // TMCC2_32 Remote - Defined FnKeys
287        {0xF80109}, // Fn15 (Aux1)
288        {0xF8011E}, // Fn16 (Letoff Sound)
289        {0xF80104}, // Fn17 (Boost)
290        {0xF80107}, // Fn18 (Brake)
291        {0xF80128}, // Fn19 (Momentum Low)
292        {0xF80129}, // Fn20 (Momentum Medium)
293        {0xF8012A}, // Fn21 (Momentum High)
294        {0xF8012B}, // Fn22 (Set)
295        {0xF8011F}, // Fn23 (Horn 2)
296
297        // TMCC2_32 RR Speed FnKeys
298        {0xF80164}, // Fn24 ( 4)   5mph
299        {0xF8016A}, // Fn25 (10)  20mph
300        {0xF8016E}, // Fn26 (14)  35mph
301        {0xF80172}, // Fn27 (18)  50mph
302        {0xF80178}, // Fn28 (24)  70mph
303        {0xF8017F}, // Fn29 (31)   Full
304
305        // TMCC2_32 Extended Lighting FnKeys
306        {0xF8017D, 0xFB00E8, 0xFB0000}, // Fn30 (Mars Lt On)
307        {0xF8017D, 0xFB00E9, 0xFB0000}, // Fn31 (Mars Lt Off)
308
309        {0xF8017D, 0xFB00D0, 0xFB0000}, // Fn32 (Ground Lt On)
310        {0xF8017D, 0xFB00D1, 0xFB0000}, // Fn33 (Ground Lt Off)
311        {0xF8017D, 0xFB00D2, 0xFB0000}, // Fn34 (Ground Lt Auto)
312
313        {0xF8017D, 0xFB00A0, 0xFB0000}, // Fn35 (DogHouse On)
314        {0xF8017D, 0xFB00A1, 0xFB0000}, // Fn36 (DogHouse Off)
315
316        {0xF8017D, 0xFB00CC, 0xFB0000}, // Fn37 (Tender Marker On)
317        {0xF8017D, 0xFB00CD, 0xFB0000}, // Fn38 (Tender Marker Off)
318
319        {0xF8017D, 0xFB00F4, 0xFB0000}, // Fn39 (Rule 17 On)
320        {0xF8017D, 0xFB00F5, 0xFB0000}, // Fn40 (Rule 17 Off)
321        {0xF8017D, 0xFB00F6, 0xFB0000}, // Fn41 (Rule 17 Auto)
322
323        {0xF8017D, 0xFB00C0, 0xFB0000}, // Fn42 (Ditch Lt On)
324        {0xF8017D, 0xFB00C1, 0xFB0000}, // Fn43 (Ditch Lt On; Pulse Off with Horn)
325        {0xF8017D, 0xFB00C2, 0xFB0000}, // Fn44 (Ditch Lt Off; Pulse On with Horn)
326        {0xF8017D, 0xFB00C3, 0xFB0000}, // Fn45 (Ditch Lt Off)
327
328        {0xF8017D, 0xFB00F0, 0xFB0000}, // Fn46 (Cab Lt On)
329        {0xF8017D, 0xFB00F1, 0xFB0000}, // Fn47 (Cab Lt Off)
330        {0xF8017D, 0xFB00F2, 0xFB0000}, // Fn48 (Cab Lt Auto)
331
332        {0xF8017D, 0xFB00C8, 0xFB0000}, // Fn49 (Loco Marker On)
333        {0xF8017D, 0xFB00C9, 0xFB0000}, // Fn50 (Loco Marker Off)
334
335        {0xF8017D, 0xFB00B0, 0xFB0000}, // Fn51 (Hazard Lt On)
336        {0xF8017D, 0xFB00B1, 0xFB0000}, // Fn52 (Hazard Lt Off)
337        {0xF8017D, 0xFB00B2, 0xFB0000}, // Fn53 (Hazard Lt Auto)
338
339        {0xF8017D, 0xFB00E0, 0xFB0000}, // Fn54 (Strobe Lt On - Single Flash)
340        {0xF8017D, 0xFB00E1, 0xFB0000}, // Fn55 (Strobe Lt On - Double Flash)
341        {0xF8017D, 0xFB00E2, 0xFB0000}, // Fn56 (Strobe Lt Off)
342
343        {0xF8017D, 0xFB00F8, 0xFB0000}, // Fn57 (Car Cabin Lt On)
344        {0xF8017D, 0xFB00F9, 0xFB0000}, // Fn58 (Car Cabin Lt Off)
345        {0xF8017D, 0xFB00FA, 0xFB0000}, // Fn59 (Car Cabin Lt Auto)
346
347        // TMCC2 Subway FnKeys
348        {0xF8017C, 0xFB0020, 0xFB0000}, // Fn60 (Open Doors - Left)
349        {0xF8017C, 0xFB0021, 0xFB0000}, // Fn61 (Close Doors - Left)
350        {0xF8017C, 0xFB0022, 0xFB0000}, // Fn62 (Open Doors - Right)
351        {0xF8017C, 0xFB0023, 0xFB0000}, // Fn63 (Close Doors - Right)
352        {0xF8017C, 0xFB0010, 0xFB0000}, // Fn64 (Pantagraph - Up/F)
353        {0xF8017C, 0xFB0011, 0xFB0000}, // Fn65 (Pantagraph - Down/F)
354        {0xF8017C, 0xFB0012, 0xFB0000}, // Fn66 (Pantagraph - Up/R)
355        {0xF8017C, 0xFB0013, 0xFB0000}, // Fn67 (Pantagraph - Down/R)
356        
357        // TMCC2 Freight/Stock Cars
358        {0xF8017C, 0xFB0030, 0xFB0000}, // Fn68 (Option1 On)
359        {0xF8017C, 0xFB0031, 0xFB0000}, // Fn69 (Opiton1 Off)
360        {0xF8017C, 0xFB0032, 0xFB0000}, // Fn70 (Option2 On)
361        {0xF8017C, 0xFB0033, 0xFB0000}, // Fn71 (Option2 Off)
362        {0xF8017C, 0xFB0034, 0xFB0000}, // Fn72 (Load)
363        {0xF8017C, 0xFB0035, 0xFB0000}, // Fn73 (Unload)
364        {0xF8017C, 0xFB0036, 0xFB0000}, // Fn74 (FRED On)
365        {0xF8017C, 0xFB0037, 0xFB0000}, // Fn75 (FRED Off)
366        {0xF8017C, 0xFB0038, 0xFB0000}, // Fn76 (Flat Wheel On)
367        {0xF8017C, 0xFB0039, 0xFB0000}, // Fn77 (Flat Wheel Off)
368        {0xF8017C, 0xFB003A, 0xFB0000}, // Fn78 (Game On)
369        {0xF8017C, 0xFB003B, 0xFB0000}, // Fn79 (Game Off)
370
371        // TMCC2 Smoke System
372        {0xF8017C, 0xFB0000, 0xFB0000}, // Fn80 (Smoke System Off)
373        {0xF8017C, 0xFB0001, 0xFB0000}, // Fn81 (Smoke System Low)
374        {0xF8017C, 0xFB0002, 0xFB0000}, // Fn82 (Smoke System Med)
375        {0xF8017C, 0xFB0003, 0xFB0000}, // Fn83 (Smoke System High)
376
377        // TRMCC2_32 Unassigned FnKeys
378        {0xF8012E}, // Fn84 Code to Trigger SerialMonFrame Message/Unassigned FnKey
379        {0xF8012E}, // Fn85 Code to Trigger SerialMonFrame Message/Unassigned FnKey
380
381        // TRMCC2_32 Aux FnKeys
382        {0xF80108}, // Fnxx (Aux1 Off)
383        {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 
384        {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off)
385        {0xF8010B}, // Fnxx (Aux1 On)
386        {0xF8010C}, // Fnxx (Aux2 Off)
387        {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 
388        {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held)
389        {0xF8010F}, // Fnxx (Aux2 On)
390
391};
392
393    // TMCC 2 Legacy Function Keys to trigger with TMCC2_200 speed steps.
394    private final static long[][] SERIAL_FUNCTION_CODES_TMCC2_200 = new long[][] {
395
396        // TMCC2_200 Remote - Defined FnKeys
397        {0xF8010D}, // Fn0 (Headlamp)
398        {0xF8011D}, // Fn1 (Bell)
399        {0xF8011C}, // Fn2 (Horn/Whistle)
400        {0xF80105}, // Fn3 (F - Open Front Coupler)
401        {0xF80106}, // Fn4 (R - Open Rear Coupler)
402
403        // TMCC2_200 Remote - Defined KeyPad FnKeys
404        {0xF80111}, {0xF80112}, {0xF80113}, /* Fn5-7 */ // 1-2-3
405        {0xF80114}, {0xF80115}, {0xF80116}, /* Fn8-10 */ // 4-5-6
406        {0xF80117}, {0xF80118}, {0xF80119}, /* Fn11-13 */ // 7-8-9
407                    {0xF80110},             /* Fn14 */ // 0
408
409        // TMCC2_200 Remote - Defined FnKeys
410        {0xF80109}, // Fn15 (Aux1)
411        {0xF8011E}, // Fn16 (Letoff Sound)
412        {0xF80104}, // Fn17 (Boost)
413        {0xF80107}, // Fn18 (Brake)
414        {0xF80128}, // Fn19 (Momentum Low)
415        {0xF80129}, // Fn20 (Momentum Medium)
416        {0xF8012A}, // Fn21 (Momentum High)
417        {0xF8012B}, // Fn22 (Set)
418        {0xF8011F}, // Fn23 (Horn 2)
419
420        // TMCC2_200 RR Speed FnKeys
421        {0xF8000A}, // Fn24 ( 10)   5mph
422        {0xF80028}, // Fn25 ( 40)  20mph
423        {0xF80046}, // Fn26 ( 70)  35mph
424        {0xF80064}, // Fn27 (100)  50mph
425        {0xF8008C}, // Fn28 (140)  70mph
426        {0xF800C7}, // Fn29 (199)   Full
427
428        // TMCC2_200 Extended Lighting FnKeys
429        {0xF8017D, 0xFB00E8, 0xFB0000}, // Fn30 (Mars Lt On)
430        {0xF8017D, 0xFB00E9, 0xFB0000}, // Fn31 (Mars Lt Off)
431
432        {0xF8017D, 0xFB00D0, 0xFB0000}, // Fn32 (Ground Lt On)
433        {0xF8017D, 0xFB00D1, 0xFB0000}, // Fn33 (Ground Lt Off)
434        {0xF8017D, 0xFB00D2, 0xFB0000}, // Fn34 (Ground Lt Auto)
435
436        {0xF8017D, 0xFB00A0, 0xFB0000}, // Fn35 (DogHouse On)
437        {0xF8017D, 0xFB00A1, 0xFB0000}, // Fn36 (DogHouse Off)
438
439        {0xF8017D, 0xFB00CC, 0xFB0000}, // Fn37 (Tender Marker On)
440        {0xF8017D, 0xFB00CD, 0xFB0000}, // Fn38 (Tender Marker Off)
441
442        {0xF8017D, 0xFB00F4, 0xFB0000}, // Fn39 (Rule 17 On)
443        {0xF8017D, 0xFB00F5, 0xFB0000}, // Fn40 (Rule 17 Off)
444        {0xF8017D, 0xFB00F6, 0xFB0000}, // Fn41 (Rule 17 Auto)
445
446        {0xF8017D, 0xFB00C0, 0xFB0000}, // Fn42 (Ditch Lt On)
447        {0xF8017D, 0xFB00C1, 0xFB0000}, // Fn43 (Ditch Lt On; Pulse Off with Horn)
448        {0xF8017D, 0xFB00C2, 0xFB0000}, // Fn44 (Ditch Lt Off; Pulse On with Horn)
449        {0xF8017D, 0xFB00C3, 0xFB0000}, // Fn45 (Ditch Lt Off)
450
451        {0xF8017D, 0xFB00F0, 0xFB0000}, // Fn46 (Cab Lt On)
452        {0xF8017D, 0xFB00F1, 0xFB0000}, // Fn47 (Cab Lt Off)
453        {0xF8017D, 0xFB00F2, 0xFB0000}, // Fn48 (Cab Lt Auto)
454
455        {0xF8017D, 0xFB00C8, 0xFB0000}, // Fn49 (Loco Marker On)
456        {0xF8017D, 0xFB00C9, 0xFB0000}, // Fn50 (Loco Marker Off)
457
458        {0xF8017D, 0xFB00B0, 0xFB0000}, // Fn51 (Hazard Lt On)
459        {0xF8017D, 0xFB00B1, 0xFB0000}, // Fn52 (Hazard Lt Off)
460        {0xF8017D, 0xFB00B2, 0xFB0000}, // Fn53 (Hazard Lt Auto)
461
462        {0xF8017D, 0xFB00E0, 0xFB0000}, // Fn54 (Strobe Lt On - SingleFlash)
463        {0xF8017D, 0xFB00E1, 0xFB0000}, // Fn55 (Strobe Lt On - DoubleFlash)
464        {0xF8017D, 0xFB00E2, 0xFB0000}, // Fn56 (Strobe Lt Off)
465
466        {0xF8017D, 0xFB00F8, 0xFB0000}, // Fn57 (Car Cabin Lt On)
467        {0xF8017D, 0xFB00F9, 0xFB0000}, // Fn58 (Car Cabin Lt Off)
468        {0xF8017D, 0xFB00FA, 0xFB0000}, // Fn59 (Car Cabin Lt Auto)
469
470        // TMCC2 Subway FnKeys
471        {0xF8017C, 0xFB0020, 0xFB0000}, // Fn60 (Open Doors - Left)
472        {0xF8017C, 0xFB0021, 0xFB0000}, // Fn61 (Close Doors - Left)
473        {0xF8017C, 0xFB0022, 0xFB0000}, // Fn62 (Open Doors - Right)
474        {0xF8017C, 0xFB0023, 0xFB0000}, // Fn63 (Close Doors - Right)
475        {0xF8017C, 0xFB0010, 0xFB0000}, // Fn64 (Pantagraph - Up/F)
476        {0xF8017C, 0xFB0011, 0xFB0000}, // Fn65 (Pantagraph - Down/F)
477        {0xF8017C, 0xFB0012, 0xFB0000}, // Fn66 (Pantagraph - Up/R)
478        {0xF8017C, 0xFB0013, 0xFB0000}, // Fn67 (Pantagraph - Down/R)
479
480        // TMCC2 Freight/Stock Cars
481        {0xF8017C, 0xFB0030, 0xFB0000}, // Fn68 (Option1 On)
482        {0xF8017C, 0xFB0031, 0xFB0000}, // Fn69 (Opiton1 Off)
483        {0xF8017C, 0xFB0032, 0xFB0000}, // Fn70 (Option2 On)
484        {0xF8017C, 0xFB0033, 0xFB0000}, // Fn71 (Option2 Off)
485        {0xF8017C, 0xFB0034, 0xFB0000}, // Fn72 (Load)
486        {0xF8017C, 0xFB0035, 0xFB0000}, // Fn73 (Unload)
487        {0xF8017C, 0xFB0036, 0xFB0000}, // Fn74 (FRED On)
488        {0xF8017C, 0xFB0037, 0xFB0000}, // Fn75 (FRED Off)
489        {0xF8017C, 0xFB0038, 0xFB0000}, // Fn76 (Flat Wheel On)
490        {0xF8017C, 0xFB0039, 0xFB0000}, // Fn77 (Flat Wheel Off)
491        {0xF8017C, 0xFB003A, 0xFB0000}, // Fn78 (Game On)
492        {0xF8017C, 0xFB003B, 0xFB0000}, // Fn79 (Game Off)
493
494        // TMCC2 Smoke System
495        {0xF8017C, 0xFB0000, 0xFB0000}, // Fn80 (Smoke System Off)
496        {0xF8017C, 0xFB0001, 0xFB0000}, // Fn81 (Smoke System Low)
497        {0xF8017C, 0xFB0002, 0xFB0000}, // Fn82 (Smoke System Med)
498        {0xF8017C, 0xFB0003, 0xFB0000}, // Fn83 (Smoke System High)
499
500        // TRMCC2_200 Unassigned FnKeys
501        {0xF8012E}, // Fn84 Code to Trigger SerialMonFrame Message/Unassigned FnKey
502        {0xF8012E}, // Fn85 Code to Trigger SerialMonFrame Message/Unassigned FnKey
503
504        // TMCC2_200 Aux FnKeys
505        {0xF80108}, // Fnxx (Aux1 Off)
506        {0xF80109}, // Fnxx (Aux1 Option 1 - On While Held) 
507        {0xF8010A}, // Fnxx (Aux1 Option 2 - Toggle On/Toggle Off)
508        {0xF8010B}, // Fnxx (Aux1 On)
509        {0xF8010C}, // Fnxx (Aux2 Off)
510        {0xF8010D}, // Fnxx (Aux2 Option 1 - Toggle On/Toggle Off) 
511        {0xF8010E}, // Fnxx (Aux2 Option 2 - On While Held)
512        {0xF8010F}, // Fnxx (Aux2 On)
513
514    };
515
516    /**
517     * Set the speed.
518     *
519     * @param speed Number from 0 to 1; less than zero is emergency stop
520     */
521    @Override
522    public void setSpeedSetting(float speed) {
523        float oldSpeed;
524        synchronized(this) {
525            oldSpeed = this.speedSetting;
526            this.speedSetting = speed;
527        }
528        
529        // send to layout option 200 speed steps
530        if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) {
531
532            // TMCC2 Legacy 200 speed step mode
533            int value = (int) (199 * speed); // max value to send is 199 in 200 step mode
534            if (value > 199) {
535                // max possible speed
536                value = 199;
537            }
538            SerialMessage m = new SerialMessage();
539            m.setOpCode(0xF8);
540    
541            if (value < 0) {
542                // System HALT (immediate stop; ALL)
543                m.putAsWord(0xFF8B);
544            } else {
545                // normal speed setting
546                m.putAsWord(0x0000 + (address.getNumber() << 9) + value);
547            }
548            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
549            tc.sendSerialMessage(m, null);
550            tc.sendSerialMessage(m, null);
551        }
552
553        // send to layout option 100 speed steps
554        if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) {
555            
556          /** 
557            * TMCC1 ERR 100 speed step mode
558            * purpose is to increase resolution of 32 bits
559            * across 100 throttle 'clicks' by dividing value by 3            
560            * and setting top speed at 32
561          */
562            int value = (int) (99 * speed); // max value to send is 99 in 100 step mode
563            if (value > 93) {
564                // max possible speed step
565                value = 93;
566            }
567            SerialMessage m = new SerialMessage();
568            m.setOpCode(0xFE);
569
570            if (value < 0) {
571                // System HALT (immediate stop; ALL)
572                m.putAsWord(0xFFFF);
573            }
574            if (value >= 0) {
575                // normal speed step setting
576                m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3);
577            }
578                            
579            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
580            tc.sendSerialMessage(m, null);
581            tc.sendSerialMessage(m, null);
582        }
583
584        // send to layout option TMCC2 32 speed steps
585        if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) {
586
587            // TMCC2 Legacy 32 speed step mode
588            int value = (int) (32 * speed);
589            if (value > 31) {
590                // max possible speed
591                value = 31;
592            }
593            SerialMessage m = new SerialMessage();
594            m.setOpCode(0xF8);
595    
596            if (value < 0) {
597                // System HALT (immediate stop; ALL)
598                m.putAsWord(0xFF8B);
599            } else {
600                // normal speed setting
601                m.putAsWord(0x0160 + address.getNumber() * 512 + value);
602            }
603    
604            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
605            tc.sendSerialMessage(m, null);
606            tc.sendSerialMessage(m, null);           
607        }
608
609        // send to layout option TMCC1 32 speed steps
610        if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) {
611
612            // TMCC1 32 speed step mode
613            int value = (int) (32 * speed);
614            if (value > 31) {
615                // max possible speed
616                value = 31;
617            }
618            SerialMessage m = new SerialMessage();
619            m.setOpCode(0xFE);
620    
621            if (value < 0) {
622                // System HALT (immediate stop; ALL)
623                m.putAsWord(0xFFFF);
624            } else {
625                // normal speed setting
626                m.putAsWord(0x0060 + address.getNumber() * 128 + value);
627            }
628    
629            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
630            tc.sendSerialMessage(m, null);
631            tc.sendSerialMessage(m, null);           
632        }
633                  
634        synchronized(this) {
635            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
636        }
637        record(speed);
638    }
639
640    /**
641     * {@inheritDoc}
642     */
643    @Override
644    public void setIsForward(boolean forward) {
645        boolean old = isForward;
646        isForward = forward;
647
648        // notify layout
649        SerialMessage m = new SerialMessage();
650        if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100) {
651            m.setOpCode(0xFE);
652            if (forward) {
653                m.putAsWord(0x0000 + address.getNumber() * 128);
654                setSpeedSetting(0.0f);
655            } else {
656                m.putAsWord(0x0003 + address.getNumber() * 128);
657                setSpeedSetting(0.0f);
658            }
659        }
660
661        if (speedStepMode == jmri.SpeedStepMode.TMCC2_32 || speedStepMode == jmri.SpeedStepMode.TMCC2_200) {
662            m.setOpCode(0xF8);
663            if (forward) {
664                m.putAsWord(0x0100 + address.getNumber() * 512);
665                setSpeedSetting(0.0f);
666            } else {
667                m.putAsWord(0x0103 + address.getNumber() * 512);
668                setSpeedSetting(0.0f);
669            }
670        }
671
672        // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
673        tc.sendSerialMessage(m, null);
674        tc.sendSerialMessage(m, null);
675
676        firePropertyChange(ISFORWARD, old, isForward);
677    }
678
679    /**
680     * Send these messages to the layout and repeat
681     * while button is pressed/on.
682     * @param value Content of message to be sent in three bytes
683     * @param func  The number of the function being addressed
684     */
685    protected void sendFnToLayout(int value, int func) {
686        
687        if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) {
688            if (func == 24) {
689                setSpeedSetting(0.055f);
690                return;
691            }
692            if (func == 25) {
693                setSpeedSetting(0.205f);
694                return;
695            }
696            if (func == 26) {
697                setSpeedSetting(0.355f);
698                return;
699            }
700            if (func == 27) {
701                setSpeedSetting(0.505f);
702                return;
703            }
704            if (func == 28) {
705                setSpeedSetting(0.705f);
706                return;
707            }
708            if (func == 29) {
709                setSpeedSetting(1.0f);
710                return;
711            }
712        }
713
714        if (speedStepMode == jmri.SpeedStepMode.TMCC1_32 || speedStepMode == jmri.SpeedStepMode.TMCC1_100 || speedStepMode == jmri.SpeedStepMode.TMCC2_32) {
715            if (func == 24) {
716                setSpeedSetting(0.130f);
717                return;
718            }
719            if (func == 25) {
720                setSpeedSetting(0.320f);
721                return;
722            }
723            if (func == 26) {
724                setSpeedSetting(0.450f);
725                return;
726            }
727            if (func == 27) {
728                setSpeedSetting(0.580f);
729                return;
730            }
731            if (func == 28) {
732                setSpeedSetting(0.775f);
733                return;
734            }
735            if (func == 29) {
736                setSpeedSetting(1.0f);
737                return;
738            }
739        }
740
741        /**
742        * This code sends FnKey presses to the command station. 
743        * Send once is set, per the need of TMCC multi-key commands that
744        * do not work when a specific command sequence is not followed.
745        * If these multi-key commands are integrated into single FnKeys,
746        * this "send" section can be converted back to "send twice" as
747        * the other send sequences througout tmcc\SerialThrottle.java.
748        */
749
750        repeatFunctionSendWhileOn(value, func); // Single FnKey Press, Single Send; FnKey Held, Repeats FnKey while pressed.
751    }
752
753    /**
754    * This code block is necessary to support the send repeats of
755    * the repeatFunctionSendWhileOn(value, func); code above.
756    * This code block "Sends Again" if FkKey is still pressed/on, and
757    * repeats per the interval set in static final int REPEAT_TIME.
758    */
759
760    static final int REPEAT_TIME = 150;
761
762    protected void repeatFunctionSendWhileOn(int value, int func) {
763        if (getFunction(func)) {
764            tc.sendSerialMessage(new SerialMessage(value), null);
765            jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> {
766                repeatFunctionSendWhileOn(value, func);
767            }, REPEAT_TIME);
768        }
769    }
770
771    /*
772     * Set the speed step value.
773     * <p>
774     * Only 32 steps is available
775     *
776     * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed
777     */
778    @Override
779    public void setSpeedStepMode(jmri.SpeedStepMode mode) {
780        if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) {
781            super.setSpeedStepMode(mode);
782        }
783    }
784
785    /**
786     * {@inheritDoc}
787     */
788    @Override
789    public void throttleDispose() {
790        finishRecord();
791    }
792
793}