001package jmri.jmrix.roco.z21;
002
003import jmri.SpeedStepMode;
004import jmri.jmrix.lenz.XNetConstants;
005import jmri.jmrix.lenz.XNetMessage;
006
007/**
008 * Represents a single command or response on the XpressNet.
009 * <p>
010 * Content is represented with ints to avoid the problems with sign-extension
011 * that bytes have, and because a Java char is actually a variable number of
012 * bytes in Unicode.
013 *
014 * @author Bob Jacobsen Copyright (C) 2002
015 * @author Paul Bender Copyright (C) 2003-2010
016 */
017public class Z21XNetMessage extends jmri.jmrix.lenz.XNetMessage {
018
019    /**
020     * Constructor, just pass on to the superclass.
021     * @param len message length.
022     */
023    public Z21XNetMessage(int len) {
024        super(len);
025    }
026
027    /**
028     * Constructor from a Z21Message.
029     * @param m the Z21Message.
030     */
031    public Z21XNetMessage(Z21Message m) {
032        super(m.getLength()-4);
033        for(int i = 4; i< m.getLength() ; i++ ){
034           this.setElement(i-4,m.getElement(i));
035        }
036    }
037
038    /**
039     * Create a new object, that is a copy of an existing message.
040     *
041     * @param message an existing Z21XpressNet message
042     */
043    public Z21XNetMessage(Z21XNetMessage message) {
044        super(message);
045    }
046
047    /**
048     * Create an Z21XNetMessage from an Z21XNetReply.
049     * @param message the existing Z21XNetReply.
050     */
051    public Z21XNetMessage(Z21XNetReply message) {
052        super(message);
053    }
054
055    /**
056     * Create an XNetMessage from a String containing bytes.
057     * @param s byte string.
058     */
059    public Z21XNetMessage(String s) {
060        super(s);
061    }
062
063    @Override
064    public String toMonitorString() {
065        switch(getElement(0)) {
066            case Z21Constants.LAN_X_SET_TURNOUT: {
067                int address = (getElement(1) << 8) + getElement(2) + 1;
068                int element = getElement(3);
069                boolean queue = (element & 0x20) == 0x20;
070                String active = ((element & 0x08) == 0x08)? "activate":"deactivate";
071                return Bundle.getMessage("Z21LAN_X_SET_TURNOUT", address, active, element & 0x01, queue);
072            }
073            case Z21Constants.LAN_X_GET_TURNOUT_INFO: {
074                int address = (getElement(1) << 8) + getElement(2) + 1;
075                return Bundle.getMessage("Z21LAN_X_GET_TURNOUT_INFO", address);
076            }
077            default:
078                return super.toMonitorString();
079        }
080    }
081
082    /**
083     * Create messages of a particular form.
084     * @param cv CV index
085     * @return message to send.
086     */
087    public static Z21XNetMessage getZ21ReadDirectCVMsg(int cv) {
088        Z21XNetMessage m = new Z21XNetMessage(5);
089        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
090        m.setTimeout(XNetProgrammingTimeout);
091        m.setElement(0, Z21Constants.LAN_X_CV_READ_XHEADER);
092        m.setElement(1, Z21Constants.LAN_X_CV_READ_DB0);
093        m.setElement(2, ((0xff00 & (cv - 1)) >> 8));
094        m.setElement(3, (0xff & (cv - 1)));
095        m.setParity(); // Set the parity bit
096        return m;
097    }
098
099    public static Z21XNetMessage getZ21WriteDirectCVMsg(int cv, int val) {
100        Z21XNetMessage m = new Z21XNetMessage(6);
101        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
102        m.setTimeout(XNetProgrammingTimeout);
103        m.setElement(0, Z21Constants.LAN_X_CV_WRITE_XHEADER);
104        m.setElement(1, Z21Constants.LAN_X_CV_WRITE_DB0);
105        m.setElement(2, (0xff00 & (cv - 1)) >> 8);
106        m.setElement(3, (0xff & (cv - 1)));
107        m.setElement(4, val);
108        m.setParity(); // Set the parity bit
109        return m;
110    }
111
112    /**
113     * Given a locomotive address, request its status.
114     *
115     * @param address is the locomotive address
116     * @return message to send.
117     */
118    public static Z21XNetMessage getZ21LocomotiveInfoRequestMsg(int address) {
119        Z21XNetMessage msg = new Z21XNetMessage(5);
120        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
121        msg.setElement(1, Z21Constants.LAN_X_LOCO_INFO_REQUEST_Z21);
122        msg.setElement(2, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressHigh(address));
123        msg.setElement(3, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressLow(address));
124        msg.setParity();
125        return (msg);
126    }
127
128    /**
129     * Given a locomotive address, a function number, and its value,
130     * generate a message to change the state.
131     *
132     * @param address is the locomotive address
133     * @param functionno is the function to change
134     * @param state is boolean representing whether the function is to be on or off
135     * @return message to send.
136     */
137    public static Z21XNetMessage getZ21LocomotiveFunctionOperationMsg(int address, int functionno, boolean state) {
138        Z21XNetMessage msg = new Z21XNetMessage(6);
139        int functionbyte = functionno;
140        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
141        msg.setElement(1, Z21Constants.LAN_X_SET_LOCO_FUNCTION);
142        msg.setElement(2, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressHigh(address));
143        msg.setElement(3, jmri.jmrix.lenz.LenzCommandStation.getDCCAddressLow(address));
144        if(state) {
145           //This function is on
146           functionbyte = functionbyte & 0x3F; // clear the 2 most significant bits.
147           functionbyte = functionbyte | 0x40; // set the 2 msb to 01.
148        } else {
149           //This function is off.
150           functionbyte = functionbyte & 0x3F; // clear the 2 most significant bits.
151        }
152        msg.setElement(4, functionbyte);
153        msg.setParity();
154        msg.setBroadcastReply();
155        return (msg);
156    }
157
158    /**
159     * Generate a Z21 message to change the speed/direction of a locomotive.
160     *
161     * @param address the locomotive address
162     * @param speedMode the speedstep mode see @jmri.DccThrottle
163     *                       for possible values.
164     * @param speed a normalized speed value (a floating point number between 0
165     *              and 1).  A negative value indicates emergency stop.
166     * @param isForward true for forward, false for reverse.
167     * @return message to send.
168     */
169    public static XNetMessage getZ21LanXSetLocoDriveMsg(int address, SpeedStepMode speedMode, float speed, boolean isForward) {
170        XNetMessage msg = XNetMessage.getSpeedAndDirectionMsg(address,
171                        speedMode,speed,isForward);
172        msg.setBroadcastReply();
173        return (msg);
174    }
175
176
177    /**
178     * Given a turnout address, generate a message to request the state.
179     *
180     * @param address the turnout address
181     * @return message to send.
182     */
183    public static Z21XNetMessage getZ21TurnoutInfoRequestMessage(int address ) {
184        // refer to section 5.1 of the z21 lan protocol manual.
185        Z21XNetMessage msg = new Z21XNetMessage(4);
186        msg.setElement(0,Z21Constants.LAN_X_GET_TURNOUT_INFO);
187        // compared to Lenz devices, the addresses on the Z21 is one below 
188        // the numerical value.  We will correct it here so higher level 
189        // code doesn't see the difference.
190        msg.setElement(1,((address-1) &0xff00)>>8);
191        msg.setElement(2,((address-1) & 0x00ff));
192        msg.setParity();
193        return(msg);
194    }
195
196    /**
197     * Given a turnout address and whether or not it is thrown, generate 
198     * a message to operate the turnout.
199     *
200     * @param address is the turnout address
201     * @param thrown boolean value representing whether the turnout is thrown.
202     * @param active boolean value representing whether the output is being set
203     * to active.
204     * @param queue boolean value representing whehter or not the message is 
205     * added to the queue.
206     * @return message to send.
207     */
208    public static Z21XNetMessage getZ21SetTurnoutRequestMessage(int address, boolean thrown, boolean active, boolean queue) {
209        // refer to section 5.2 of the z21 lan protocol manual.
210        Z21XNetMessage msg = new Z21XNetMessage(5);
211        msg.setElement(0,Z21Constants.LAN_X_SET_TURNOUT);
212        // compared to Lenz devices, the addresses on the Z21 is one below 
213        // the numerical value.  We will correct it here so higher level 
214        // code doesn't see the difference.
215        msg.setElement(1,((address-1) &0xff00)>>8);
216        msg.setElement(2,((address-1) & 0x00ff));
217        int element3=0x80;
218        if(active) {
219           element3 |=  0x08;
220        } 
221        if(thrown) {
222           element3 |=  0x01;
223        } 
224        if(queue) {
225           element3 |=  0x20;
226        } 
227
228        msg.setElement(3,element3);
229        msg.setParity();
230        msg.setBroadcastReply();
231        return(msg);
232    }
233
234}