001package jmri.jmrix.sprog;
002
003import jmri.DccLocoAddress;
004import jmri.InstanceManager;
005import jmri.LocoAddress;
006import jmri.SpeedStepMode;
007import jmri.jmrix.AbstractThrottle;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * An implementation of DccThrottle with code specific to an SPROG connection.
013 * <p>
014 * Based on the {@link jmri.jmrix.nce.NceThrottle} implementation.
015 * <p>
016 * Updated by Andrew Crosland February 2012 to enable 28 step speed packets
017 *
018 * @author Bob Jacobsen Copyright (C) 2003
019 */
020public class SprogThrottle extends AbstractThrottle {
021
022    /**
023     * Constructor.
024     * @param memo system connection.
025     * @param address Loco address.
026     */
027    public SprogThrottle(SprogSystemConnectionMemo memo, DccLocoAddress address) {
028        super(memo, SprogConstants.MAX_FUNCTIONS);
029        station = memo.getCommandStation();
030
031        // cache settings.
032        synchronized (this) {
033            this.speedSetting = 0;
034        }
035        // Functions default to false
036        this.address = address;
037        this.isForward = true;
038        this.speedStepMode = SpeedStepMode.NMRA_DCC_128;
039
040    }
041
042    SprogCommandStation station = null;
043
044    DccLocoAddress address;
045
046    @Override
047    public LocoAddress getLocoAddress() {
048        return address;
049    }
050
051    /**
052     * Send the message to set the state of functions F0, F1, F2, F3, F4.
053     */
054    @Override
055    protected void sendFunctionGroup1() {
056        byte[] result = jmri.NmraPacket.function0Through4Packet(
057                address.getNumber(), address.isLongAddress(),
058                getFunction(0), getFunction(1), getFunction(2), getFunction(3), getFunction(4));
059
060        station.sendPacket(result, 1);
061    }
062
063    /**
064     * Send the message to set the state of functions F5, F6, F7, F8.
065     */
066    @Override
067    protected void sendFunctionGroup2() {
068
069        byte[] result = jmri.NmraPacket.function5Through8Packet(
070                address.getNumber(), address.isLongAddress(),
071                getFunction(5), getFunction(6), getFunction(7), getFunction(8));
072
073        station.sendPacket(result, 1);
074    }
075
076    /**
077     * Send the message to set the state of functions F9, F10, F11, F12.
078     */
079    @Override
080    protected void sendFunctionGroup3() {
081
082        byte[] result = jmri.NmraPacket.function9Through12Packet(
083                address.getNumber(), address.isLongAddress(),
084                getFunction(9), getFunction(10), getFunction(11), getFunction(12));
085
086        station.sendPacket(result, 1);
087    }
088
089    /**
090     * Send the message to set the state of functions F13 - F20.
091     */
092    @Override
093    protected void sendFunctionGroup4() {
094
095        byte[] result = jmri.NmraPacket.function13Through20Packet(
096                address.getNumber(), address.isLongAddress(),
097                getFunction(13), getFunction(14), getFunction(15), getFunction(16),
098                getFunction(17), getFunction(18), getFunction(19), getFunction(20));
099
100        station.sendPacket(result, 1);
101    }
102
103    /**
104     * Send the message to set the state of functions F21 - F28.
105     */
106    @Override
107    protected void sendFunctionGroup5() {
108
109        byte[] result = jmri.NmraPacket.function21Through28Packet(
110                address.getNumber(), address.isLongAddress(),
111                getFunction(21), getFunction(22), getFunction(23), getFunction(24),
112                getFunction(25), getFunction(26), getFunction(27), getFunction(28));
113
114        station.sendPacket(result, 1);
115    }
116
117    /**
118     * Send the message to set the state of functions F29 - F36.
119     */
120    @Override
121    protected void sendFunctionGroup6() {
122
123        byte[] result = jmri.NmraPacket.function29Through36Packet(
124                address.getNumber(), address.isLongAddress(),
125                getFunctionNoWarn(29), getFunctionNoWarn(30), getFunctionNoWarn(31), getFunctionNoWarn(32),
126                getFunctionNoWarn(33), getFunctionNoWarn(34), getFunctionNoWarn(35), getFunctionNoWarn(36)
127        );
128
129        station.sendPacket(result, 1);
130    }
131
132    /**
133     * Send the message to set the state of functions F37 - F44.
134     */
135    @Override
136    protected void sendFunctionGroup7() {
137
138        byte[] result = jmri.NmraPacket.function37Through44Packet(
139                address.getNumber(), address.isLongAddress(),
140                getFunctionNoWarn(37), getFunctionNoWarn(38), getFunctionNoWarn(39), getFunctionNoWarn(40),
141                getFunctionNoWarn(41), getFunctionNoWarn(42), getFunctionNoWarn(43), getFunctionNoWarn(44)
142        );
143
144        station.sendPacket(result, 1);
145    }
146
147    /**
148     * Send the message to set the state of functions F45 - F52.
149     */
150    @Override
151    protected void sendFunctionGroup8() {
152
153        byte[] result = jmri.NmraPacket.function45Through52Packet(
154                address.getNumber(), address.isLongAddress(),
155                getFunctionNoWarn(45), getFunctionNoWarn(46), getFunctionNoWarn(47), getFunctionNoWarn(48),
156                getFunctionNoWarn(49), getFunctionNoWarn(50), getFunctionNoWarn(51), getFunctionNoWarn(52)
157        );
158
159        station.sendPacket(result, 1);
160    }
161
162    /**
163     * Send the message to set the state of functions F53 - F60.
164     */
165    @Override
166    protected void sendFunctionGroup9() {
167
168        byte[] result = jmri.NmraPacket.function53Through60Packet(
169                address.getNumber(), address.isLongAddress(),
170                getFunctionNoWarn(53), getFunctionNoWarn(54), getFunctionNoWarn(55), getFunctionNoWarn(56),
171                getFunctionNoWarn(57), getFunctionNoWarn(58), getFunctionNoWarn(59), getFunctionNoWarn(60)
172        );
173
174        station.sendPacket(result, 1);
175    }
176
177    /**
178     * Send the message to set the state of functions F61 - F68.
179     */
180    @Override
181    protected void sendFunctionGroup10() {
182
183        byte[] result = jmri.NmraPacket.function61Through68Packet(
184                address.getNumber(), address.isLongAddress(),
185                getFunctionNoWarn(61), getFunctionNoWarn(62), getFunctionNoWarn(63), getFunctionNoWarn(64),
186                getFunctionNoWarn(65), getFunctionNoWarn(66), getFunctionNoWarn(67), getFunctionNoWarn(68)
187        );
188
189        station.sendPacket(result, 1);
190    }
191
192    /**
193     * Set the speed step value and the related
194     * speedIncrement value.
195     *
196     * @param Mode  the current speed step mode - default should be 128 speed
197     *             step mode in most cases
198     */
199    @Override
200    public void setSpeedStepMode(SpeedStepMode Mode) {
201        SprogMessage m;
202        int mode = address.isLongAddress()
203                ? SprogConstants.LONG_ADD : 0;
204        try {
205            mode |= (InstanceManager.getDefault(jmri.PowerManager.class).getPower() == SprogPowerManager.ON)
206                    ? SprogConstants.POWER_BIT : 0;
207        } catch (Exception e) {
208            log.error("Exception from InstanceManager.getDefault(jmri.PowerManager.class)", e);
209        }
210        if (log.isDebugEnabled()) {
211            log.debug("Speed Step Mode Change to Mode: {} Current mode is: {}", Mode, this.speedStepMode);
212        }
213        if (Mode == SpeedStepMode.NMRA_DCC_14) {
214            mode += 0x200;
215        } else if (Mode == SpeedStepMode.NMRA_DCC_27) {
216            log.error("Requested Speed Step Mode 27 not supported Current mode is: {}", this.speedStepMode);
217            return;
218        } else if (Mode == SpeedStepMode.NMRA_DCC_28) {
219            mode += 0x400;
220        } else { // default to 128 speed step mode
221            mode += 0x800;
222        }
223        m = new SprogMessage("M h" + Integer.toHexString(mode));
224        ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null);
225        if (Mode != SpeedStepMode.NMRA_DCC_27) {
226            firePropertyChange(SPEEDSTEPS, this.speedStepMode, this.speedStepMode = Mode);
227        }
228    }
229
230    /**
231     * Set the speed and direction.
232     * <p>
233     * This intentionally skips the emergency stop value of 1 in 128 step mode
234     * and the stop and estop values 1-3 in 28 step mode.
235     *
236     * @param speed Number from 0 to 1; less than zero is emergency stop
237     */
238    @Override
239    public synchronized void setSpeedSetting(float speed) {
240        SpeedStepMode mode = getSpeedStepMode();
241        if (mode == SpeedStepMode.NMRA_DCC_28) {
242            // 28 step mode speed commands are
243            // stop, estop, stop, estop, 4, 5, ..., 31
244            float oldSpeed = this.speedSetting;
245            this.speedSetting = speed;
246            int value = Math.round((31 - 3) * speed);     // -3 for rescale to avoid estopx2 and stop
247            if (this.speedSetting > 0 && value == 0) {
248                value = 1;          // ensure non-zero input results in non-zero output
249            }
250
251            log.debug("Speed: {} value: {}", speed, value);
252
253            if (value > 0) {
254                value = value + 3;  // skip estopx2 and stop
255            }
256            if (value > 31) {
257                value = 31;      // max possible speed
258            }
259            if (value < 0) {
260                value = 1;        // emergency stop
261            }
262            String step = "" + value;
263
264            SprogMessage m = new SprogMessage(1 + step.length());
265            int i = 0;  // message index counter
266            if (isForward) {
267                m.setElement(i++, '>');
268            } else {
269                m.setElement(i++, '<');
270            }
271
272            for (int j = 0; j < step.length(); j++) {
273                m.setElement(i++, step.charAt(j));
274            }
275
276            ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null);
277            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
278        } else {
279            // 128 step mode speed commands are
280            // stop, estop, 2, 3, ..., 127
281            float oldSpeed = this.speedSetting;
282            this.speedSetting = speed;
283            int value = Math.round((127 - 1) * speed);     // -1 for rescale to avoid estop
284            if (this.speedSetting > 0 && value == 0) {
285                value = 1;          // ensure non-zero input results in non-zero output
286            }
287            if (value > 0) {
288                value = value + 1;  // skip estop
289            }
290            if (value > 127) {
291                value = 127;    // max possible speed
292            }
293            if (value < 0) {
294                value = 1;        // emergency stop
295            }
296            String step = "" + value;
297
298            SprogMessage m = new SprogMessage(1 + step.length());
299            int i = 0;  // message index counter
300            if (isForward) {
301                m.setElement(i++, '>');
302            } else {
303                m.setElement(i++, '<');
304            }
305
306            for (int j = 0; j < step.length(); j++) {
307                m.setElement(i++, step.charAt(j));
308            }
309
310            ((SprogSystemConnectionMemo)adapterMemo).getSprogTrafficController().sendSprogMessage(m, null);
311            firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
312        }
313        record(speed);
314    }
315
316    @Override
317    public void setIsForward(boolean forward) {
318        boolean old = isForward;
319        isForward = forward;
320        synchronized (this) {
321            setSpeedSetting(speedSetting);  // send the command
322        }
323        firePropertyChange(ISFORWARD, old, isForward);
324    }
325
326    @Override
327    public void throttleDispose() {
328        finishRecord();
329    }
330
331    // initialize logging
332    private final static Logger log = LoggerFactory.getLogger(SprogThrottle.class);
333
334}