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, SprogConstants.MAX_FUNCTIONS);
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        this.speedStepMode = SpeedStepMode.NMRA_DCC_128;
043
044
045        //@TODO - this needs a little work. Current implementation looks like it
046        //should support other modes, but doesn't in practice.
047        //@see AbstractThrottleManager.supportedSpeedModes()
048        // Find our command station
049        if ((memo != null) && (memo.get(jmri.CommandStation.class) != null)) {
050            commandStation = (SprogCommandStation) memo.get(jmri.CommandStation.class);
051        } else {
052            commandStation = (SprogCommandStation) jmri.InstanceManager.getNullableDefault(jmri.CommandStation.class);
053        }
054
055    }
056
057    private final SprogCommandStation commandStation;
058
059    DccLocoAddress address;
060
061    @Override
062    public LocoAddress getLocoAddress() {
063        return address;
064    }
065
066    /**
067     * Send the message to set the state of functions F0, F1, F2, F3, F4 by
068     * adding it to the S queue
069     */
070    @Override
071    protected void sendFunctionGroup1() {
072        commandStation.function0Through4Packet(address,
073                getFunction(0), getFunctionMomentary(0),
074                getFunction(1), getFunctionMomentary(1),
075                getFunction(2), getFunctionMomentary(2),
076                getFunction(3), getFunctionMomentary(3),
077                getFunction(4), getFunctionMomentary(4));
078
079    }
080
081    /**
082     * Send the message to set the state of functions F5, F6, F7, F8 by# adding
083     * it to the S queue
084     */
085    @Override
086    protected void sendFunctionGroup2() {
087        commandStation.function5Through8Packet(address,
088                getFunction(5), getFunctionMomentary(5),
089                getFunction(6), getFunctionMomentary(6),
090                getFunction(7), getFunctionMomentary(7),
091                getFunction(8), getFunctionMomentary(8));
092    }
093
094    /**
095     * Send the message to set the state of functions F9, F10, F11, F12 by
096     * adding it to the S queue
097     */
098    @Override
099    protected void sendFunctionGroup3() {
100        commandStation.function9Through12Packet(address,
101                getFunction(9), getFunctionMomentary(9),
102                getFunction(10), getFunctionMomentary(10),
103                getFunction(11), getFunctionMomentary(11),
104                getFunction(12), getFunctionMomentary(12));
105    }
106
107    /**
108     * Send the message to set the state of functions F13 - F20
109     * adding it to the S queue
110     */
111    @Override
112    protected void sendFunctionGroup4() {
113        commandStation.function13Through20Packet(address,
114                getFunction(13), getFunctionMomentary(13),
115                getFunction(14), getFunctionMomentary(14),
116                getFunction(15), getFunctionMomentary(15),
117                getFunction(16), getFunctionMomentary(16),
118                getFunction(17), getFunctionMomentary(17),
119                getFunction(18), getFunctionMomentary(18),
120                getFunction(19), getFunctionMomentary(19),
121                getFunction(20), getFunctionMomentary(20));
122    }
123
124    /**
125     * Send the message to set the state of functions F21 - F28
126     * adding it to the S queue
127     */
128    @Override
129    protected void sendFunctionGroup5() {
130        commandStation.function21Through28Packet(address,
131                getFunction(21), getFunctionMomentary(21),
132                getFunction(22), getFunctionMomentary(22),
133                getFunction(23), getFunctionMomentary(23),
134                getFunction(24), getFunctionMomentary(24),
135                getFunction(25), getFunctionMomentary(25),
136                getFunction(26), getFunctionMomentary(26),
137                getFunction(27), getFunctionMomentary(27),
138                getFunction(28), getFunctionMomentary(28));
139    }
140
141    /**
142     * Send the message to set the state of functions F29 - F36
143     * adding it to the S queue
144     */
145    @Override
146    protected void sendFunctionGroup6() {
147        commandStation.function29Through36Packet(address,
148                getFunctionNoWarn(29), getFunctionMomentaryNoWarn(29),
149                getFunctionNoWarn(30), getFunctionMomentaryNoWarn(30),
150                getFunctionNoWarn(31), getFunctionMomentaryNoWarn(32),
151                getFunctionNoWarn(32), getFunctionMomentaryNoWarn(32),
152                getFunctionNoWarn(33), getFunctionMomentaryNoWarn(33),
153                getFunctionNoWarn(34), getFunctionMomentaryNoWarn(34),
154                getFunctionNoWarn(35), getFunctionMomentaryNoWarn(35),
155                getFunctionNoWarn(36), getFunctionMomentaryNoWarn(36));
156    }
157
158    /**
159     * Send the message to set the state of functions F37 - F44
160     * adding it to the S queue
161     */
162    @Override
163    protected void sendFunctionGroup7() {
164        commandStation.function37Through44Packet(address,
165                getFunctionNoWarn(37), getFunctionMomentaryNoWarn(37),
166                getFunctionNoWarn(38), getFunctionMomentaryNoWarn(38),
167                getFunctionNoWarn(39), getFunctionMomentaryNoWarn(39),
168                getFunctionNoWarn(40), getFunctionMomentaryNoWarn(40),
169                getFunctionNoWarn(41), getFunctionMomentaryNoWarn(41),
170                getFunctionNoWarn(42), getFunctionMomentaryNoWarn(42),
171                getFunctionNoWarn(43), getFunctionMomentaryNoWarn(43),
172                getFunctionNoWarn(44), getFunctionMomentaryNoWarn(44));
173    }
174
175    /**
176     * Send the message to set the state of functions F45 - F52
177     * adding it to the S queue
178     */
179    @Override
180    protected void sendFunctionGroup8() {
181        commandStation.function45Through52Packet(address,
182                getFunctionNoWarn(45), getFunctionMomentaryNoWarn(45),
183                getFunctionNoWarn(46), getFunctionMomentaryNoWarn(46),
184                getFunctionNoWarn(47), getFunctionMomentaryNoWarn(47),
185                getFunctionNoWarn(48), getFunctionMomentaryNoWarn(48),
186                getFunctionNoWarn(49), getFunctionMomentaryNoWarn(49),
187                getFunctionNoWarn(50), getFunctionMomentaryNoWarn(50),
188                getFunctionNoWarn(51), getFunctionMomentaryNoWarn(51),
189                getFunctionNoWarn(52), getFunctionMomentaryNoWarn(52));
190    }
191
192    /**
193     * Send the message to set the state of functions F53 - F60
194     * adding it to the S queue
195     */
196    @Override
197    protected void sendFunctionGroup9() {
198        commandStation.function53Through60Packet(address,
199                getFunctionNoWarn(53), getFunctionMomentaryNoWarn(53),
200                getFunctionNoWarn(54), getFunctionMomentaryNoWarn(54),
201                getFunctionNoWarn(55), getFunctionMomentaryNoWarn(55),
202                getFunctionNoWarn(56), getFunctionMomentaryNoWarn(56),
203                getFunctionNoWarn(57), getFunctionMomentaryNoWarn(57),
204                getFunctionNoWarn(58), getFunctionMomentaryNoWarn(58),
205                getFunctionNoWarn(59), getFunctionMomentaryNoWarn(59),
206                getFunctionNoWarn(60), getFunctionMomentaryNoWarn(60));
207    }
208
209    /**
210     * Send the message to set the state of functions F61 - F68
211     * adding it to the S queue
212     */
213    @Override
214    protected void sendFunctionGroup10() {
215        commandStation.function61Through68Packet(address,
216                getFunctionNoWarn(61), getFunctionMomentaryNoWarn(61),
217                getFunctionNoWarn(62), getFunctionMomentaryNoWarn(62),
218                getFunctionNoWarn(63), getFunctionMomentaryNoWarn(63),
219                getFunctionNoWarn(64), getFunctionMomentaryNoWarn(64),
220                getFunctionNoWarn(65), getFunctionMomentaryNoWarn(65),
221                getFunctionNoWarn(66), getFunctionMomentaryNoWarn(66),
222                getFunctionNoWarn(67), getFunctionMomentaryNoWarn(67),
223                getFunctionNoWarn(68), getFunctionMomentaryNoWarn(68));
224    }
225
226    /**
227     * Set the speed and direction.
228     * <p>
229     * This intentionally skips the emergency stop value of 1 in 128 step mode
230     * and the stop and estop values 1-3 in 28 step mode.
231     *
232     * @param speed Number from 0 to 1; less than zero is emergency stop
233     */
234    @Override
235    public synchronized void setSpeedSetting(float speed) {
236        SpeedStepMode mode = getSpeedStepMode();
237        if (mode == SpeedStepMode.NMRA_DCC_28) {
238            // 28 step mode speed commands are
239            // stop, estop, stop, estop, 4, 5, ..., 31
240            float oldSpeed = this.speedSetting;
241            this.speedSetting = speed;
242            int value = Math.round((31 - 3) * speed);     // -3 for rescale to avoid estopx2 and stop
243            if (this.speedSetting > 0 && value == 0) {
244                value = 1;          // ensure non-zero input results in non-zero output
245            }
246            if (value > 0) {
247                value = value + 3;  // skip estopx2 and stop
248            }
249            if (value > 31) {
250                value = 31;      // max possible speed
251            }
252            if (value < 0) {
253                value = 1;        // emergency stop
254            }
255            commandStation.setSpeed(SpeedStepMode.NMRA_DCC_28, address, value, isForward);
256            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
257        } else {
258            // 128 step mode speed commands are
259            // stop, estop, 2, 3, ..., 127
260            float oldSpeed = this.speedSetting;
261            this.speedSetting = speed;
262            int value = Math.round((127 - 1) * speed);     // -1 for rescale to avoid estop
263            if (this.speedSetting > 0 && value == 0) {
264                value = 1;          // ensure non-zero input results in non-zero output
265            }
266            if (value > 0) {
267                value = value + 1;  // skip estop
268            }
269            if (value > 127) {
270                value = 127;    // max possible speed
271            }
272            if (value < 0) {
273                value = 1;        // emergency stop
274            }
275            commandStation.setSpeed(SpeedStepMode.NMRA_DCC_128, address, value, isForward);
276            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
277        }
278        record(speed);
279    }
280
281    @Override
282    public void setIsForward(boolean forward) {
283        boolean old = isForward;
284        isForward = forward;
285        synchronized(this) {
286            setSpeedSetting(speedSetting);  // Update the speed setting
287        }
288        firePropertyChange(ISFORWARD, old, isForward);
289    }
290
291    @Override
292    public void throttleDispose() {
293        active = false;
294        commandStation.release(address);
295        finishRecord();
296    }
297
298    private final static Logger log = LoggerFactory.getLogger(SprogCSThrottle.class);
299
300}