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.TMCC_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    /**
051     * {@inheritDoc}
052     */
053    @Override
054    public void setFunction(int func, boolean value) {
055        updateFunction(func, value);
056        if (func>=0 && func < SERIAL_FUNCTION_CODES.length) {
057            if ( SERIAL_FUNCTION_CODES[func] > 0xFFFF ) {
058                // TMCC 2 format
059                sendToLayout(SERIAL_FUNCTION_CODES[func] + address.getNumber() * 512);
060            } else {
061                // TMCC 1 format
062                sendToLayout(SERIAL_FUNCTION_CODES[func] + address.getNumber() * 128);
063            }
064        }
065        else {
066            super.setFunction(func, value);
067        }
068    }
069
070    // Translate function number to line characters.
071    // If the upper byte is zero, it will be replaces by 0xF8
072    //    and the address will be set in the low position.
073    // If the upper byte is non-zero, that value will be sent,
074    //    and the address will be set in the upper (TMCC2) position.
075    private final static int[] SERIAL_FUNCTION_CODES = new int[] {
076        0x00000D, 0x00001D, 0x00001C, 0x000005, 0x000006, /* Fn0-4 */
077        0x000010, 0x000011, 0x000012, 0x000013, 0x000014, /* Fn5-9 */
078        0x000015, 0x000016, 0x000017, 0x000018, 0x000019, /* Fn10-14 */
079        0x000009, 0x00001E, 0x000000, 0x000003, 0x000001, /* Fn15-19 */
080        0x000004, 0x000007, 0x000047, 0x000042, 0x000028, /* Fn20-24 */
081        0x000029, 0x00002A, 0x00002B, /* 25-27 */
082        // start of TMCC 2 functions
083        0xF801FB, // Fn28 Start Up Sequence 1 (Delayed Prime Mover)
084        0xF801FC, // Fn29 Start Up Sequence 2 (Immediate Start Up)
085        0xF801FD, // Fn30 Shut Down Sequence 1 (Delay w/ Announcement)
086        0xF801FE, // Fn31 Shut down Sequence 2 (Immediate Shut Down)
087        0xF90000, // Fn32
088        0xF90000, // Fn33
089        0xF90000, // Fn34
090        0xF90000, // Fn35
091        0xF90000, // Fn36
092        0xF90000, // Fn37
093        0xF90000, // Fn38
094        0xF90000, // Fn39
095        0xF90000, // Fn40
096        0xF90000, // Fn41
097        0xF90000, // Fn42
098        0xF90000, // Fn43
099        0xF90000, // Fn44
100        0xF90000, // Fn45
101        0xF90000, // Fn46
102        0xF90000, // Fn47
103        0xF90000, // Fn48
104        0xF90000, // Fn49
105        0xF90000, // Fn50
106        0xF90000, // Fn51
107        0xF90000, // Fn52
108        0xF90000, // Fn53
109        0xF90000, // Fn54
110        0xF90000, // Fn55
111        0xF90000, // Fn56
112        0xF90000, // Fn57
113        0xF90000, // Fn58
114        0xF90000, // Fn59
115        0xF90000, // Fn60
116        0xF90000, // Fn61
117        0xF90000, // Fn62
118        0xF90000, // Fn63
119        0xF90000, // Fn64
120        0xF90000, // Fn65
121        0xF90000, // Fn66
122        0xF90000, // Fn67
123    };
124
125    /**
126     * Set the speed.
127     *
128     * @param speed Number from 0 to 1; less than zero is emergency stop
129     */
130    @Override
131    public void setSpeedSetting(float speed) {
132        float oldSpeed;
133        synchronized(this) {
134            oldSpeed = this.speedSetting;
135            this.speedSetting = speed;
136        }
137        int value = (int) (32 * speed);     // -1 for rescale to avoid estop
138        if (value > 31) {
139            value = 31;    // max possible speed
140        }
141        SerialMessage m = new SerialMessage();
142
143        if (value < 0) {
144            // immediate stop
145            m.putAsWord(0x0060 + address.getNumber() * 128 + 0);
146        } else {
147            // normal speed setting
148            m.putAsWord(0x0060 + address.getNumber() * 128 + value);
149        }
150
151        tc.sendSerialMessage(m, null);
152        tc.sendSerialMessage(m, null);
153        tc.sendSerialMessage(m, null);
154        tc.sendSerialMessage(m, null);
155        synchronized(this) {
156            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
157        }
158        record(speed);
159    }
160
161    /**
162     * {@inheritDoc}
163     */
164    @Override
165    public void setIsForward(boolean forward) {
166        boolean old = isForward;
167        isForward = forward;
168
169        // notify layout
170        SerialMessage m = new SerialMessage();
171        if (forward) {
172            m.putAsWord(0x0000 + address.getNumber() * 128);
173        } else {
174            m.putAsWord(0x0003 + address.getNumber() * 128);
175        }
176        tc.sendSerialMessage(m, null);
177        tc.sendSerialMessage(m, null);
178        tc.sendSerialMessage(m, null);
179        tc.sendSerialMessage(m, null);
180        firePropertyChange(ISFORWARD, old, isForward);
181    }
182
183    /**
184     * Send these messages to the layout four times
185     * to make sure they're accepted.
186     * @param value Content of message to be sent in three bytes
187     */
188    protected void sendToLayout(int value) {
189        tc.sendSerialMessage(new SerialMessage(value), null);
190        tc.sendSerialMessage(new SerialMessage(value), null);
191        tc.sendSerialMessage(new SerialMessage(value), null);
192        tc.sendSerialMessage(new SerialMessage(value), null);
193    }
194
195    /*
196     * Set the speed step value.
197     * <p>
198     * Only 32 steps is available
199     *
200     * @param Mode ignored, as only 32 is valid
201     */
202    @Override
203    public void setSpeedStepMode(jmri.SpeedStepMode Mode) {
204    }
205
206    /**
207     * {@inheritDoc}
208     */
209    @Override
210    public void throttleDispose() {
211        finishRecord();
212    }
213
214}