001package jmri.jmrix.can.adapters.lawicell;
002
003import jmri.jmrix.AbstractMRReply;
004import jmri.jmrix.can.CanReply;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Class for replies in a LAWICELL message/reply protocol.
010 * <p>
011 * The Lawicell adapter protocol encodes messages as an ASCII string of up to 24
012 * characters of the form: tiiildd...[CR] Tiiiiiiiildd...[CR] The t or T
013 * indicates a standard or extended CAN frame iiiiiiii is the header as hex
014 * digits l is the number of bytes of data dd are the (up to) 8 data bytes
015 * <p>
016 * RTR Extended frames start with an R, RTR standard frames with r.
017 * @author Andrew Crosland Copyright (C) 2008
018 * @author Bob Jacobsen Copyright (C) 2008
019 * @author Steve Young Copyright (C) 2022 ( added RTR Can Frame support )
020*/
021public class Reply extends AbstractMRReply {
022
023    // Creates a new instance of ConnectReply
024    public Reply() {
025        super();
026    }
027
028    public Reply(String s) {
029        _nDataChars = s.length();
030        for (int i = 0; i < s.length(); i++) {
031            _dataChars[i] = s.charAt(i);
032        }
033    }
034
035    public CanReply createReply() {
036        // is this just an ACK to e.g. a send?
037        if (_dataChars[0] != 't' 
038            && _dataChars[0] != 'T'
039            && _dataChars[0] != 'r'
040            && _dataChars[0] != 'R') {
041            log.debug("non-frame reply skipped: {}", this);
042            return null;
043        }
044        // carries a frame
045        CanReply ret = new CanReply();
046
047        ret.setExtended(isExtended());
048        ret.setRtr(isRtrSet());
049
050        // Copy the header
051        ret.setHeader(getHeader());
052
053        // Get the data
054        for (int i = 0; i < getNumBytes(); i++) {
055            ret.setElement(i, getByte(i));
056        }
057        ret.setNumDataElements(getNumBytes());
058        return ret;
059    }
060
061    @Override
062    protected int skipPrefix(int index) {
063        while (_dataChars[index] == ':') {
064            index++;
065        }
066        return index;
067    }
068
069    public void setData(int[] d) {
070        int len = (d.length <= 24) ? d.length : 24;
071        System.arraycopy(d, 0, _dataChars, 0, len);
072    }
073
074    public boolean isExtended() {
075        return _dataChars[0] == 'T' || _dataChars[0] == 'R';
076    }
077    
078    public boolean isRtrSet() {
079        return _dataChars[0] == 'r' || _dataChars[0] == 'R';
080    }
081
082    /**
083     * Get the CAN header as an int
084     *
085     * @return int the CAN ID
086     */
087    public int getHeader() {
088        if (isExtended()) {
089            // 11 bit header
090            int val = 0;
091            for (int i = 1; i <= 8; i++) {
092                val = val * 16 + getHexDigit(i);
093            }
094            return val;
095        } else {
096            // 11 bit header
097            return getHexDigit(1) * 256
098                    + getHexDigit(2) * 16 + getHexDigit(3);
099        }
100    }
101
102    /**
103     * Get the number of data bytes
104     *
105     * @return int the number of bytes
106     */
107    public int getNumBytes() {
108        if (isExtended()) {
109            return _dataChars[9] - '0';
110        } else {
111            return _dataChars[4] - '0';
112        }
113    }
114
115    /**
116     * Get a hex data byte from the message
117     * <p>
118     * Data bytes are encoded as two ASCII hex digits. The starting position is
119     * byte 10 or byte 5, depending on whether this is an extended or standard
120     * message
121     *
122     * @param b The byte offset (0 - 7)
123     * @return The value
124     */
125    public int getByte(int b) {
126        if ((b >= 0) && (b <= 7)) {
127            int index = b * 2 + (isExtended() ? 10 : 5);
128            int hi = getHexDigit(index++);
129            int lo = getHexDigit(index);
130            if ((hi < 16) && (lo < 16)) {
131                return (hi * 16 + lo);
132            }
133        }
134        return 0;
135    }
136
137    // Get a single hex digit. returns 0 if digit is invalid
138    private int getHexDigit(int index) {
139        int b = _dataChars[index];
140        if ((b >= '0') && (b <= '9')) {
141            b = b - '0';
142        } else if ((b >= 'A') && (b <= 'F')) {
143            b = b - 'A' + 10;
144        } else if ((b >= 'a') && (b <= 'f')) {
145            b = b - 'a' + 10;
146        } else {
147            b = 0;
148        }
149        return (byte) b;
150    }
151
152    private final static Logger log = LoggerFactory.getLogger(Reply.class);
153}
154
155