001package jmri.jmrix.roco.z21;
002
003import jmri.LocoAddress;
004import jmri.jmrix.lenz.XNetMessage;
005import jmri.jmrix.lenz.XNetReply;
006import jmri.jmrix.lenz.XNetSystemConnectionMemo;
007import jmri.jmrix.lenz.XNetTrafficController;
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011/**
012 * An implementation of DccThrottle with code specific to a z21 XpressNet
013 * connection.
014 *
015 * @author Paul Bender (C) 2015
016 */
017public class Z21XNetThrottle extends jmri.jmrix.roco.RocoXNetThrottle {
018
019    /**
020     * Constructor.
021     * @param memo system connection.
022     * @param controller traffic controller.
023     */
024    public Z21XNetThrottle(XNetSystemConnectionMemo memo, XNetTrafficController controller) {
025        super(memo,controller);
026    }
027
028    /**
029     * Constructor.
030     * @param memo system connection.
031     * @param address loco address.
032     * @param controller traffic controller.
033     */
034    public Z21XNetThrottle(XNetSystemConnectionMemo memo, LocoAddress address, XNetTrafficController controller) {
035        super(memo,address,controller);
036    }
037
038    /**
039     * {@inheritDoc}
040     */
041    @Override
042    synchronized public void setSpeedSetting(float speed) {
043        log.debug("set Speed to: {} Current step mode is: {}",speed,this.speedStepMode);
044        synchronized(this) {
045            this.speedSetting = speed;
046        }
047        record(speed);
048        if (speed < 0) {
049            /* we're sending an emergency stop to this locomotive only */
050            sendEmergencyStop();
051        } else {
052            if (speed > 1) {
053                speed = (float) 1.0;
054            }
055            /* we're sending a speed to the locomotive */
056            XNetMessage msg = Z21XNetMessage.getZ21LanXSetLocoDriveMsg(getDccAddress(),
057                    this.speedStepMode,
058                    speed,
059                    this.isForward);
060            // now, queue the message for sending to the command station
061            queueMessage(msg, THROTTLEIDLE);
062        }
063    }
064
065    /**
066     * {@inheritDoc}
067     */
068    @Override
069    public void setSpeedSetting(float speed, boolean allowDuplicates, boolean allowDuplicatesOnStop) {
070        synchronized(this) {
071            this.speedSetting = speed;
072        }
073        record(speed);
074    }
075
076    /**
077     * Send a request to get the speed, direction and function status from
078     * the command station.
079     */
080    @Override
081    synchronized protected void sendStatusInformationRequest() {
082        /* Send the request for status */
083        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveInfoRequestMsg(this.address);
084        msg.setRetries(1); // Since we repeat this ourselves, don't ask the 
085        // traffic controller to do this for us.
086        // now, we queue the message for sending to the command station
087        queueMessage(msg, THROTTLESTATSENT);
088    }
089
090    /**
091     * Send the XpressNet messages to set the state of locomotive direction and
092     * functions F0, F1, F2, F3, F4.
093     */
094    @Override
095    protected void sendFunctionGroup1() {
096        // because of the way the z21 wants to see the functions, we
097        // send all the functions when there is a change in the group.
098        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),0,getFunction(0));
099        // now, queue the message for sending to the command station
100        queueMessage(msg, THROTTLEIDLE);
101        XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),1,getFunction(1));
102        // now, queue the message for sending to the command station
103        queueMessage(msg1, THROTTLEIDLE);
104        XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),2,getFunction(2));
105        // now, queue the message for sending to the command station
106        queueMessage(msg2, THROTTLEIDLE);
107        XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),3,getFunction(3));
108        // now, queue the message for sending to the command station
109        queueMessage(msg3, THROTTLEIDLE);
110        XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),4,getFunction(4));
111        // now, queue the message for sending to the command station
112        queueMessage(msg4, THROTTLEIDLE);
113    }
114
115    /**
116     * Send the XpressNet message to set the state of functions F5, F6, F7, F8.
117     */
118    @Override
119    protected void sendFunctionGroup2() {
120        // because of the way the z21 wants to see the functions, we
121        // send all the functions when there is a change in the group.
122        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),5,getFunction(5));
123        // now, queue the message for sending to the command station
124        queueMessage(msg, THROTTLEIDLE);
125        XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),6,getFunction(6));
126        // now, queue the message for sending to the command station
127        queueMessage(msg1, THROTTLEIDLE);
128        XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),7,getFunction(7));
129        // now, queue the message for sending to the command station
130        queueMessage(msg2, THROTTLEIDLE);
131        XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),8,getFunction(8));
132        // now, queue the message for sending to the command station
133        queueMessage(msg3, THROTTLEIDLE);
134    }
135
136    /**
137     * Send the XpressNet message to set the state of functions F9, F10, F11,
138     * F12.
139     */
140    @Override
141    protected void sendFunctionGroup3() {
142        // because of the way the z21 wants to see the functions, we
143        // send all the functions when there is a change in the group.
144        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),9,getFunction(9));
145        // now, queue the message for sending to the command station
146        queueMessage(msg, THROTTLEIDLE);
147        XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),10,getFunction(10));
148        // now, queue the message for sending to the command station
149        queueMessage(msg1, THROTTLEIDLE);
150        XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),11,getFunction(11));
151        // now, queue the message for sending to the command station
152        queueMessage(msg2, THROTTLEIDLE);
153        XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),12,getFunction(12));
154        // now, queue the message for sending to the command station
155        queueMessage(msg3, THROTTLEIDLE);
156    }
157
158    /**
159     * Send the XpressNet message to set the state of functions F13, F14, F15,
160     * F16, F17, F18, F19, F20.
161     */
162    @Override
163    protected void sendFunctionGroup4() {
164        // because of the way the z21 wants to see the functions, we
165        // send all the functions when there is a change in the group.
166        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),13,getFunction(13));
167        // now, queue the message for sending to the command station
168        queueMessage(msg, THROTTLEIDLE);
169        XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),14,getFunction(14));
170        // now, queue the message for sending to the command station
171        queueMessage(msg1, THROTTLEIDLE);
172        XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),15,getFunction(15));
173        // now, queue the message for sending to the command station
174        queueMessage(msg2, THROTTLEIDLE);
175        XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),16,getFunction(16));
176        // now, queue the message for sending to the command station
177        queueMessage(msg3, THROTTLEIDLE);
178        XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),17,getFunction(17));
179        // now, queue the message for sending to the command station
180        queueMessage(msg4, THROTTLEIDLE);
181        XNetMessage msg5 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),18,getFunction(18));
182        // now, queue the message for sending to the command station
183        queueMessage(msg5, THROTTLEIDLE);
184        XNetMessage msg6 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),19,getFunction(19));
185        // now, queue the message for sending to the command station
186        queueMessage(msg6, THROTTLEIDLE);
187        XNetMessage msg7 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),20,getFunction(20));
188        // now, queue the message for sending to the command station
189        queueMessage(msg7, THROTTLEIDLE);
190    }
191    /**
192     * Send the XpressNet message to set the state of functions F21, F22, F23,
193     * F24, F25, F26, F27, F28.
194     */
195    @Override
196    protected void sendFunctionGroup5() {
197        // because of the way the z21 wants to see the functions, we
198        // send all the functions when there is a change in the group.
199        XNetMessage msg = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),21,getFunction(21));
200        // now, queue the message for sending to the command station
201        queueMessage(msg, THROTTLEIDLE);
202        XNetMessage msg1 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),22,getFunction(22));
203        // now, queue the message for sending to the command station
204        queueMessage(msg1, THROTTLEIDLE);
205        XNetMessage msg2 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),23,getFunction(23));
206        // now, queue the message for sending to the command station
207        queueMessage(msg2, THROTTLEIDLE);
208        XNetMessage msg3 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),24,getFunction(24));
209        // now, queue the message for sending to the command station
210        queueMessage(msg3, THROTTLEIDLE);
211        XNetMessage msg4 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),25,getFunction(25));
212        // now, queue the message for sending to the command station
213        queueMessage(msg4, THROTTLEIDLE);
214        XNetMessage msg5 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),26,getFunction(26));
215        // now, queue the message for sending to the command station
216        queueMessage(msg5, THROTTLEIDLE);
217        XNetMessage msg6 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),27,getFunction(27));
218        // now, queue the message for sending to the command station
219        queueMessage(msg6, THROTTLEIDLE);
220        XNetMessage msg7 = Z21XNetMessage.getZ21LocomotiveFunctionOperationMsg(this.getDccAddress(),28,getFunction(28));
221        // now, queue the message for sending to the command station
222        queueMessage(msg7, THROTTLEIDLE);
223    }
224
225    // The Roco doesn't support the XpressNet directed emergency stop
226    // instruction, so override sendEmergencyStop in the parent, and
227    // just send speed step 0.
228    @Override
229    protected void sendEmergencyStop(){
230       setSpeedSetting(0);
231    }
232
233    // Handle incoming messages for this throttle.
234    @Override
235    public void message(XNetReply l) {
236        log.debug("Throttle {} - received message {}",getDccAddress(),l.toString());
237        if((l.getElement(0)&0xE0)==0xE0 && ((l.getElement(0)&0x0f) >= 7 && (l.getElement(0)&0x0f) <=15 )){
238            //This is a Roco specific throttle information message.
239            //Data Byte 0 and 1 contain the locomotive address
240            int messageaddress=((l.getElement(1)&0x3F) << 8)+l.getElement(2);
241            if(messageaddress==getDccAddress()){
242               //The message is for this throttle.
243               int b2= l.getElement(3)&0xff; 
244               int b3= l.getElement(4)&0xff; 
245               int b4= l.getElement(5)&0xff; 
246               int b5= l.getElement(6)&0xff; 
247               int b6= l.getElement(7)&0xff; 
248               int b7= l.getElement(8)&0xff; 
249               // byte 2 contains the speed step mode and availability 
250               // information.
251               parseSpeedAndAvailability(b2);
252               // byte 3 contains the direction and the speed information
253               parseSpeedAndDirection(b3);
254               // byte 4 contains flags for whether or not the locomotive
255               // is in a double header and for smart search.  These aren't used
256               // here.
257
258               // byte 4 and 5 contain function information for F0-F12
259               parseFunctionInformation(b4,b5);
260               // byte 6 and 7 contain function information for F13-F28
261               parseFunctionHighInformation(b6,b7);
262              
263               // set the request state to idle
264               requestState = THROTTLEIDLE;
265               // and send any queued messages.
266               sendQueuedMessage();
267           } 
268        } else {
269            // let the standard XpressNet Throttle have a chance to look
270            // at the message.
271            super.message(l);
272        }
273    }
274
275    /**
276     * Dispose when finished with this object. After this, further usage of this
277     * Throttle object will result in a JmriException.
278     * <p>
279     * This is quite problematic, because a using object doesn't know when it's
280     * the last user.
281     */
282    @Override
283    public void throttleDispose() {
284        active = false;
285        stopStatusTimer();
286        finishRecord();
287    }
288
289    // register for notification
290    private final static Logger log = LoggerFactory.getLogger(Z21XNetThrottle.class);
291
292}