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