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