001package jmri.jmrix.zimo;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import jmri.DccLocoAddress;
005import jmri.LocoAddress;
006import jmri.jmrix.AbstractThrottle;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * An implementation of DccThrottle with code specific to an Mx1 connection.
012 * <p>
013 * Based on Glen Oberhauser's original LnThrottleManager implementation
014 *
015 * @author Bob Jacobsen Copyright (C) 2001
016 */
017public class Mx1Throttle extends AbstractThrottle implements Mx1Listener {
018
019    private Mx1TrafficController tc = null;
020    //private Mx1Interface network;
021
022    /**
023     * Create a new throttle.
024     *
025     * @param memo    the system connection the throttle is associated with
026     * @param address the address for the throttle
027     */
028    public Mx1Throttle(Mx1SystemConnectionMemo memo, DccLocoAddress address) {
029        super(memo);
030        this.tc = memo.getMx1TrafficController();
031        super.speedStepMode = jmri.SpeedStepMode.NMRA_DCC_128;
032
033        // cache settings. It would be better to read the
034        // actual state, but I don't know how to do this
035        synchronized(this) {
036            this.speedSetting = 0;
037        }
038        // Functions default to false
039        this.address = address;
040        this.isForward = true;
041        if (address.isLongAddress()) {
042            addressLo = address.getNumber();
043            addressHi = address.getNumber() >> 8;
044            addressHi = addressHi + 0xc0; //We add 0xc0 to the high byte.
045        } else {
046            addressLo = address.getNumber();
047        }
048        tc.addMx1Listener(~0, this);
049    }
050
051    DccLocoAddress address;
052
053    int addressLo = 0x00;
054    int addressHi = 0x00;
055
056    @Override
057    public LocoAddress getLocoAddress() {
058        return address;
059    }
060
061    @Override
062    protected void sendFunctionGroup1() {
063        sendSpeedCmd();
064        /*int data = 0x00 |
065         ( f0 ? 0x10 : 0) |
066         ( f1 ? 0x01 : 0) |
067         ( f2 ? 0x02 : 0) |
068         ( f3 ? 0x04 : 0) |
069         ( f4 ? 0x08 : 0);
070        
071         data = data + 0x80;*/
072 /*Mx1Message m = Mx1Message.getSendFunction(1, addressLo, addressHi, data);
073         if(m!=null)
074         tc.sendMx1Message(m);*/
075    }
076
077    /**
078     * Send the message to set the state of functions F5, F6, F7, F8.
079     */
080    @Override
081    protected void sendFunctionGroup2() {
082        sendSpeedCmd();
083        // Always need speed command before function group command to reset consist pointer
084        /*int data = 0x00 |
085         (f8 ? 0x08 : 0) |
086         (f7 ? 0x04 : 0) |
087         (f6 ? 0x02 : 0) |
088         (f5 ? 0x01 : 0);
089        
090         data = data + 0xB0;*/
091    }
092
093    /**
094     * Send the message to set the state of functions F9, F12, F11, F12.
095     */
096    @Override
097    protected void sendFunctionGroup3() {
098        sendSpeedCmd();
099        /*int data = 0x00 |
100         ( f9 ? 0x01 : 0) |
101         ( f10 ? 0x02 : 0) |
102         ( f11 ? 0x04 : 0) |
103         ( f12 ? 0x08 : 0);
104        
105         data = data + 0xA0;*/
106    }
107
108    /**
109     * Send the message to set the state of functions F13 to F20 in function
110     * Group 4 and 5
111     */
112    @Override
113    protected void sendFunctionGroup4() {
114        // The NCE USB doesn't support the NMRA packet format
115        // Always need speed command before function group command to reset consist pointer
116//         int data = 0x00
117//                 | (f16 ? 0x08 : 0)
118//                 | (f15 ? 0x04 : 0)
119//                 | (f14 ? 0x02 : 0)
120//                 | (f13 ? 0x01 : 0);
121// 
122//         data = data + 0xD0;
123
124        /*Mx1Message m = Mx1Message.getSendFunction(4, addressLo, addressHi, data);
125         if(m!=null)
126         tc.sendMx1Message(m);*/
127//         data = 0x00
128//                 | (f20 ? 0x08 : 0)
129//                 | (f19 ? 0x04 : 0)
130//                 | (f18 ? 0x02 : 0)
131//                 | (f17 ? 0x01 : 0);
132//         data = data + 0xC0;
133
134        /*m = Mx1Message.getSendFunction(5, addressLo, addressHi, data);
135         if(m!=null)
136         tc.sendMx1Message(m);*/
137    }
138
139    /**
140     * Send the message to set the state of functions F21 to F28. MRC Group 6
141     */
142    @Override
143    protected void sendFunctionGroup5() {
144        /* int data = 0x00 |
145         (f28 ? 0x80 : 0) |
146         (f27 ? 0x40 : 0) |
147         (f26 ? 0x20 : 0) |
148         (f25 ? 0x10 : 0) |
149         (f24 ? 0x08 : 0) |
150         (f23 ? 0x04 : 0) |
151         (f22 ? 0x02 : 0) |
152         (f21 ? 0x01 : 0); */
153
154 /*Mx1Message m = Mx1Message.getSendFunction(6, addressLo, addressHi, data);
155         if(m!=null)
156         tc.sendMx1Message(m);   */
157    }
158
159    /**
160     * Set the speed and direction.
161     *
162     * @param speed Number from 0 to 1; less than zero is emergency stop
163     */
164    @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY") // OK to compare floating point, notify on any change
165    @Override
166    public synchronized void setSpeedSetting(float speed) {
167        float oldSpeed = this.speedSetting;
168        this.speedSetting = speed;
169        sendSpeedCmd();
170        firePropertyChange(SPEEDSETTING, oldSpeed, this.speedSetting);
171        record(speed);
172    }
173
174    void sendSpeedCmd() {
175        Mx1Message m;
176        int value = 0;
177        int cData1 = (isForward ? 0x00 : 0x20);
178        cData1 = cData1 + (getFunction(0) ? 0x10 : 0x00);
179        synchronized(this) {
180            if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_128) {
181                //m = Mx1Message.getSendSpeed128(addressLo, addressHi, value);
182                value = Math.round((127 - 1) * speedSetting); // -1 for rescale to avoid estop
183                if (speedSetting > 0 && value == 0) {
184                    value = 1;          // ensure non-zero speed for non-zero input
185                }
186                if (value > 0) {
187                    value = value + 1;  // skip estop
188                }
189                if (value > 127) {
190                    value = 127;    // max possible speed
191                }
192                if (value < 0) {
193                    value = 1;        // emergency stop
194                }
195                value = (value & 0x7F);
196                cData1 = cData1 + 0xc;
197            } else if (super.speedStepMode == jmri.SpeedStepMode.NMRA_DCC_28) {
198                value = Math.round((28) * speedSetting); // -1 for rescale to avoid estop
199                if (speedSetting > 0 && value == 0) {
200                    value = 1;          // ensure non-zero speed for non-zero input
201                }
202                if (value > 0) {
203                    value = value + 3; // skip estop
204                }
205                if (value > 32) {
206                    value = 31; // max possible speed
207                }
208                if (value < 0) {
209                    value = 2; // emergency stop
210                }
211                int speedC = (value & 0x1F) >> 1;
212                int c = (value & 0x01) << 4; // intermediate speed step
213
214                speedC = speedC + c;
215                value = (isForward ? 0x60 : 0x40) | speedC;
216                cData1 = cData1 + 0x8;
217            }
218        }
219        m = Mx1Message.getLocoControl(address.getNumber(), value, true, cData1, getFunction1to8(), getFunction9to12());
220        tc.sendMx1Message(m, this);
221    }
222
223    int getFunction1to8() {
224
225        int data = 0x00
226                | (getFunction(1) ? 0x01 : 0)
227                | (getFunction(2) ? 0x02 : 0)
228                | (getFunction(3) ? 0x04 : 0)
229                | (getFunction(4) ? 0x08 : 0)
230                | (getFunction(5) ? 0x10 : 0)
231                | (getFunction(6) ? 0x20 : 0)
232                | (getFunction(7) ? 0x40 : 0)
233                | (getFunction(8) ? 0x80 : 0);
234
235        return data;
236    }
237
238    int getFunction9to12() {
239        int data = 0x00
240                | (getFunction(9) ? 0x01 : 0)
241                | (getFunction(10) ? 0x02 : 0)
242                | (getFunction(11) ? 0x04 : 0)
243                | (getFunction(12) ? 0x08 : 0);
244        return data;
245    }
246
247    @Override
248    public void setIsForward(boolean forward) {
249        boolean old = isForward;
250        isForward = forward;
251        synchronized(this) {
252            setSpeedSetting(speedSetting);  // send the command
253        }
254        log.debug("setIsForward= {}", forward);
255        firePropertyChange(ISFORWARD, old, isForward);
256    }
257
258    @Override
259    public void throttleDispose() {
260        finishRecord();
261    }
262
263    @Override
264    public void message(Mx1Message m) {
265
266    }
267
268    // initialize logging
269    private final static Logger log = LoggerFactory.getLogger(Mx1Throttle.class);
270
271}