001package jmri.jmrix.can.adapters.lawicell;
002
003import jmri.jmrix.AbstractMRMessage;
004import jmri.jmrix.can.CanMessage;
005// import org.slf4j.Logger;
006// import org.slf4j.LoggerFactory;
007
008/**
009 * Class for messages for a LAWICELL CAN hardware adapter.
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 * <p>
018 *
019 * @author Andrew Crosland Copyright (C) 2008
020 * @author Bob Jacobsen Copyright (C) 2008, 2009
021 * @author Steve Young Copyright (C) 2022 ( added RTR Can Frame support )
022*/
023public class Message extends AbstractMRMessage {
024
025    static final int MAXLEN = 27;
026    private boolean _isRTR = false;
027
028    public Message() {
029        _nDataChars = MAXLEN;
030        _dataChars = new int[_nDataChars];
031    }
032
033    public Message(CanMessage m) {
034        this();
035        addCanMessage(m);
036    }
037
038    private void addCanMessage(CanMessage m){
039
040        // Standard or extended frame?
041        setExtended(m.isExtended());
042
043        // CAN header
044        int index = 1;
045        index = setHeader(m.getHeader(), index);
046
047        setRtr(m.isRtr());
048
049        // length
050        setHexDigit(m.getNumDataElements(), index++);
051
052        // Data payload
053        for (int i = 0; i < m.getNumDataElements(); i++) {
054            setHexDigit((m.getElement(i) >> 4) & 0x0F, index++);
055            setHexDigit(m.getElement(i) & 0x0F, index++);
056        }
057        // Terminator
058        setElement(index++, 0x0D);
059        setNumDataElements(index);
060    }
061
062    // accessors to the bulk data
063    @Override
064    public int getNumDataElements() {
065        return _nDataChars;
066    }
067
068    public void setNumDataElements(int n) {
069        _nDataChars = (n <= MAXLEN) ? n : MAXLEN;
070    }
071
072    @Override
073    public int getElement(int n) {
074        return _dataChars[n];
075    }
076
077    @Override
078    public void setElement(int n, int v) {
079        _dataChars[n] = v;
080    }
081
082    public void setData(int[] d) {
083        int len = (d.length <= MAXLEN) ? d.length : MAXLEN;
084        System.arraycopy(d, 0, _dataChars, 0, len);
085    }
086
087    public void setExtended(boolean extended) {
088        if (extended) { // extended
089            setElement(0, ( isRtrSet() ? 'R': 'T'));
090        } else {
091            // standard
092            setElement(0, ( isRtrSet() ? 'r': 't'));
093        }
094    }
095
096    public boolean isExtended() {
097        return getElement(0) == 'T' || getElement(0) == 'R';
098    }
099    
100    public void setRtr(boolean isrtr){
101        _isRTR = isrtr;
102        setExtended(isExtended()); // reset element 0.
103    }
104    
105    public boolean isRtrSet(){
106        return _isRTR;
107    }
108
109    /**
110     * Set the CAN header as ASCII hex digits.
111     * Handles extended/standard internally.
112     *
113     * @param header A valid CAN header value
114     * @param index start index.
115     * @return index to next bytes, after this
116     */
117    public int setHeader(int header, int index) {
118        if (isExtended()) {
119            // extended MSB part
120            setHexDigit((header >> 28) & 0xF, index++);
121            setHexDigit((header >> 24) & 0xF, index++);
122            setHexDigit((header >> 20) & 0xF, index++);
123            setHexDigit((header >> 16) & 0xF, index++);
124            setHexDigit((header >> 12) & 0xF, index++);
125        }
126        // standard part
127        setHexDigit((header >> 8) & 0xF, index++);
128        setHexDigit((header >> 4) & 0xF, index++);
129        setHexDigit((header >> 0) & 0xF, index++);
130
131        return index;
132    }
133
134    /**
135     * Set a byte as two ASCII hex digits
136     * <p>
137     * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the
138     * message.
139     *
140     * @param val the value to set
141     * @param n   the index of the byte to be set
142     */
143    public void setByte(int val, int n) {
144        if ((n >= 0) && (n <= 7)) {
145            int index = n * 2 + 7;
146            setHexDigit((val / 16) & 0xF, index++);
147            setHexDigit(val & 0xF, index);
148        }
149    }
150
151    // Set a hex digit at offset n in _dataChars
152    void setHexDigit(int val, int n) {
153        if ((val >= 0) && (val <= 15)) {
154            if (val < 10) {
155                _dataChars[n] = val + '0';
156            } else {
157                _dataChars[n] = val - 10 + 'A';
158            }
159        } else {
160            _dataChars[n] = '0';
161        }
162    }
163
164    // private final static Logger log = LoggerFactory.getLogger(Message.class);
165}
166
167