001package jmri.jmrix.sprog;
002
003import jmri.DccLocoAddress;
004import jmri.LocoAddress;
005import jmri.SpeedStepMode;
006import jmri.jmrix.AbstractThrottle;
007
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * An implementation of DccThrottle with code specific to a SPROG Command
013 * Station connection.
014 * <p>
015 * Updated by Andrew Crosland February 2012 to enable 28 step speed packets
016 *
017 * @author Andrew Crosland Copyright (C) 2006, 2012
018 */
019public class SprogCSThrottle extends AbstractThrottle {
020
021    /**
022     * Constructor.
023     * @param memo system connection.
024     * @param address Loco Address.
025     */
026    public SprogCSThrottle(SprogSystemConnectionMemo memo, LocoAddress address) {
027        super(memo);
028        
029        if (address instanceof DccLocoAddress) {
030            this.address = ((DccLocoAddress) address);
031        }
032        else {
033            log.error("{} is not a DccLocoAddress",address);
034        }
035
036        // cache settings.
037        synchronized(this) {
038            this.speedSetting = 0;
039        }
040        // Functions default to false
041        this.isForward = true;
042
043        //@TODO - this needs a little work. Current implementation looks like it
044        //should support other modes, but doesn't in practice.  
045        //@see AbstractThrottleManager.supportedSpeedModes()
046        // Find our command station
047        if ((memo != null) && (memo.get(jmri.CommandStation.class) != null)) {
048            commandStation = memo.get(jmri.CommandStation.class);
049        } else {
050            commandStation = (SprogCommandStation) jmri.InstanceManager.getNullableDefault(jmri.CommandStation.class);
051        }
052
053    }
054
055    private final SprogCommandStation commandStation;
056
057    DccLocoAddress address;
058
059    @Override
060    public LocoAddress getLocoAddress() {
061        return address;
062    }
063
064    /**
065     * Send the message to set the state of functions F0, F1, F2, F3, F4 by
066     * adding it to the S queue
067     */
068    @Override
069    protected void sendFunctionGroup1() {
070        commandStation.function0Through4Packet(address,
071                getF0(), getF0Momentary(),
072                getF1(), getF1Momentary(),
073                getF2(), getF2Momentary(),
074                getF3(), getF3Momentary(),
075                getF4(), getF4Momentary());
076
077    }
078
079    /**
080     * Send the message to set the state of functions F5, F6, F7, F8 by# adding
081     * it to the S queue
082     */
083    @Override
084    protected void sendFunctionGroup2() {
085        commandStation.function5Through8Packet(address,
086                getF5(), getF5Momentary(),
087                getF6(), getF6Momentary(),
088                getF7(), getF7Momentary(),
089                getF8(), getF8Momentary());
090    }
091
092    /**
093     * Send the message to set the state of functions F9, F10, F11, F12 by
094     * adding it to the S queue
095     */
096    @Override
097    protected void sendFunctionGroup3() {
098        commandStation.function9Through12Packet(address,
099                getF9(), getF9Momentary(),
100                getF10(), getF10Momentary(),
101                getF11(), getF11Momentary(),
102                getF12(), getF12Momentary());
103    }
104
105    /**
106     * Send the message to set the state of functions F13 - F20
107     * adding it to the S queue
108     */
109    @Override
110    protected void sendFunctionGroup4() {
111        commandStation.function13Through20Packet(address,
112                getF13(), getF13Momentary(),
113                getF14(), getF14Momentary(),
114                getF15(), getF15Momentary(),
115                getF16(), getF16Momentary(),
116                getF17(), getF17Momentary(),
117                getF18(), getF18Momentary(),
118                getF19(), getF19Momentary(),
119                getF20(), getF20Momentary());
120    }
121
122    /**
123     * Send the message to set the state of functions F21 - F28
124     * adding it to the S queue
125     */
126    @Override
127    protected void sendFunctionGroup5() {
128        commandStation.function21Through28Packet(address,
129                getF21(), getF21Momentary(),
130                getF22(), getF22Momentary(),
131                getF23(), getF23Momentary(),
132                getF24(), getF24Momentary(),
133                getF25(), getF25Momentary(),
134                getF26(), getF26Momentary(),
135                getF27(), getF27Momentary(),
136                getF28(), getF28Momentary());
137    }
138
139    /**
140     * Set the speed and direction.
141     * <p>
142     * This intentionally skips the emergency stop value of 1 in 128 step mode
143     * and the stop and estop values 1-3 in 28 step mode.
144     *
145     * @param speed Number from 0 to 1; less than zero is emergency stop
146     */
147    @Override
148    public synchronized void setSpeedSetting(float speed) {
149        SpeedStepMode mode = getSpeedStepMode();
150        if (mode == SpeedStepMode.NMRA_DCC_28) {
151            // 28 step mode speed commands are 
152            // stop, estop, stop, estop, 4, 5, ..., 31
153            float oldSpeed = this.speedSetting;
154            this.speedSetting = speed;
155            int value = (int) ((31 - 3) * speed);     // -1 for rescale to avoid estop
156            if (value > 0) {
157                value = value + 3;  // skip estopx2 and stop
158            }
159            if (value > 31) {
160                value = 31;      // max possible speed
161            }
162            if (value < 0) {
163                value = 1;        // emergency stop
164            }
165            commandStation.setSpeed(SpeedStepMode.NMRA_DCC_28, address, value, isForward);
166            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
167        } else {
168            // 128 step mode speed commands are
169            // stop, estop, 2, 3, ..., 127
170            float oldSpeed = this.speedSetting;
171            this.speedSetting = speed;
172            int value = (int) ((127 - 1) * speed);     // -1 for rescale to avoid estop
173            if (value > 0) {
174                value = value + 1;  // skip estop
175            }
176            if (value > 127) {
177                value = 127;    // max possible speed
178            }
179            if (value < 0) {
180                value = 1;        // emergency stop
181            }
182            commandStation.setSpeed(SpeedStepMode.NMRA_DCC_128, address, value, isForward);
183            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
184        }
185        record(speed);
186    }
187
188    @Override
189    public void setIsForward(boolean forward) {
190        boolean old = isForward;
191        isForward = forward;
192        synchronized(this) {
193            setSpeedSetting(speedSetting);  // Update the speed setting
194        }
195        firePropertyChange(ISFORWARD, old, isForward);
196    }
197
198    @Override
199    public void throttleDispose() {
200        active = false;
201        commandStation.release(address);
202        finishRecord();
203    }
204
205    private final static Logger log = LoggerFactory.getLogger(SprogCSThrottle.class);
206
207}