001package jmri.jmrix.qsi;
002
003/**
004 * Carries the reply to an QsiMessage.
005 *
006 * @author Bob Jacobsen Copyright (C) 2007
007 */
008public class QsiReply extends jmri.jmrix.AbstractMessage {
009
010    static final int MAXREPLYLENGTH = 200;
011
012    // create a new one
013    public QsiReply() {
014        super(MAXREPLYLENGTH);
015        _isBoot = false;
016        _nDataChars = 0;
017    }
018
019    // copy one
020    public QsiReply(QsiReply m) {
021        super(m);
022        _isBoot = m._isBoot;
023    }
024
025    // from String
026    public QsiReply(String s) {
027        super(s);
028    }
029
030    public QsiReply(String s, boolean b) {
031        super(s);
032        _isBoot = b;
033    }
034
035    public void setOpCode(int i) {
036        _dataChars[0] = (char) i;
037    }
038
039    public int getOpCode() {
040        return _dataChars[0];
041    }
042
043    // accessors to the bulk data
044    @Override
045    public void setElement(int n, int v) {
046        _dataChars[n] = v;
047        _nDataChars = Math.max(_nDataChars, n + 1);
048    }
049
050    static public QsiMessage getAck(QsiReply r) {
051        // send ack to received (unsolicited) message m
052        QsiMessage m = new QsiMessage(1);
053        m.setElement(0, r.getElement(1));
054        return m;
055    }
056
057    // Check and strip framing characters and DLE from a QSI bootloader reply
058    public boolean strip() {
059        int tmp[] = new int[_nDataChars];
060        int j = 0;
061        _isBoot = true; // definitely a boot message
062        // Check framing characters
063        if (_dataChars[0] != QsiMessage.STX) {
064            return false;
065        }
066        if (_dataChars[1] != QsiMessage.STX) {
067            return false;
068        }
069        if (_dataChars[_nDataChars - 1] != QsiMessage.ETX) {
070            return false;
071        }
072
073        // Ignore framing characters and strip DLEs
074        for (int i = 2; i < _nDataChars - 1; i++) {
075            if (_dataChars[i] == QsiMessage.DLE) {
076                i++;
077            }
078            tmp[j++] = _dataChars[i];
079        }
080
081        // Copy back to original QsiReply
082        System.arraycopy(tmp, 0, _dataChars, 0, j);
083        _nDataChars = j;
084        return true;
085    }
086
087    // Check and strip checksum from a QSI bootloader reply
088    // Assumes framing and DLE chars have been stripped
089    public boolean getChecksum() {
090        int checksum = 0;
091        for (int i = 0; i < _nDataChars; i++) {
092            checksum += _dataChars[i] & 0xff;
093        }
094        _nDataChars--;
095        return ((checksum & 0xff) == 0);
096    }
097
098    // display format
099    @Override
100    public String toString() {
101        QsiSystemConnectionMemo memo = jmri.InstanceManager.getDefault(jmri.jmrix.qsi.QsiSystemConnectionMemo.class);
102        return toString(memo.getQsiTrafficController());
103    }
104
105    public String toString(QsiTrafficController controller) {
106        StringBuilder s = new StringBuilder();
107        if (_dataChars == null) {
108            return "<none>";
109        }
110        if (controller == null || controller.isSIIBootMode()) {
111            for (int i = 0; i < _nDataChars; i++) {
112                s.append(jmri.util.StringUtil.twoHexFromInt(_dataChars[i]));
113                s.append(" ");
114            }
115        } else {
116            for (int i = 0; i < _nDataChars; i++) {
117                s.append("<");
118                s.append(_dataChars[i]);
119                s.append(">");
120            }
121        }
122        return new String(s);
123    }
124
125    /**
126     * Extracts Read-CV returned value from a message.
127     * <p>
128     * QSI is assumed to not be echoing commands. A reply to a command may
129     * include the prompt that was printed after the previous command Reply to a
130     * CV read is of the form " = hvv" where vv is the CV value in hex.
131     *
132     * @return the value of the read CV or -1 if the reply cannot be parsed.
133     */
134    public int value() {
135        return getElement(5) & 0xFF;
136    }
137
138    int match(String s) {
139        // find a specific string in the reply
140        String rep = new String(_dataChars, 0, _nDataChars);
141        return rep.indexOf(s);
142    }
143
144    int skipWhiteSpace(int index) {
145        // start at index, passing any whitespace & control characters at the start of the buffer
146        while (index < getNumDataElements() - 1
147                && ((char) getElement(index) <= ' ')) {
148            index++;
149        }
150        return index;
151    }
152
153    int skipEqual(int index) {
154        // start at index, skip over the equals and hex prefix
155        int len = "= h".length();
156        if (getNumDataElements() >= index + len - 1
157                && '=' == (char) getElement(index)
158                && ' ' == (char) getElement(index + 1)
159                && 'h' == (char) getElement(index + 2)) {
160            index += len;
161        }
162        return index;
163    }
164
165    // Longest boot reply is 256bytes each preceded by DLE + 2xSTX + ETX
166    static public final int MAXSIZE = 515;
167
168    // contents (private)
169    private boolean _isBoot = false;
170
171}