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