001package jmri.jmrix.srcp;
002
003import jmri.DccLocoAddress;
004import jmri.LocoAddress;
005import jmri.SpeedStepMode;
006import jmri.Throttle;
007import jmri.jmrix.AbstractThrottle;
008
009/**
010 * An implementation of DccThrottle with code specific to an SRCP connection.
011 * <p>
012 * Addresses of 99 and below are considered short addresses, and over 100 are
013 * considered long addresses. This is not the NCE system standard, but is used
014 * as an expedient here.
015 *
016 * @author Bob Jacobsen Copyright (C) 2001,2008
017 */
018public class SRCPThrottle extends AbstractThrottle {
019
020    /**
021     * Constructor.
022     *
023     * @param memo    the memo containing the connection
024     * @param address the address to use
025     */
026    public SRCPThrottle(SRCPBusConnectionMemo memo, DccLocoAddress address) {
027        // default to 128 speed steps with 28 functions and NMRA protocl.
028        this(memo, address, "N", SpeedStepMode.NMRA_DCC_128, 28);
029    }
030
031    public SRCPThrottle(SRCPBusConnectionMemo memo, DccLocoAddress address,
032            String protocol, SpeedStepMode mode, int functions) {
033        super(memo);
034        if (!protocol.equals("N")) {
035            throw new IllegalArgumentException("Protocol " + protocol + " not supported");
036        }
037        setSpeedStepMode(mode);
038
039        bus = memo.getBus();
040
041        // cache settings. It would be better to read the
042        // actual state, but I don't know how to do this
043        synchronized(this) {
044            this.speedSetting = 0;
045        }
046        // Functions default to false
047        this.address = address;
048        this.isForward = true;
049
050        // send allocation message
051        String msg = "INIT " + bus + " GL "
052                + (address.getNumber())
053                + " " + protocol + " "
054                + (address.isLongAddress() ? " 2 " : " 1 ")
055                + maxsteps + " "
056                + functions + "\n";
057        memo.getTrafficController()
058                .sendSRCPMessage(new SRCPMessage(msg), null);
059    }
060
061    /**
062     * Send the message to set the state of functions F0, F1, F2, F3, F4.
063     */
064    @Override
065    protected void sendFunctionGroup1() {
066        sendUpdate();
067    }
068
069    /**
070     * Send the message to set the state of functions F5, F6, F7, F8.
071     */
072    @Override
073    protected void sendFunctionGroup2() {
074        sendUpdate();
075    }
076
077    /**
078     * Send the message to set the state of functions F9, F10, F11, F12.
079     */
080    @Override
081    protected void sendFunctionGroup3() {
082        sendUpdate();
083    }
084
085    /**
086     * Send the message to set the state of functions F13, F14, F15, F16, F17,
087     * F18, F19, and F20.
088     */
089    @Override
090    protected void sendFunctionGroup4() {
091        sendUpdate();
092    }
093
094    /**
095     * Send the message to set the state of functions F21, F22, F23, F24, F25,
096     * F26, F27 and F28.
097     */
098    @Override
099    protected void sendFunctionGroup5() {
100        sendUpdate();
101    }
102
103    /**
104     * Set the speed and direction.
105     * <p>
106     * This intentionally skips the emergency stop value of 1.
107     *
108     * @param speed Number from 0 to 1; less than zero is emergency stop
109     */
110    @Override
111    public synchronized void setSpeedSetting(float speed) {
112        float oldSpeed = this.speedSetting;
113        this.speedSetting = speed;
114        sendUpdate();
115        firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
116        record(speed);
117    }
118
119    @Override
120    public void setIsForward(boolean forward) {
121        boolean old = isForward;
122        isForward = forward;
123        sendUpdate();
124        firePropertyChange(Throttle.ISFORWARD, old, isForward);
125    }
126
127    private DccLocoAddress address;
128    private int bus;
129    private int maxsteps;
130
131    /**
132     * Send the complete status
133     */
134    void sendUpdate() {
135        String msg = "SET " + bus + " GL ";
136
137        int outSpeed;
138        
139        synchronized(this) {
140            outSpeed = Math.round(speedSetting * maxsteps);
141            if (speedSetting > 0 && outSpeed == 0) {
142                outSpeed = 1;       //  ensure non-zero input results in non-zero output
143            }
144        }
145        
146        // address
147        msg += (address.getNumber());
148
149        // direction and speed
150        synchronized(this) {
151            if (speedSetting >= 0) {
152                msg += (isForward ? " 1" : " 0");
153            }
154            else {
155                msg += " 2";        // handle emergency stop
156            }
157        }
158        msg += " " + outSpeed;
159        msg += " ";
160        msg += maxsteps;
161
162        // now add the functions
163        msg += getFunction(0) ? " 1" : " 0";
164        msg += getFunction(1) ? " 1" : " 0";
165        msg += getFunction(2) ? " 1" : " 0";
166        msg += getFunction(3) ? " 1" : " 0";
167        msg += getFunction(4) ? " 1" : " 0";
168        msg += getFunction(5) ? " 1" : " 0";
169        msg += getFunction(6) ? " 1" : " 0";
170        msg += getFunction(7) ? " 1" : " 0";
171        msg += getFunction(8) ? " 1" : " 0";
172        msg += getFunction(9) ? " 1" : " 0";
173        msg += getFunction(10) ? " 1" : " 0";
174        msg += getFunction(11) ? " 1" : " 0";
175        msg += getFunction(12) ? " 1" : " 0";
176        msg += getFunction(13) ? " 1" : " 0";
177        msg += getFunction(14) ? " 1" : " 0";
178        msg += getFunction(15) ? " 1" : " 0";
179        msg += getFunction(16) ? " 1" : " 0";
180        msg += getFunction(17) ? " 1" : " 0";
181        msg += getFunction(18) ? " 1" : " 0";
182        msg += getFunction(19) ? " 1" : " 0";
183        msg += getFunction(20) ? " 1" : " 0";
184        msg += getFunction(21) ? " 1" : " 0";
185        msg += getFunction(22) ? " 1" : " 0";
186        msg += getFunction(23) ? " 1" : " 0";
187        msg += getFunction(24) ? " 1" : " 0";
188        msg += getFunction(25) ? " 1" : " 0";
189        msg += getFunction(26) ? " 1" : " 0";
190        msg += getFunction(27) ? " 1" : " 0";
191        msg += getFunction(28) ? " 1" : " 0";
192
193        // send the result
194        SRCPMessage m = new SRCPMessage(msg + "\n");
195
196        ((SRCPBusConnectionMemo) adapterMemo).getTrafficController().sendSRCPMessage(m, null);
197    }
198
199    @Override
200    public void setSpeedStepMode(SpeedStepMode Mode) {
201        super.setSpeedStepMode(Mode);
202        switch (Mode) {
203            case NMRA_DCC_14:
204            case NMRA_DCC_27:
205            case NMRA_DCC_28:
206            case NMRA_DCC_128:
207                maxsteps = Mode.numSteps;
208                break;
209            default:
210                maxsteps = 126;
211                break;
212        }
213    }
214
215    @Override
216    public LocoAddress getLocoAddress() {
217        return address;
218    }
219
220    @Override
221    public void throttleDispose() {
222        finishRecord();
223    }
224
225}