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 */
016public class SerialThrottle extends AbstractThrottle {
017
018    /**
019     * Constructor.
020     *
021     * @param memo the connected SerialTrafficController
022     * @param address Loco ID
023     */
024    public SerialThrottle(TmccSystemConnectionMemo memo, DccLocoAddress address) {
025        super(memo, 69); // supports 69 functions
026        tc = memo.getTrafficController();
027
028        // cache settings. It would be better to read the
029        // actual state, but I don't know how to do this
030        synchronized(this) {
031            this.speedSetting = 0;
032        }
033        // Functions default to false
034        this.address = address;
035        this.isForward = true;
036        this.speedStepMode = SpeedStepMode.TMCC1_32;
037    }
038
039    private final DccLocoAddress address;
040    private final SerialTrafficController tc;
041
042    /**
043     * {@inheritDoc}
044     */
045    @Override
046    public LocoAddress getLocoAddress() {
047        return address;
048    }
049
050    // Relating SERIAL_FUNCTION_CODES_TMCC1 to SpeedStepMode.TMCC1_32 and TMCC1_100;
051    //    and SERIAL_FUNCTION_CODES_TMCC2 to SpeedStepMode.TMCC2_32 and TMCC2_200.
052    private long getFnValue(int number) {
053                if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC1_100) {
054                    if (number < SERIAL_FUNCTION_CODES_TMCC1.length) {
055                        return SERIAL_FUNCTION_CODES_TMCC1[number];
056                    } else {
057                        return 0;
058                    }
059                } else if (getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_32 || getSpeedStepMode() == jmri.SpeedStepMode.TMCC2_200) {
060                     if (number < SERIAL_FUNCTION_CODES_TMCC2.length) {
061                         return SERIAL_FUNCTION_CODES_TMCC2[number];
062                    } else {
063                        return 0;
064                    }
065                }
066            return 0;
067        }
068
069
070    /**
071     * {@inheritDoc}
072     */
073    @Override
074    public void setFunction(int func, boolean value) {
075        updateFunction(func, value);
076        if (func>=0 && func < SERIAL_FUNCTION_CODES_TMCC1.length) {
077            if ( getFnValue(func) > 0xFFFF ) {
078                // TMCC 2 format
079                if (getFnValue(func) > 0xFFFFFF ) {
080                    int first =  (int)(getFnValue(func) >> 24);
081                    int second = (int)(getFnValue(func) & 0xFFFFFF);
082                    // doubles are only sent once, not repeating
083                    sendOneWordOnce(first  + address.getNumber() * 512);
084                    sendOneWordOnce(second + address.getNumber() * 512);
085                } else {
086                    // single message
087                    sendFnToLayout((int)getFnValue(func) + address.getNumber() * 512, func);
088                    }
089            } else {
090                // TMCC 1 format
091                sendFnToLayout((int)getFnValue(func) + address.getNumber() * 128, func);
092                }
093        } else {
094            super.setFunction(func, value);
095            }
096    }
097
098    // the argument is a long containing 3 bytes. 
099    // The first byte is the message opcode
100    private void sendOneWordOnce(int word) {
101        SerialMessage m = new SerialMessage(word);
102        tc.sendSerialMessage(m, null);
103    }
104
105    // TMCC 1 Function Keys to trigger with TMCC1_32 and TMCC1_100 speed steps.
106    private final static long[] SERIAL_FUNCTION_CODES_TMCC1 = new long[] {
107        0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */
108        0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */
109        0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */
110        0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */
111        0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */
112        0x000029, 0x00002A, 0x00002B, 0x00001F, /* 25-28 */
113        
114        0xF901FF, // Fn29
115        0xF901FF, // Fn30
116        0xF901FF, // Fn31
117        0xF901FF, // Fn32
118        0xF901FF, // Fn33
119        0xF901FF, // Fn34
120        0xF901FF, // Fn35
121        0xF901FF, // Fn36
122        0xF901FF, // Fn37
123        0xF901FF, // Fn38
124        0xF901FF, // Fn39
125        0xF901FF, // Fn40
126        0xF901FF, // Fn41
127        0xF901FF, // Fn42
128        0xF901FF, // Fn43
129        0xF901FF, // Fn44
130        0xF901FF, // Fn45
131        0xF901FF, // Fn46
132        0xF901FF, // Fn47
133        0xF901FF, // Fn48
134        0xF901FF, // Fn49
135        0xF901FF, // Fn50
136        0xF901FF, // Fn51
137        0xF901FF, // Fn52
138        0xF901FF, // Fn53
139        0xF901FF, // Fn54
140        0xF901FF, // Fn55
141        0xF901FF, // Fn56
142        0xF901FF, // Fn57
143        0xF901FF, // Fn58
144        0xF901FF, // Fn59
145        0xF901FF, // Fn60
146        0xF901FF, // Fn61
147        0xF901FF, // Fn62
148        0xF901FF, // Fn63
149        0xF901FF, // Fn64
150        0xF901FF, // Fn65
151        0xF901FF, // Fn66
152        0xF901FF, // Fn67
153        0xF901FF, // Fn68
154    };
155
156    // Translate TMCC1 function numbers to line characters.
157    // If the upper byte is zero, it will be replaced by 0xF8
158    //    and the address will be set in the low position.
159    // If the upper byte is non-zero, that value will be sent,
160    //    and the address will be set in the upper (TMCC2) position.
161    // If six bytes are specified (with the upper one non-zero), 
162    //    this will be interpreted as two commands to be sequentially sent,
163    //    with the upper bytes sent first.
164
165    // TMCC 2 Legacy Function Keys to trigger with TMCC2_32 and TMCC2_200 speed steps.
166    private final static long[] SERIAL_FUNCTION_CODES_TMCC2 = new long[] {
167        0xF8010D, 0xF8011D, 0xF8011C, 0xF80105, 0xF80106, /* Fn0-4 */
168        0xF80110, 0xF80111, 0xF80112, 0xF80113, 0xF80114, /* Fn5-9 */
169        0xF80115, 0xF80116, 0xF80117, 0xF80118, 0xF80119, /* Fn10-14 */
170        0xF80109, 0xF8011E, 0xF80100, 0xF80103, 0xF80101, /* Fn15-19 */
171        0xF80104, 0xF80107, 0xF80147, 0xF80142, 0xF80128, /* Fn20-24 */
172        0xF80129, 0xF8012A, 0xF8012B, 0xF8011F, /* 25-28 */
173        
174        0xF80108, // Fn29
175        0xF8010A, // Fn30
176        0xF8010B, // Fn31
177        0xF8010C, // Fn32
178        0xF8010E, // Fn33
179        0xF8010F, // Fn34
180        
181        0xF801FBF801FCL, // Fn35 Start Up Sequence 1 (Delayed Prime Mover, then Immediate Start Up)
182        0xF801FC, // Fn36 Start Up Sequence 2 (Immediate Start Up)
183        0xF801FDF801FEL, // Fn37 Shut Down Sequence 1 (Delay w/ Announcement then Immediate Shut Down)
184        0xF801FE, // Fn38 Shut down Sequence 2 (Immediate Shut Down)
185
186        0xF901FF, // Fn39
187        0xF901FF, // Fn40
188        0xF901FF, // Fn41
189        0xF901FF, // Fn42
190        0xF901FF, // Fn43
191        0xF901FF, // Fn44
192        0xF901FF, // Fn45
193        0xF901FF, // Fn46
194        0xF901FF, // Fn47
195        0xF901FF, // Fn48
196        0xF901FF, // Fn49
197        0xF901FF, // Fn50
198        0xF901FF, // Fn51
199        0xF901FF, // Fn52
200        0xF901FF, // Fn53
201        0xF901FF, // Fn54
202        0xF901FF, // Fn55
203        0xF901FF, // Fn56
204        0xF901FF, // Fn57
205        0xF901FF, // Fn58
206        0xF901FF, // Fn59
207        0xF901FF, // Fn60
208        0xF901FF, // Fn61
209        0xF901FF, // Fn62
210        0xF901FF, // Fn63
211        0xF901FF, // Fn64
212        0xF901FF, // Fn65
213        0xF901FF, // Fn66
214        0xF901FF, // Fn67
215        0xF901FF, // Fn68
216    };
217
218    /**
219     * Set the speed.
220     *
221     * @param speed Number from 0 to 1; less than zero is emergency stop
222     */
223    @Override
224    public void setSpeedSetting(float speed) {
225        float oldSpeed;
226        synchronized(this) {
227            oldSpeed = this.speedSetting;
228            this.speedSetting = speed;
229        }
230        
231        // send to layout option 200 speed steps
232        if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) {
233
234            // TMCC2 Legacy 200 speed step mode
235            int value = (int) (199 * speed); // max value to send is 199 in 200 step mode
236            if (value > 199) {
237                // max possible speed
238                value = 199;
239            }
240            SerialMessage m = new SerialMessage();
241            m.setOpCode(0xF8);
242    
243            if (value < 1) {
244                // immediate stop
245                m.putAsWord(0x0000 + (address.getNumber() << 9) + 0);
246            } else {
247                // normal speed setting
248                m.putAsWord(0x0000 + (address.getNumber() << 9) + value);
249            }
250            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
251            tc.sendSerialMessage(m, null);
252            tc.sendSerialMessage(m, null);
253        }
254
255        // send to layout option 100 speed steps
256        if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) {
257            
258          /** 
259            * TMCC1 ERR 100 speed step mode
260            * purpose is to increase resolution of 32 bits
261            * across 100 throttle 'clicks' by dividing value by 3            
262            * and setting top speed at 32
263          */
264            int value = (int) (99 * speed); // max value to send is 99 in 100 step mode
265            if (value > 93) {
266                // max possible speed step
267                value = 93;
268            }
269            SerialMessage m = new SerialMessage();
270            m.setOpCode(0xFE);
271
272            if (value < 1) {
273                // immediate stop
274                m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
275            }
276            if (value > 0) {
277                // normal speed step setting
278                m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3);
279            }
280                            
281            // send to command station (send once for maximum efficiency; add extra sends if layout not responding with only one send)
282            tc.sendSerialMessage(m, null);
283        }
284
285        // send to layout option TMCC2 32 speed steps
286        if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) {
287
288            // TMCC2 Legacy 32 speed step mode
289            int value = (int) (32 * speed);
290            if (value > 31) {
291                // max possible speed
292                value = 31;
293            }
294            SerialMessage m = new SerialMessage();
295            m.setOpCode(0xF8);
296    
297            if (value < 1) {
298                // immediate stop
299                m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
300            } else {
301                // normal speed setting
302                m.putAsWord(0x0060 + address.getNumber() * 128 + value);
303            }
304    
305            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
306            tc.sendSerialMessage(m, null);
307            tc.sendSerialMessage(m, null);           
308        }
309
310        // send to layout option TMCC1 32 speed steps
311        if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) {
312
313            // TMCC1 32 speed step mode
314            int value = (int) (32 * speed);
315            if (value > 31) {
316                // max possible speed
317                value = 31;
318            }
319            SerialMessage m = new SerialMessage();
320            m.setOpCode(0xFE);
321    
322            if (value < 1) {
323                // immediate stop
324                m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
325            } else {
326                // normal speed setting
327                m.putAsWord(0x0060 + address.getNumber() * 128 + value);
328            }
329    
330            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
331            tc.sendSerialMessage(m, null);
332            tc.sendSerialMessage(m, null);           
333        }
334                  
335        synchronized(this) {
336            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
337        }
338        record(speed);
339    }
340
341    /**
342     * {@inheritDoc}
343     */
344    @Override
345    public void setIsForward(boolean forward) {
346        boolean old = isForward;
347        isForward = forward;
348
349        // notify layout
350        SerialMessage m = new SerialMessage();
351        if (forward) {
352            m.putAsWord(0x0000 + address.getNumber() * 128);
353        } else {
354            m.putAsWord(0x0003 + address.getNumber() * 128);
355        }
356        tc.sendSerialMessage(m, null);
357        tc.sendSerialMessage(m, null);
358        tc.sendSerialMessage(m, null);
359        tc.sendSerialMessage(m, null);
360        firePropertyChange(ISFORWARD, old, isForward);
361    }
362
363    /**
364     * Send these messages to the layout and repeat
365     * while button is on.
366     * @param value Content of message to be sent in three bytes
367     * @param func  The number of the function being addressed
368     */
369    protected void sendFnToLayout(int value, int func) {
370    /**
371     * Commenting out these repeat send lines in case it is
372     * necessary to reinstate them after testing. These are
373     * holdovers from the original "repeat 4 times to make
374     * sure they're accepted" instructions.
375     */
376        // tc.sendSerialMessage(new SerialMessage(value), null);
377        // tc.sendSerialMessage(new SerialMessage(value), null);
378        // tc.sendSerialMessage(new SerialMessage(value), null);     
379    
380        repeatFunctionSendWhileOn(value, func); // 4th send is here
381    }
382
383    static final int REPEAT_TIME = 150;
384
385    protected void repeatFunctionSendWhileOn(int value, int func) {
386        // Send again if function is still on and repeat in a short while
387        if (getFunction(func)) {
388            tc.sendSerialMessage(new SerialMessage(value), null);
389            jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> {
390                repeatFunctionSendWhileOn(value, func);
391            }, REPEAT_TIME);
392        }
393    }
394
395    /*
396     * Set the speed step value.
397     * <p>
398     * Only 32 steps is available
399     *
400     * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed
401     */
402    @Override
403    public void setSpeedStepMode(jmri.SpeedStepMode mode) {
404        if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) {
405            super.setSpeedStepMode(mode);
406        }
407    }
408
409    /**
410     * {@inheritDoc}
411     */
412    @Override
413    public void throttleDispose() {
414        finishRecord();
415    }
416
417}