001package jmri.jmrix.can.adapters.gridconnect.can2usbino;
002
003import jmri.jmrix.can.CanMessage;
004import jmri.jmrix.can.adapters.gridconnect.GridConnectMessage;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Class for GridConnectDoubled messages for a CAN hardware adapter.
010 * <p>
011 * The GridConnect protocol encodes messages as an ASCII string of up to 24
012 * characters of the form: :ShhhhNd0d1d2d3d4d5d6d7; The S indicates a standard
013 * CAN frame :XhhhhhhhhNd0d1d2d3d4d5d6d7; The X indicates an extended CAN frame
014 * hhhh is the two byte header N or R indicates a normal or remote frame, in
015 * position 6 or 10 d0 - d7 are the (up to) 8 data bytes
016 * <p>
017 * On transmit, this is doubled and starts with an "!" character.
018 *
019 * @author Andrew Crosland Copyright (C) 2012
020 * @author Andrew Crosland Copyright (C) 2008
021 */
022public class GridConnectDoubledMessage extends GridConnectMessage {
023
024    // Creates a new instance of GridConnectDoubledMessage
025    public GridConnectDoubledMessage() {
026        _nDataChars = 28;
027        _dataChars = new int[_nDataChars];
028        GridConnectDoubledMessage.this.setElement(0, '!');
029    }
030
031    public GridConnectDoubledMessage(CanMessage m) {
032        this();
033        addCanMessage(m);
034    }
035
036    private void addCanMessage(CanMessage m) {
037        // Standard or extended frame
038        setExtended(m.isExtended());
039
040        // Copy the header
041        setHeader(m.getHeader());
042
043        // Normal or Remote frame?
044        setRtr(m.isRtr());
045
046        // Data payload
047        for (int i = 0; i < m.getNumDataElements(); i++) {
048            setByte(m.getElement(i), i);
049        }
050        // Terminator
051        int offset = isExtended() ? 11 : 6;
052        setElement(offset + m.getNumDataElements() * 2, ';');
053        setNumDataElements(offset + 1 + m.getNumDataElements() * 2);
054        log.debug("encoded as {}", this.toString());
055    }
056
057    // accessors to the bulk data
058    @Override
059    public int getNumDataElements() {
060        return _nDataChars * 2;
061    }
062
063    @Override
064    public void setNumDataElements(int n) {
065        _nDataChars = (n <= 28) ? n : 28;
066    }
067
068    @Override
069    public int getElement(int n) {
070        return _dataChars[n / 2];
071    }
072
073    @Override
074    public void setElement(int n, int v) {
075        _dataChars[n] = v;
076    }
077
078    @Override
079    public void setData(int[] d) {
080        int len = (d.length <= 24) ? d.length : 24;
081        System.arraycopy(d, 0, _dataChars, 0, len);
082    }
083
084    @Override
085    public void setExtended(boolean extended) {
086        // Standard or extended frame
087        this.extended = extended;
088        if (extended) {
089            setElement(1, 'X');
090        } else {
091            setElement(1, 'S');
092        }
093    }
094
095    private boolean extended;
096
097    @Override
098    public boolean isExtended() {
099        return extended;
100    }
101
102    /**
103     * Set the header
104     *
105     * @param header A valid CAN header value
106     */
107    @Override
108    public void setHeader(int header) {
109        if (isExtended()) {
110            setHexDigit((header >> 28) & 0xF, 2);
111            setHexDigit((header >> 24) & 0xF, 3);
112            setHexDigit((header >> 20) & 0xF, 4);
113            setHexDigit((header >> 16) & 0xF, 5);
114            setHexDigit((header >> 12) & 0xF, 6);
115            setHexDigit((header >> 8) & 0xF, 7);
116            setHexDigit((header >> 4) & 0xF, 8);
117            setHexDigit(header & 0xF, 9);
118        } else {
119            setHexDigit((header >> 8) & 0xF, 2);
120            setHexDigit((header >> 4) & 0xF, 3);
121            setHexDigit(header & 0xF, 4);
122        }
123    }
124
125    @Override
126    public void setRtr(boolean rtr) {
127        int offset = isExtended() ? 10 : 5;
128        setElement(offset, rtr ? 'R' : 'N');
129    }
130
131    /**
132     * Set a byte as two ASCII hex digits
133     * <p>
134     * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the
135     * message.
136     *
137     * @param val the value to set
138     * @param n   the index of the byte to be set
139     */
140    @Override
141    public void setByte(int val, int n) {
142        if ((n >= 0) && (n <= 7)) {
143            int index = n * 2 + (isExtended() ? 11 : 6);
144            setHexDigit((val / 16) & 0xF, index++);
145            setHexDigit(val & 0xF, index);
146        }
147    }
148
149    // Set a hex digit at offset n in _dataChars
150    @Override
151    protected 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(GridConnectDoubledMessage.class);
164
165}