001package jmri.jmrix.can.adapters.gridconnect.canrs;
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 messages for a MERG CAN-RS hardware adapter.
010 * <p>
011 * The MERG variant of the GridConnect protocol encodes messages as an ASCII
012 * string of up to 24 characters of the form: :ShhhhNd0d1d2d3d4d5d6d7; hhhh is
013 * the two byte (11 useful bits) header The S indicates a standard CAN frame
014 * :XhhhhhhhhNd0d1d2d3d4d5d6d7; The X indicates an extended CAN frame Strict
015 * Gridconnect protocol allows a variable number of header characters, e.g., a
016 * header value of 0x123 could be encoded as S123 rather than S0123 or
017 * X00000123. We choose a fixed number, either 4 or 8 bytes when sending
018 * GridConnectMessages to keep MERG CAN-RS/USB adapters happy. The 11 bit
019 * standard header is left justified in these 4 bytes. The 29 bit standard
020 * header is sent as {@code <11 bit SID><0><1><0>< 18 bit EID>}
021 * N or R indicates a normal or remote frame, in position 6 or 10 d0 - d7 are
022 * the (up to) 8 data bytes
023 * <p>
024 *
025 * @author Andrew Crosland Copyright (C) 2008
026 */
027public class MergMessage extends GridConnectMessage {
028
029    // Creates a new instance of GridConnectMessage
030    public MergMessage() {
031        super();
032    }
033
034    public MergMessage(CanMessage m) {
035        this();
036
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 : 7;  // differs here from superclass
052        setElement(offset + m.getNumDataElements() * 2, ';');
053        setNumDataElements(offset + 1 + m.getNumDataElements() * 2);
054        if (log.isDebugEnabled()) {
055            log.debug("encoded as {}", this.toString());
056        }
057    }
058
059    /**
060     * Set the header in MERG format
061     *
062     * @param header A valid CAN header value
063     */
064    @Override
065    public void setHeader(int header) {
066        int munged;
067        if (isExtended()) {
068            munged = ((header << 3) & 0xFFE00000) | 0x80000 | (header & 0x3FFFF);
069            if (log.isDebugEnabled()) {
070                log.debug("Extended header is {}", header);
071            }
072            if (log.isDebugEnabled()) {
073                log.debug("Munged header is {}", munged);
074            }
075            super.setHeader(munged);
076        } else {
077            // 11 header bits are left justified
078            munged = header << 5;
079            if (log.isDebugEnabled()) {
080                log.debug("Standard header is {}", header);
081            }
082            if (log.isDebugEnabled()) {
083                log.debug("Munged header is {}", munged);
084            }
085            // Can't use super() as we want to send 4 digits
086            setHexDigit((munged >> 12) & 0xF, 2);
087            setHexDigit((munged >> 8) & 0xF, 3);
088            setHexDigit((munged >> 4) & 0xF, 4);
089            setHexDigit(0, 5);
090        }
091    }
092
093    @Override
094    public void setRtr(boolean rtr) {
095        int offset = isExtended() ? 10 : 6;
096        setElement(offset, rtr ? 'R' : 'N');
097    }
098
099    /**
100     * Set a byte as two ASCII hex digits
101     * <p>
102     * Data bytes are encoded as two ASCII hex digits starting at byte 7 of the
103     * message.
104     *
105     * @param val the value to set, must be in range 0 - 255
106     * @param n   the index of the byte to be set
107     */
108    @Override
109    public void setByte(int val, int n) {
110        if ((n >= 0) && (n <= 7)) {
111            int index = n * 2 + (isExtended() ? 11 : 7);  // differs here from superclass
112            try {
113                if ((val < 0) || (val > 255)) {
114                    val = 0;
115                    throw new IllegalArgumentException();
116                }
117            }
118            catch(IllegalArgumentException e) {
119                log.error("Value out of range for MergMessage data payload {}", e);
120            }
121            setHexDigit((val / 16) & 0xF, index++);
122            setHexDigit(val & 0xF, index);
123        }
124    }
125
126    private final static Logger log = LoggerFactory.getLogger(MergMessage.class);
127}