001package jmri.jmrix.dcc4pc;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Encodes a message to the DCC4PC Interface.
008 * <p>
009 * The {@link Dcc4PcReply} class handles the response from the command station.
010 *
011 * @author Bob Jacobsen Copyright (C) 2001
012 * @author Kevin Dickerson Copyright (C) 2012
013 * 
014 */
015public class Dcc4PcMessage extends jmri.jmrix.AbstractMRMessage {
016
017    public static final int MAXSIZE = 515;
018
019    // create a new one
020    public Dcc4PcMessage(int i) {
021        if (i < 1) {
022            log.error("invalid length in call to ctor");
023        }
024        _nDataChars = i;
025        _dataChars = new int[i];
026        setBinary(true);
027        setRetries(3);
028    }
029
030    /**
031     * Creates a new Dcc4PcMessage containing a byte array to represent a packet
032     * to output
033     *
034     * @param packet The contents of the packet
035     */
036    public Dcc4PcMessage(byte[] packet) {
037        this((packet.length));
038        int i = 0; // counter of byte in output message
039        int j; // counter of byte in input packet
040        setBinary(true);
041        // add each byte of the input message
042        for (j = 0; j < packet.length; j++) {
043            this.setElement(i, packet[i]);
044            i++;
045        }
046        setRetries(1);
047    }
048
049    // from String
050    public Dcc4PcMessage(String s) {
051        _nDataChars = s.length();
052        _dataChars = new int[_nDataChars];
053        for (int i = 0; i < _nDataChars; i++) {
054            _dataChars[i] = s.charAt(i);
055        }
056        setBinary(true);
057        setRetries(3);
058    }
059
060    // copy one
061    public Dcc4PcMessage(Dcc4PcMessage m) {
062        if (m == null) {
063            log.error("copy ctor of null message");
064            return;
065        }
066        _nDataChars = m._nDataChars;
067        _dataChars = new int[_nDataChars];
068        for (int i = 0; i < _nDataChars; i++) {
069            _dataChars[i] = m._dataChars[i];
070        }
071        setBinary(true);
072        setRetries(1);
073    }
074
075    public void setForChildBoard(boolean boo) {
076        childBoard = boo;
077    }
078
079    boolean childBoard = false;
080
081    public boolean isForChildBoard() {
082        return childBoard;
083    }
084
085    /** {@inheritDoc} */
086    @Override
087    public void setElement(int n, int v) {
088        _dataChars[n] = v;
089    }
090
091    /** {@inheritDoc} */
092    @Override
093    public String toString() {
094        StringBuilder buf = new StringBuilder();
095        buf.append("0x");
096        buf.append(Integer.toHexString(0xFF & _dataChars[0]));
097        for (int i = 1; i < _nDataChars; i++) {
098            buf.append(".0x");
099            buf.append(Integer.toHexString(0xff & _dataChars[i]));
100            //buf.append(", 0x" + Integer.toHexString(0xFF & _dataChars[i]));
101        }
102        return buf.toString();
103    }
104
105    /**
106     * Get formatted message for direct output to stream - this is the final
107     * format of the message as a byte array
108     *
109     * @return the formatted message as a byte array
110     */
111    public byte[] getFormattedMessage() {
112        int len = this.getNumDataElements();
113        // space for carriage return if required
114        int cr = 0;
115
116        byte msg[] = new byte[len + cr];
117
118        for (int i = 0; i < len; i++) {
119            msg[i] = (byte) this.getElement(i);
120        }
121        return msg;
122    }
123
124    //Not supported
125    public static Dcc4PcMessage getProgMode() {
126        Dcc4PcMessage m = new Dcc4PcMessage(1);
127        //m.setOpCode('+');
128        return m;
129    }
130
131    //Not supported
132    public static Dcc4PcMessage getExitProgMode() {
133        Dcc4PcMessage m = new Dcc4PcMessage(1);
134        // m.setOpCode(' ');
135        return m;
136    }
137
138    boolean isResponse = false;
139
140    public boolean isGetResponse() {
141        return isResponse;
142    }
143    
144    int board = -1;
145    
146    public int getBoard(){
147        return board;
148    }
149    
150    int messageType = -1;
151    public int getMessageType(){
152        return messageType;
153    }
154    
155    static final int INFO = 0x00;
156    static final int DESC = 0x01;
157    static final int SERIAL = 0x02;
158    static final int CHILDSELECTENCODING = 0x06;
159    static final int CHILDENABLEDINPUTS = 0x07;
160    static final int CHILDRESET = 0x09;
161    static final int CHILDPOLL = 0x0a;
162    static final int RESPONSE = 0x0c;
163    
164    public static Dcc4PcMessage getInfo(int address) {
165        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) INFO});
166        m.childBoard = true;
167        m.board = address;
168        m.messageType = INFO;
169        m.setRetries(2);
170        return m;
171    }
172
173    public static Dcc4PcMessage getDescription(int address) {
174        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) DESC});
175        m.childBoard = true;
176        m.board = address;
177        m.messageType = DESC;
178        return m;
179    }
180
181    public static Dcc4PcMessage getSerialNumber(int address) {
182        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) SERIAL});
183        m.childBoard = true;
184        m.board = address;
185        m.messageType = SERIAL;
186        return m;
187    }
188
189    static final int CHILDSETSPEED = 0x04;
190    // interface (BUS) commands; offset so they can't be mistaken for child board commands
191    static final int BUSSETSPEED = 0x107;
192    static final int BUSRESETSPEEDS = 0x10D;
193
194    /** SPEED code for 62.5 kbps (Omnibus spec section 6). */
195    public static final int SPEED_62K5 = 4;
196
197    /**
198     * Mandatory Set Speed (0x04) for a board: it switches to the new bus speed
199     * once this transaction is complete. The interface must then be told with
200     * {@link #busSetSpeed(int, int)}.
201     *
202     * @param address board address
203     * @param speed   SPEED code, 0 for 8 kbps
204     * @return the message
205     */
206    public static Dcc4PcMessage setBoardSpeed(int address, int speed) {
207        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDSETSPEED, (byte) speed});
208        m.childBoard = true;
209        m.board = address;
210        m.messageType = CHILDSETSPEED;
211        return m;
212    }
213
214    /**
215     * BUS Set Speed (0x07): the speed the interface uses to talk to one board.
216     *
217     * @param address board address
218     * @param speed   SPEED code, 0 for 8 kbps
219     * @return the message
220     */
221    public static Dcc4PcMessage busSetSpeed(int address, int speed) {
222        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x07, (byte) address, (byte) speed});
223        m.messageType = BUSSETSPEED;
224        m.noDataReply = true;
225        return m;
226    }
227
228    // The interface answers some commands with no bytes at all, only DSR rising
229    // and falling. The receive side can't see that, so the traffic controller
230    // waits for DSR to fall instead of waiting for a reply.
231    boolean noDataReply = false;
232
233    /**
234     * @return true if the interface answers this message with no data, only
235     *         its DSR handshake
236     */
237    public boolean isNoDataReply() {
238        return noDataReply;
239    }
240
241    @Override
242    public boolean replyExpected() {
243        return !noDataReply;
244    }
245
246    /**
247     * BUS Reset Speeds (0x0D, BUS 1.1): every board and the interface go back
248     * to 8 kbps.
249     *
250     * @return the message
251     */
252    public static Dcc4PcMessage busResetSpeeds() {
253        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0D});
254        m.messageType = BUSRESETSPEEDS;
255        m.noDataReply = true;
256        return m;
257    }
258
259    /**
260     * Select the Raw RailCom encoding (encoding 0), the only one decoded.
261     *
262     * @param address board address
263     * @return the message
264     */
265    public static Dcc4PcMessage selectRawEncoding(int address) {
266        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDSELECTENCODING, (byte) 0x00});
267        m.childBoard = true;
268        m.board = address;
269        m.messageType = CHILDSELECTENCODING;
270        return m;
271    }
272
273    public static Dcc4PcMessage resetBoardData(int address) {
274        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDRESET});
275        m.childBoard = true;
276        m.board = address;
277        m.messageType = CHILDRESET;
278        return m;
279    }
280       
281    public static Dcc4PcMessage pollBoard(int address){
282        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDPOLL});
283        m.childBoard = true;
284        m.setTimeout(3000); // a Get Data at 8 kbps takes over 500 ms; timing out early lets the next command interrupt it
285        m.board = address;
286        m.messageType = CHILDPOLL;
287        return m;
288    }
289
290    public static Dcc4PcMessage getEnabledInputs(int address) {
291        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) 0x0b, (byte) address, (byte) CHILDENABLEDINPUTS});
292        m.childBoard = true;
293        m.board = address;
294        m.messageType = CHILDENABLEDINPUTS;
295        return m;
296    }
297
298    public static Dcc4PcMessage getInfo() {
299        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) INFO});
300        m.messageType = INFO;
301        return m;
302    }
303
304    public static Dcc4PcMessage getResponse() {
305        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) RESPONSE});
306        m.isResponse = true;
307        m.messageType = RESPONSE;
308        return m;
309    }
310
311    public static Dcc4PcMessage getDescription() {
312        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) DESC});
313        m.messageType = DESC;
314        return m;
315    }
316
317    public static Dcc4PcMessage getSerialNumber() {
318        Dcc4PcMessage m = new Dcc4PcMessage(new byte[]{(byte) SERIAL});
319        m.messageType = SERIAL;
320        return m;
321    }
322    
323    private static final Logger log = LoggerFactory.getLogger(Dcc4PcMessage.class);
324}