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        0xF8012E, // Fn29
115        0xF8012E, // Fn30
116        0xF8012E, // Fn31
117        0xF8012E, // Fn32
118        0xF8012E, // Fn33
119        0xF8012E, // Fn34
120        0xF8012E, // Fn35
121        0xF8012E, // Fn36
122        0xF8012E, // Fn37
123        0xF8012E, // Fn38
124        0xF8012E, // Fn39
125        0xF8012E, // Fn40
126        0xF8012E, // Fn41
127        0xF8012E, // Fn42
128        0xF8012E, // Fn43
129        0xF8012E, // Fn44
130        0xF8012E, // Fn45
131        0xF8012E, // Fn46
132        0xF8012E, // Fn47
133        0xF8012E, // Fn48
134        0xF8012E, // Fn49
135        0xF8012E, // Fn50
136        0xF8012E, // Fn51
137        0xF8012E, // Fn52
138        0xF8012E, // Fn53
139        0xF8012E, // Fn54
140        0xF8012E, // Fn55
141        0xF8012E, // Fn56
142        0xF8012E, // Fn57
143        0xF8012E, // Fn58
144        0xF8012E, // Fn59
145        0xF8012E, // Fn60
146        0xF8012E, // Fn61
147        0xF8012E, // Fn62
148        0xF8012E, // Fn63
149        0xF8012E, // Fn64
150        0xF8012E, // Fn65
151        0xF8012E, // Fn66
152        0xF8012E, // Fn67
153        0xF8012E, // 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        0xF8012E, // Fn35
187        0xF8012E, // Fn36
188        0xF8012E, // Fn37
189        0xF8012E, // Fn38
190        0xF8012E, // Fn39
191        0xF8012E, // Fn40
192        0xF8012E, // Fn41
193        0xF8012E, // Fn42
194        0xF8012E, // Fn43
195        0xF8012E, // Fn44
196        0xF8012E, // Fn45
197        0xF8012E, // Fn46
198        0xF8012E, // Fn47
199        0xF8012E, // Fn48
200        0xF8012E, // Fn49
201        0xF8012E, // Fn50
202        0xF8012E, // Fn51
203        0xF8012E, // Fn52
204        0xF8012E, // Fn53
205        0xF8012E, // Fn54
206        0xF8012E, // Fn55
207        0xF8012E, // Fn56
208        0xF8012E, // Fn57
209        0xF8012E, // Fn58
210        0xF8012E, // Fn59
211        0xF8012E, // Fn60
212        0xF8012E, // Fn61
213        0xF8012E, // Fn62
214        0xF8012E, // Fn63
215        0xF8012E, // Fn64
216        0xF8012E, // Fn65
217        0xF8012E, // Fn66
218        0xF8012E, // Fn67
219        0xF8012E, // Fn68
220    };
221
222    /**
223     * Set the speed.
224     *
225     * @param speed Number from 0 to 1; less than zero is emergency stop
226     */
227    @Override
228    public void setSpeedSetting(float speed) {
229        float oldSpeed;
230        synchronized(this) {
231            oldSpeed = this.speedSetting;
232            this.speedSetting = speed;
233        }
234        
235        // send to layout option 200 speed steps
236        if (speedStepMode == jmri.SpeedStepMode.TMCC2_200) {
237
238            // TMCC2 Legacy 200 speed step mode
239            int value = (int) (199 * speed); // max value to send is 199 in 200 step mode
240            if (value > 199) {
241                // max possible speed
242                value = 199;
243            }
244            SerialMessage m = new SerialMessage();
245            m.setOpCode(0xF8);
246    
247            if (value < 1) {
248                // immediate stop
249                m.putAsWord(0x0000 + (address.getNumber() << 9) + 0);
250            } else {
251                // normal speed setting
252                m.putAsWord(0x0000 + (address.getNumber() << 9) + value);
253            }
254            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
255            tc.sendSerialMessage(m, null);
256            tc.sendSerialMessage(m, null);
257        }
258
259        // send to layout option 100 speed steps
260        if (speedStepMode == jmri.SpeedStepMode.TMCC1_100) {
261            
262          /** 
263            * TMCC1 ERR 100 speed step mode
264            * purpose is to increase resolution of 32 bits
265            * across 100 throttle 'clicks' by dividing value by 3            
266            * and setting top speed at 32
267          */
268            int value = (int) (99 * speed); // max value to send is 99 in 100 step mode
269            if (value > 93) {
270                // max possible speed step
271                value = 93;
272            }
273            SerialMessage m = new SerialMessage();
274            m.setOpCode(0xFE);
275
276            if (value < 1) {
277                // immediate stop
278                m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
279            }
280            if (value > 0) {
281                // normal speed step setting
282                m.putAsWord(0x0060 + address.getNumber() * 128 + value / 3);
283            }
284                            
285            // send to command station (send once for maximum efficiency; add extra sends if layout not responding with only one send)
286            tc.sendSerialMessage(m, null);
287        }
288
289        // send to layout option TMCC2 32 speed steps
290        if (speedStepMode == jmri.SpeedStepMode.TMCC2_32) {
291
292            // TMCC2 Legacy 32 speed step mode
293            int value = (int) (32 * speed);
294            if (value > 31) {
295                // max possible speed
296                value = 31;
297            }
298            SerialMessage m = new SerialMessage();
299            m.setOpCode(0xF8);
300    
301            if (value < 1) {
302                // immediate stop
303                m.putAsWord(0x0160 + address.getNumber() * 512 + 0);
304            } else {
305                // normal speed setting
306                m.putAsWord(0x0160 + address.getNumber() * 512 + value);
307            }
308    
309            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
310            tc.sendSerialMessage(m, null);
311            tc.sendSerialMessage(m, null);           
312        }
313
314        // send to layout option TMCC1 32 speed steps
315        if (speedStepMode == jmri.SpeedStepMode.TMCC1_32) {
316
317            // TMCC1 32 speed step mode
318            int value = (int) (32 * speed);
319            if (value > 31) {
320                // max possible speed
321                value = 31;
322            }
323            SerialMessage m = new SerialMessage();
324            m.setOpCode(0xFE);
325    
326            if (value < 1) {
327                // immediate stop
328                m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
329            } else {
330                // normal speed setting
331                m.putAsWord(0x0060 + address.getNumber() * 128 + value);
332            }
333    
334            // send to command station (send twice is set, but number of sends may need to be adjusted depending on efficiency)
335            tc.sendSerialMessage(m, null);
336            tc.sendSerialMessage(m, null);           
337        }
338                  
339        synchronized(this) {
340            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
341        }
342        record(speed);
343    }
344
345    /**
346     * {@inheritDoc}
347     */
348    @Override
349    public void setIsForward(boolean forward) {
350        boolean old = isForward;
351        isForward = forward;
352
353        // notify layout
354        SerialMessage m = new SerialMessage();
355        if (forward) {
356            m.putAsWord(0x0000 + address.getNumber() * 128);
357        } else {
358            m.putAsWord(0x0003 + address.getNumber() * 128);
359        }
360        tc.sendSerialMessage(m, null);
361        tc.sendSerialMessage(m, null);
362        tc.sendSerialMessage(m, null);
363        tc.sendSerialMessage(m, null);
364        firePropertyChange(ISFORWARD, old, isForward);
365    }
366
367    /**
368     * Send these messages to the layout and repeat
369     * while button is on.
370     * @param value Content of message to be sent in three bytes
371     * @param func  The number of the function being addressed
372     */
373    protected void sendFnToLayout(int value, int func) {
374    /**
375     * Commenting out these repeat send lines in case it is
376     * necessary to reinstate them after testing. These are
377     * holdovers from the original "repeat 4 times to make
378     * sure they're accepted" instructions.
379     */
380        // tc.sendSerialMessage(new SerialMessage(value), null);
381        // tc.sendSerialMessage(new SerialMessage(value), null);
382        // tc.sendSerialMessage(new SerialMessage(value), null);     
383    
384        repeatFunctionSendWhileOn(value, func); // 4th send is here
385    }
386
387    static final int REPEAT_TIME = 150;
388
389    protected void repeatFunctionSendWhileOn(int value, int func) {
390        // Send again if function is still on and repeat in a short while
391        if (getFunction(func)) {
392            tc.sendSerialMessage(new SerialMessage(value), null);
393            jmri.util.ThreadingUtil.runOnLayoutDelayed(() -> {
394                repeatFunctionSendWhileOn(value, func);
395            }, REPEAT_TIME);
396        }
397    }
398
399    /*
400     * Set the speed step value.
401     * <p>
402     * Only 32 steps is available
403     *
404     * @param mode only TMCC1 32, TMCC2 32, TMCC1 100 and TMCC2 200 are allowed
405     */
406    @Override
407    public void setSpeedStepMode(jmri.SpeedStepMode mode) {
408        if (mode == jmri.SpeedStepMode.TMCC1_32 || mode == jmri.SpeedStepMode.TMCC2_32 || mode == jmri.SpeedStepMode.TMCC1_100 || mode == jmri.SpeedStepMode.TMCC2_200) {
409            super.setSpeedStepMode(mode);
410        }
411    }
412
413    /**
414     * {@inheritDoc}
415     */
416    @Override
417    public void throttleDispose() {
418        finishRecord();
419    }
420
421}