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